PageRenderTime 159ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 3ms

/text/src/test/resources/examples/ruby/rails4.in.rb

https://github.com/rimolive/core
Ruby | 15296 lines | 9112 code | 1840 blank | 4344 comment | 628 complexity | b0245bf2875f2473b31162a1cc38de04 MD5 | raw file
Possible License(s): EPL-1.0, MPL-2.0-no-copyleft-exception
  1. require 'mail'
  2. require 'action_mailer/collector'
  3. require 'active_support/core_ext/string/inflections'
  4. require 'active_support/core_ext/hash/except'
  5. require 'active_support/core_ext/module/anonymous'
  6. require 'action_mailer/log_subscriber'
  7. module ActionMailer
  8. # Action Mailer allows you to send email from your application using a mailer model and views.
  9. #
  10. # = Mailer Models
  11. #
  12. # To use Action Mailer, you need to create a mailer model.
  13. #
  14. # $ rails generate mailer Notifier
  15. #
  16. # The generated model inherits from <tt>ActionMailer::Base</tt>. A mailer model defines methods
  17. # used to generate an email message. In these methods, you can setup variables to be used in
  18. # the mailer views, options on the mail itself such as the <tt>:from</tt> address, and attachments.
  19. #
  20. # class Notifier < ActionMailer::Base
  21. # default from: 'no-reply@example.com',
  22. # return_path: 'system@example.com'
  23. #
  24. # def welcome(recipient)
  25. # @account = recipient
  26. # mail(to: recipient.email_address_with_name,
  27. # bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
  28. # end
  29. # end
  30. #
  31. # Within the mailer method, you have access to the following methods:
  32. #
  33. # * <tt>attachments[]=</tt> - Allows you to add attachments to your email in an intuitive
  34. # manner; <tt>attachments['filename.png'] = File.read('path/to/filename.png')</tt>
  35. #
  36. # * <tt>attachments.inline[]=</tt> - Allows you to add an inline attachment to your email
  37. # in the same manner as <tt>attachments[]=</tt>
  38. #
  39. # * <tt>headers[]=</tt> - Allows you to specify any header field in your email such
  40. # as <tt>headers['X-No-Spam'] = 'True'</tt>. Note, while most fields like <tt>To:</tt>
  41. # <tt>From:</tt> can only appear once in an email header, other fields like <tt>X-Anything</tt>
  42. # can appear multiple times. If you want to change a field that can appear multiple times,
  43. # you need to set it to nil first so that Mail knows you are replacing it and not adding
  44. # another field of the same name.
  45. #
  46. # * <tt>headers(hash)</tt> - Allows you to specify multiple headers in your email such
  47. # as <tt>headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})</tt>
  48. #
  49. # * <tt>mail</tt> - Allows you to specify email to be sent.
  50. #
  51. # The hash passed to the mail method allows you to specify any header that a Mail::Message
  52. # will accept (any valid Email header including optional fields).
  53. #
  54. # The mail method, if not passed a block, will inspect your views and send all the views with
  55. # the same name as the method, so the above action would send the +welcome.text.erb+ view
  56. # file as well as the +welcome.text.html.erb+ view file in a +multipart/alternative+ email.
  57. #
  58. # If you want to explicitly render only certain templates, pass a block:
  59. #
  60. # mail(to: user.email) do |format|
  61. # format.text
  62. # format.html
  63. # end
  64. #
  65. # The block syntax is also useful in providing information specific to a part:
  66. #
  67. # mail(to: user.email) do |format|
  68. # format.text(content_transfer_encoding: "base64")
  69. # format.html
  70. # end
  71. #
  72. # Or even to render a special view:
  73. #
  74. # mail(to: user.email) do |format|
  75. # format.text
  76. # format.html { render "some_other_template" }
  77. # end
  78. #
  79. # = Mailer views
  80. #
  81. # Like Action Controller, each mailer class has a corresponding view directory in which each
  82. # method of the class looks for a template with its name.
  83. #
  84. # To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same
  85. # name as the method in your mailer model. For example, in the mailer defined above, the template at
  86. # <tt>app/views/notifier/welcome.text.erb</tt> would be used to generate the email.
  87. #
  88. # Variables defined in the model are accessible as instance variables in the view.
  89. #
  90. # Emails by default are sent in plain text, so a sample view for our model example might look like this:
  91. #
  92. # Hi <%= @account.name %>,
  93. # Thanks for joining our service! Please check back often.
  94. #
  95. # You can even use Action Pack helpers in these views. For example:
  96. #
  97. # You got a new note!
  98. # <%= truncate(@note.body, length: 25) %>
  99. #
  100. # If you need to access the subject, from or the recipients in the view, you can do that through message object:
  101. #
  102. # You got a new note from <%= message.from %>!
  103. # <%= truncate(@note.body, length: 25) %>
  104. #
  105. #
  106. # = Generating URLs
  107. #
  108. # URLs can be generated in mailer views using <tt>url_for</tt> or named routes. Unlike controllers from
  109. # Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need
  110. # to provide all of the details needed to generate a URL.
  111. #
  112. # When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
  113. #
  114. # <%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
  115. #
  116. # When using named routes you only need to supply the <tt>:host</tt>:
  117. #
  118. # <%= users_url(host: "example.com") %>
  119. #
  120. # You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
  121. # <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
  122. # have no concept of a current URL from which to determine a relative path.
  123. #
  124. # It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
  125. # option as a configuration option in <tt>config/application.rb</tt>:
  126. #
  127. # config.action_mailer.default_url_options = { host: "example.com" }
  128. #
  129. # When you decide to set a default <tt>:host</tt> for your mailers, then you need to make sure to use the
  130. # <tt>only_path: false</tt> option when using <tt>url_for</tt>. Since the <tt>url_for</tt> view helper
  131. # will generate relative URLs by default when a <tt>:host</tt> option isn't explicitly provided, passing
  132. # <tt>only_path: false</tt> will ensure that absolute URLs are generated.
  133. #
  134. # = Sending mail
  135. #
  136. # Once a mailer action and template are defined, you can deliver your message or create it and save it
  137. # for delivery later:
  138. #
  139. # Notifier.welcome(david).deliver # sends the email
  140. # mail = Notifier.welcome(david) # => a Mail::Message object
  141. # mail.deliver # sends the email
  142. #
  143. # You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
  144. #
  145. # = Multipart Emails
  146. #
  147. # Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
  148. # multipart templates, where each template is named after the name of the action, followed by the content
  149. # type. Each such detected template will be added as a separate part to the message.
  150. #
  151. # For example, if the following templates exist:
  152. # * signup_notification.text.erb
  153. # * signup_notification.text.html.erb
  154. # * signup_notification.text.xml.builder
  155. # * signup_notification.text.yaml.erb
  156. #
  157. # Each would be rendered and added as a separate part to the message, with the corresponding content
  158. # type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>,
  159. # which indicates that the email contains multiple different representations of the same email
  160. # body. The same instance variables defined in the action are passed to all email templates.
  161. #
  162. # Implicit template rendering is not performed if any attachments or parts have been added to the email.
  163. # This means that you'll have to manually add each part to the email and set the content type of the email
  164. # to <tt>multipart/alternative</tt>.
  165. #
  166. # = Attachments
  167. #
  168. # Sending attachment in emails is easy:
  169. #
  170. # class ApplicationMailer < ActionMailer::Base
  171. # def welcome(recipient)
  172. # attachments['free_book.pdf'] = File.read('path/to/file.pdf')
  173. # mail(to: recipient, subject: "New account information")
  174. # end
  175. # end
  176. #
  177. # Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.text.html.erb</tt>
  178. # template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
  179. # the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
  180. # and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
  181. # with the filename +free_book.pdf+.
  182. #
  183. # If you need to send attachments with no content, you need to create an empty view for it,
  184. # or add an empty body parameter like this:
  185. #
  186. # class ApplicationMailer < ActionMailer::Base
  187. # def welcome(recipient)
  188. # attachments['free_book.pdf'] = File.read('path/to/file.pdf')
  189. # mail(to: recipient, subject: "New account information", body: "")
  190. # end
  191. # end
  192. #
  193. # = Inline Attachments
  194. #
  195. # You can also specify that a file should be displayed inline with other HTML. This is useful
  196. # if you want to display a corporate logo or a photo.
  197. #
  198. # class ApplicationMailer < ActionMailer::Base
  199. # def welcome(recipient)
  200. # attachments.inline['photo.png'] = File.read('path/to/photo.png')
  201. # mail(to: recipient, subject: "Here is what we look like")
  202. # end
  203. # end
  204. #
  205. # And then to reference the image in the view, you create a <tt>welcome.html.erb</tt> file and
  206. # make a call to +image_tag+ passing in the attachment you want to display and then call
  207. # +url+ on the attachment to get the relative content id path for the image source:
  208. #
  209. # <h1>Please Don't Cringe</h1>
  210. #
  211. # <%= image_tag attachments['photo.png'].url -%>
  212. #
  213. # As we are using Action View's +image_tag+ method, you can pass in any other options you want:
  214. #
  215. # <h1>Please Don't Cringe</h1>
  216. #
  217. # <%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
  218. #
  219. # = Observing and Intercepting Mails
  220. #
  221. # Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to
  222. # register classes that are called during the mail delivery life cycle.
  223. #
  224. # An observer class must implement the <tt>:delivered_email(message)</tt> method which will be
  225. # called once for every email sent after the email has been sent.
  226. #
  227. # An interceptor class must implement the <tt>:delivering_email(message)</tt> method which will be
  228. # called before the email is sent, allowing you to make modifications to the email before it hits
  229. # the delivery agents. Your class should make any needed modifications directly to the passed
  230. # in Mail::Message instance.
  231. #
  232. # = Default Hash
  233. #
  234. # Action Mailer provides some intelligent defaults for your emails, these are usually specified in a
  235. # default method inside the class definition:
  236. #
  237. # class Notifier < ActionMailer::Base
  238. # default sender: 'system@example.com'
  239. # end
  240. #
  241. # You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
  242. # <tt>ActionMailer::Base</tt> sets the following:
  243. #
  244. # * <tt>mime_version: "1.0"</tt>
  245. # * <tt>charset: "UTF-8",</tt>
  246. # * <tt>content_type: "text/plain",</tt>
  247. # * <tt>parts_order: [ "text/plain", "text/enriched", "text/html" ]</tt>
  248. #
  249. # <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
  250. # but Action Mailer translates them appropriately and sets the correct values.
  251. #
  252. # As you can pass in any header, you need to either quote the header as a string, or pass it in as
  253. # an underscored symbol, so the following will work:
  254. #
  255. # class Notifier < ActionMailer::Base
  256. # default 'Content-Transfer-Encoding' => '7bit',
  257. # content_description: 'This is a description'
  258. # end
  259. #
  260. # Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash, so you
  261. # can define methods that evaluate as the message is being generated:
  262. #
  263. # class Notifier < ActionMailer::Base
  264. # default 'X-Special-Header' => Proc.new { my_method }
  265. #
  266. # private
  267. #
  268. # def my_method
  269. # 'some complex call'
  270. # end
  271. # end
  272. #
  273. # Note that the proc is evaluated right at the start of the mail message generation, so if you
  274. # set something in the defaults using a proc, and then set the same thing inside of your
  275. # mailer method, it will get over written by the mailer method.
  276. #
  277. # It is also possible to set these default options that will be used in all mailers through
  278. # the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
  279. #
  280. # config.action_mailer.default_options = { from: "no-reply@example.org" }
  281. #
  282. # = Callbacks
  283. #
  284. # You can specify callbacks using before_action and after_action for configuring your messages.
  285. # This may be useful, for example, when you want to add default inline attachments for all
  286. # messages sent out by a certain mailer class:
  287. #
  288. # class Notifier < ActionMailer::Base
  289. # before_action :add_inline_attachment!
  290. #
  291. # def welcome
  292. # mail
  293. # end
  294. #
  295. # private
  296. #
  297. # def add_inline_attachment!
  298. # attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
  299. # end
  300. # end
  301. #
  302. # Callbacks in ActionMailer are implemented using AbstractController::Callbacks, so you
  303. # can define and configure callbacks in the same manner that you would use callbacks in
  304. # classes that inherit from ActionController::Base.
  305. #
  306. # Note that unless you have a specific reason to do so, you should prefer using before_action
  307. # rather than after_action in your ActionMailer classes so that headers are parsed properly.
  308. #
  309. # = Configuration options
  310. #
  311. # These options are specified on the class level, like
  312. # <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
  313. #
  314. # * <tt>default_options</tt> - You can pass this in at a class level as well as within the class itself as
  315. # per the above section.
  316. #
  317. # * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
  318. # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
  319. #
  320. # * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
  321. # * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default
  322. # "localhost" setting.
  323. # * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
  324. # * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
  325. # * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
  326. # * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
  327. # * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
  328. # authentication type here.
  329. # This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
  330. # send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
  331. # information and a cryptographic Message Digest 5 algorithm to hash important information)
  332. # * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
  333. # and starts to use it.
  334. # * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
  335. # really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
  336. # of an OpenSSL verify constant ('none', 'peer', 'client_once','fail_if_no_peer_cert') or directly the
  337. # constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER,...).
  338. #
  339. # * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
  340. # * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
  341. # * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@address</tt>
  342. # added automatically before the message is sent.
  343. #
  344. # * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
  345. # * <tt>:location</tt> - The directory into which emails will be written. Defaults to the application
  346. # <tt>tmp/mails</tt>.
  347. #
  348. # * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
  349. #
  350. # * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default),
  351. # <tt>:sendmail</tt>, <tt>:test</tt>, and <tt>:file</tt>. Or you may provide a custom delivery method
  352. # object e.g. MyOwnDeliveryMethodClass. See the Mail gem documentation on the interface you need to
  353. # implement for a custom delivery agent.
  354. #
  355. # * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you
  356. # call <tt>.deliver</tt> on an mail message or on an Action Mailer method. This is on by default but can
  357. # be turned off to aid in functional testing.
  358. #
  359. # * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
  360. # <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
  361. class Base < AbstractController::Base
  362. include DeliveryMethods
  363. abstract!
  364. include AbstractController::Logger
  365. include AbstractController::Rendering
  366. include AbstractController::Layouts
  367. include AbstractController::Helpers
  368. include AbstractController::Translation
  369. include AbstractController::AssetPaths
  370. include AbstractController::Callbacks
  371. self.protected_instance_variables = [:@_action_has_layout]
  372. helper ActionMailer::MailHelper
  373. private_class_method :new #:nodoc:
  374. class_attribute :default_params
  375. self.default_params = {
  376. mime_version: "1.0",
  377. charset: "UTF-8",
  378. content_type: "text/plain",
  379. parts_order: [ "text/plain", "text/enriched", "text/html" ]
  380. }.freeze
  381. class << self
  382. # Register one or more Observers which will be notified when mail is delivered.
  383. def register_observers(*observers)
  384. observers.flatten.compact.each { |observer| register_observer(observer) }
  385. end
  386. # Register one or more Interceptors which will be called before mail is sent.
  387. def register_interceptors(*interceptors)
  388. interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
  389. end
  390. # Register an Observer which will be notified when mail is delivered.
  391. # Either a class or a string can be passed in as the Observer. If a string is passed in
  392. # it will be <tt>constantize</tt>d.
  393. def register_observer(observer)
  394. delivery_observer = (observer.is_a?(String) ? observer.constantize : observer)
  395. Mail.register_observer(delivery_observer)
  396. end
  397. # Register an Interceptor which will be called before mail is sent.
  398. # Either a class or a string can be passed in as the Interceptor. If a string is passed in
  399. # it will be <tt>constantize</tt>d.
  400. def register_interceptor(interceptor)
  401. delivery_interceptor = (interceptor.is_a?(String) ? interceptor.constantize : interceptor)
  402. Mail.register_interceptor(delivery_interceptor)
  403. end
  404. def mailer_name
  405. @mailer_name ||= anonymous? ? "anonymous" : name.underscore
  406. end
  407. attr_writer :mailer_name
  408. alias :controller_path :mailer_name
  409. def default(value = nil)
  410. self.default_params = default_params.merge(value).freeze if value
  411. default_params
  412. end
  413. # Allows to set defaults through app configuration:
  414. #
  415. # config.action_mailer.default_options = { from: "no-reply@example.org" }
  416. alias :default_options= :default
  417. # Receives a raw email, parses it into an email object, decodes it,
  418. # instantiates a new mailer, and passes the email object to the mailer
  419. # object's +receive+ method. If you want your mailer to be able to
  420. # process incoming messages, you'll need to implement a +receive+
  421. # method that accepts the raw email string as a parameter:
  422. #
  423. # class MyMailer < ActionMailer::Base
  424. # def receive(mail)
  425. # ...
  426. # end
  427. # end
  428. def receive(raw_mail)
  429. ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload|
  430. mail = Mail.new(raw_mail)
  431. set_payload_for_mail(payload, mail)
  432. new.receive(mail)
  433. end
  434. end
  435. # Wraps an email delivery inside of Active Support Notifications instrumentation. This
  436. # method is actually called by the <tt>Mail::Message</tt> object itself through a callback
  437. # when you call <tt>:deliver</tt> on the Mail::Message, calling +deliver_mail+ directly
  438. # and passing a Mail::Message will do nothing except tell the logger you sent the email.
  439. def deliver_mail(mail) #:nodoc:
  440. ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
  441. set_payload_for_mail(payload, mail)
  442. yield # Let Mail do the delivery actions
  443. end
  444. end
  445. def respond_to?(method, include_private = false) #:nodoc:
  446. super || action_methods.include?(method.to_s)
  447. end
  448. protected
  449. def set_payload_for_mail(payload, mail) #:nodoc:
  450. payload[:mailer] = name
  451. payload[:message_id] = mail.message_id
  452. payload[:subject] = mail.subject
  453. payload[:to] = mail.to
  454. payload[:from] = mail.from
  455. payload[:bcc] = mail.bcc if mail.bcc.present?
  456. payload[:cc] = mail.cc if mail.cc.present?
  457. payload[:date] = mail.date
  458. payload[:mail] = mail.encoded
  459. end
  460. def method_missing(method_name, *args)
  461. if respond_to?(method_name)
  462. new(method_name, *args).message
  463. else
  464. super
  465. end
  466. end
  467. end
  468. attr_internal :message
  469. # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
  470. # will be initialized according to the named method. If not, the mailer will
  471. # remain uninitialized (useful when you only need to invoke the "receive"
  472. # method, for instance).
  473. def initialize(method_name=nil, *args)
  474. super()
  475. @_mail_was_called = false
  476. @_message = Mail.new
  477. process(method_name, *args) if method_name
  478. end
  479. def process(*args) #:nodoc:
  480. lookup_context.skip_default_locale!
  481. super
  482. @_message = NullMail.new unless @_mail_was_called
  483. end
  484. class NullMail #:nodoc:
  485. def body; '' end
  486. def method_missing(*args)
  487. nil
  488. end
  489. end
  490. def mailer_name
  491. self.class.mailer_name
  492. end
  493. # Allows you to pass random and unusual headers to the new <tt>Mail::Message</tt> object
  494. # which will add them to itself.
  495. #
  496. # headers['X-Special-Domain-Specific-Header'] = "SecretValue"
  497. #
  498. # You can also pass a hash into headers of header field names and values, which
  499. # will then be set on the Mail::Message object:
  500. #
  501. # headers 'X-Special-Domain-Specific-Header' => "SecretValue",
  502. # 'In-Reply-To' => incoming.message_id
  503. #
  504. # The resulting Mail::Message will have the following in its header:
  505. #
  506. # X-Special-Domain-Specific-Header: SecretValue
  507. def headers(args = nil)
  508. if args
  509. @_message.headers(args)
  510. else
  511. @_message
  512. end
  513. end
  514. # Allows you to add attachments to an email, like so:
  515. #
  516. # mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
  517. #
  518. # If you do this, then Mail will take the file name and work out the mime type
  519. # set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
  520. # base64 encode the contents of the attachment all for you.
  521. #
  522. # You can also specify overrides if you want by passing a hash instead of a string:
  523. #
  524. # mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
  525. # content: File.read('/path/to/filename.jpg')}
  526. #
  527. # If you want to use a different encoding than Base64, you can pass an encoding in,
  528. # but then it is up to you to pass in the content pre-encoded, and don't expect
  529. # Mail to know how to decode this data:
  530. #
  531. # file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
  532. # mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
  533. # encoding: 'SpecialEncoding',
  534. # content: file_content }
  535. #
  536. # You can also search for specific attachments:
  537. #
  538. # # By Filename
  539. # mail.attachments['filename.jpg'] # => Mail::Part object or nil
  540. #
  541. # # or by index
  542. # mail.attachments[0] # => Mail::Part (first attachment)
  543. #
  544. def attachments
  545. @_message.attachments
  546. end
  547. # The main method that creates the message and renders the email templates. There are
  548. # two ways to call this method, with a block, or without a block.
  549. #
  550. # Both methods accept a headers hash. This hash allows you to specify the most used headers
  551. # in an email message, these are:
  552. #
  553. # * <tt>:subject</tt> - The subject of the message, if this is omitted, Action Mailer will
  554. # ask the Rails I18n class for a translated <tt>:subject</tt> in the scope of
  555. # <tt>[mailer_scope, action_name]</tt> or if this is missing, will translate the
  556. # humanized version of the <tt>action_name</tt>
  557. # * <tt>:to</tt> - Who the message is destined for, can be a string of addresses, or an array
  558. # of addresses.
  559. # * <tt>:from</tt> - Who the message is from
  560. # * <tt>:cc</tt> - Who you would like to Carbon-Copy on this email, can be a string of addresses,
  561. # or an array of addresses.
  562. # * <tt>:bcc</tt> - Who you would like to Blind-Carbon-Copy on this email, can be a string of
  563. # addresses, or an array of addresses.
  564. # * <tt>:reply_to</tt> - Who to set the Reply-To header of the email to.
  565. # * <tt>:date</tt> - The date to say the email was sent on.
  566. #
  567. # You can set default values for any of the above headers (except :date) by using the <tt>default</tt>
  568. # class method:
  569. #
  570. # class Notifier < ActionMailer::Base
  571. # self.default from: 'no-reply@test.lindsaar.net',
  572. # bcc: 'email_logger@test.lindsaar.net',
  573. # reply_to: 'bounces@test.lindsaar.net'
  574. # end
  575. #
  576. # If you need other headers not listed above, you can either pass them in
  577. # as part of the headers hash or use the <tt>headers['name'] = value</tt>
  578. # method.
  579. #
  580. # When a <tt>:return_path</tt> is specified as header, that value will be used as the 'envelope from'
  581. # address for the Mail message. Setting this is useful when you want delivery notifications
  582. # sent to a different address than the one in <tt>:from</tt>. Mail will actually use the
  583. # <tt>:return_path</tt> in preference to the <tt>:sender</tt> in preference to the <tt>:from</tt>
  584. # field for the 'envelope from' value.
  585. #
  586. # If you do not pass a block to the +mail+ method, it will find all templates in the
  587. # view paths using by default the mailer name and the method name that it is being
  588. # called from, it will then create parts for each of these templates intelligently,
  589. # making educated guesses on correct content type and sequence, and return a fully
  590. # prepared Mail::Message ready to call <tt>:deliver</tt> on to send.
  591. #
  592. # For example:
  593. #
  594. # class Notifier < ActionMailer::Base
  595. # default from: 'no-reply@test.lindsaar.net',
  596. #
  597. # def welcome
  598. # mail(to: 'mikel@test.lindsaar.net')
  599. # end
  600. # end
  601. #
  602. # Will look for all templates at "app/views/notifier" with name "welcome".
  603. # If no welcome template exists, it will raise an ActionView::MissingTemplate error.
  604. #
  605. # However, those can be customized:
  606. #
  607. # mail(template_path: 'notifications', template_name: 'another')
  608. #
  609. # And now it will look for all templates at "app/views/notifications" with name "another".
  610. #
  611. # If you do pass a block, you can render specific templates of your choice:
  612. #
  613. # mail(to: 'mikel@test.lindsaar.net') do |format|
  614. # format.text
  615. # format.html
  616. # end
  617. #
  618. # You can even render text directly without using a template:
  619. #
  620. # mail(to: 'mikel@test.lindsaar.net') do |format|
  621. # format.text { render text: "Hello Mikel!" }
  622. # format.html { render text: "<h1>Hello Mikel!</h1>" }
  623. # end
  624. #
  625. # Which will render a <tt>multipart/alternative</tt> email with <tt>text/plain</tt> and
  626. # <tt>text/html</tt> parts.
  627. #
  628. # The block syntax also allows you to customize the part headers if desired:
  629. #
  630. # mail(to: 'mikel@test.lindsaar.net') do |format|
  631. # format.text(content_transfer_encoding: "base64")
  632. # format.html
  633. # end
  634. #
  635. def mail(headers = {}, &block)
  636. @_mail_was_called = true
  637. m = @_message
  638. # At the beginning, do not consider class default for content_type
  639. content_type = headers[:content_type]
  640. # Call all the procs (if any)
  641. class_default = self.class.default
  642. default_values = class_default.merge(class_default) do |k,v|
  643. v.respond_to?(:to_proc) ? instance_eval(&v) : v
  644. end
  645. # Handle defaults
  646. headers = headers.reverse_merge(default_values)
  647. headers[:subject] ||= default_i18n_subject
  648. # Apply charset at the beginning so all fields are properly quoted
  649. m.charset = charset = headers[:charset]
  650. # Set configure delivery behavior
  651. wrap_delivery_behavior!(headers.delete(:delivery_method),headers.delete(:delivery_method_options))
  652. # Assign all headers except parts_order, content_type and body
  653. assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
  654. assignable.each { |k, v| m[k] = v }
  655. # Render the templates and blocks
  656. responses = collect_responses(headers, &block)
  657. create_parts_from_responses(m, responses)
  658. # Setup content type, reapply charset and handle parts order
  659. m.content_type = set_content_type(m, content_type, headers[:content_type])
  660. m.charset = charset
  661. if m.multipart?
  662. m.body.set_sort_order(headers[:parts_order])
  663. m.body.sort_parts!
  664. end
  665. m
  666. end
  667. protected
  668. def set_content_type(m, user_content_type, class_default)
  669. params = m.content_type_parameters || {}
  670. case
  671. when user_content_type.present?
  672. user_content_type
  673. when m.has_attachments?
  674. if m.attachments.detect { |a| a.inline? }
  675. ["multipart", "related", params]
  676. else
  677. ["multipart", "mixed", params]
  678. end
  679. when m.multipart?
  680. ["multipart", "alternative", params]
  681. else
  682. m.content_type || class_default
  683. end
  684. end
  685. # Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
  686. # If it does not find a translation for the +subject+ under the specified scope it will default to a
  687. # humanized version of the <tt>action_name</tt>.
  688. # If the subject has interpolations, you can pass them through the +interpolations+ parameter.
  689. def default_i18n_subject(interpolations = {})
  690. mailer_scope = self.class.mailer_name.tr('/', '.')
  691. I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
  692. end
  693. def collect_responses(headers) #:nodoc:
  694. responses = []
  695. if block_given?
  696. collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
  697. yield(collector)
  698. responses = collector.responses
  699. elsif headers[:body]
  700. responses << {
  701. body: headers.delete(:body),
  702. content_type: self.class.default[:content_type] || "text/plain"
  703. }
  704. else
  705. templates_path = headers.delete(:template_path) || self.class.mailer_name
  706. templates_name = headers.delete(:template_name) || action_name
  707. each_template(Array(templates_path), templates_name) do |template|
  708. self.formats = template.formats
  709. responses << {
  710. body: render(template: template),
  711. content_type: template.type.to_s
  712. }
  713. end
  714. end
  715. responses
  716. end
  717. def each_template(paths, name, &block) #:nodoc:
  718. templates = lookup_context.find_all(name, paths)
  719. if templates.empty?
  720. raise ActionView::MissingTemplate.new(paths, name, paths, false, 'mailer')
  721. else
  722. templates.uniq { |t| t.formats }.each(&block)
  723. end
  724. end
  725. def create_parts_from_responses(m, responses) #:nodoc:
  726. if responses.size == 1 && !m.has_attachments?
  727. responses[0].each { |k,v| m[k] = v }
  728. elsif responses.size > 1 && m.has_attachments?
  729. container = Mail::Part.new
  730. container.content_type = "multipart/alternative"
  731. responses.each { |r| insert_part(container, r, m.charset) }
  732. m.add_part(container)
  733. else
  734. responses.each { |r| insert_part(m, r, m.charset) }
  735. end
  736. end
  737. def insert_part(container, response, charset) #:nodoc:
  738. response[:charset] ||= charset
  739. part = Mail::Part.new(response)
  740. container.add_part(part)
  741. end
  742. ActiveSupport.run_load_hooks(:action_mailer, self)
  743. end
  744. end
  745. require 'abstract_controller/collector'
  746. require 'active_support/core_ext/hash/reverse_merge'
  747. require 'active_support/core_ext/array/extract_options'
  748. module ActionMailer
  749. class Collector
  750. include AbstractController::Collector
  751. attr_reader :responses
  752. def initialize(context, &block)
  753. @context = context
  754. @responses = []
  755. @default_render = block
  756. end
  757. def any(*args, &block)
  758. options = args.extract_options!
  759. raise ArgumentError, "You have to supply at least one format" if args.empty?
  760. args.each { |type| send(type, options.dup, &block) }
  761. end
  762. alias :all :any
  763. def custom(mime, options = {})
  764. options.reverse_merge!(content_type: mime.to_s)
  765. @context.formats = [mime.to_sym]
  766. options[:body] = block_given? ? yield : @default_render.call
  767. @responses << options
  768. end
  769. end
  770. end
  771. require 'tmpdir'
  772. module ActionMailer
  773. # This module handles everything related to mail delivery, from registering
  774. # new delivery methods to configuring the mail object to be sent.
  775. module DeliveryMethods
  776. extend ActiveSupport::Concern
  777. included do
  778. class_attribute :delivery_methods, :delivery_method
  779. # Do not make this inheritable, because we always want it to propagate
  780. cattr_accessor :raise_delivery_errors
  781. self.raise_delivery_errors = true
  782. cattr_accessor :perform_deliveries
  783. self.perform_deliveries = true
  784. self.delivery_methods = {}.freeze
  785. self.delivery_method = :smtp
  786. add_delivery_method :smtp, Mail::SMTP,
  787. address: "localhost",
  788. port: 25,
  789. domain: 'localhost.localdomain',
  790. user_name: nil,
  791. password: nil,
  792. authentication: nil,
  793. enable_starttls_auto: true
  794. add_delivery_method :file, Mail::FileDelivery,
  795. location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
  796. add_delivery_method :sendmail, Mail::Sendmail,
  797. location: '/usr/sbin/sendmail',
  798. arguments: '-i -t'
  799. add_delivery_method :test, Mail::TestMailer
  800. end
  801. module ClassMethods
  802. # Provides a list of emails that have been delivered by Mail::TestMailer
  803. delegate :deliveries, :deliveries=, to: Mail::TestMailer
  804. # Adds a new delivery method through the given class using the given
  805. # symbol as alias and the default options supplied.
  806. #
  807. # add_delivery_method :sendmail, Mail::Sendmail,
  808. # location: '/usr/sbin/sendmail',
  809. # arguments: '-i -t'
  810. def add_delivery_method(symbol, klass, default_options={})
  811. class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
  812. send(:"#{symbol}_settings=", default_options)
  813. self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
  814. end
  815. def wrap_delivery_behavior(mail, method=nil, options=nil) # :nodoc:
  816. method ||= self.delivery_method
  817. mail.delivery_handler = self
  818. case method
  819. when NilClass
  820. raise "Delivery method cannot be nil"
  821. when Symbol
  822. if klass = delivery_methods[method]
  823. mail.delivery_method(klass,(send(:"#{method}_settings") || {}).merge!(options || {}))
  824. else
  825. raise "Invalid delivery method #{method.inspect}"
  826. end
  827. else
  828. mail.delivery_method(method)
  829. end
  830. mail.perform_deliveries = perform_deliveries
  831. mail.raise_delivery_errors = raise_delivery_errors
  832. end
  833. end
  834. def wrap_delivery_behavior!(*args) # :nodoc:
  835. self.class.wrap_delivery_behavior(message, *args)
  836. end
  837. end
  838. end
  839. module ActionMailer
  840. class LogSubscriber < ActiveSupport::LogSubscriber
  841. def deliver(event)
  842. return unless logger.info?
  843. recipients = Array(event.payload[:to]).join(', ')
  844. info("\nSent mail to #{recipients} (#{event.duration.round(1)}ms)")
  845. debug(event.payload[:mail])
  846. end
  847. def receive(event)
  848. return unless logger.info?
  849. info("\nReceived mail (#{event.duration.round(1)}ms)")
  850. debug(event.payload[:mail])
  851. end
  852. def logger
  853. ActionMailer::Base.logger
  854. end
  855. end
  856. end
  857. ActionMailer::LogSubscriber.attach_to :action_mailer
  858. module ActionMailer
  859. module MailHelper
  860. # Take the text and format it, indented two spaces for each line, and
  861. # wrapped at 72 columns.
  862. def block_format(text)
  863. formatted = text.split(/\n\r?\n/).collect { |paragraph|
  864. format_paragraph(paragraph)
  865. }.join("\n\n")
  866. # Make list points stand on their own line
  867. formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
  868. formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
  869. formatted
  870. end
  871. # Access the mailer instance.
  872. def mailer
  873. @_controller
  874. end
  875. # Access the message instance.
  876. def message
  877. @_message
  878. end
  879. # Access the message attachments list.
  880. def attachments
  881. @_message.attachments
  882. end
  883. # Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
  884. #
  885. # my_text = 'Here is a sample text with more than 40 characters'
  886. #
  887. # format_paragraph(my_text, 25, 4)
  888. # # => " Here is a sample text with\n more than 40 characters"
  889. def format_paragraph(text, len = 72, indent = 2)
  890. sentences = [[]]
  891. text.split.each do |word|
  892. if sentences.first.present? && (sentences.last + [word]).join(' ').length > len
  893. sentences << [word]
  894. else
  895. sentences.last << word
  896. end
  897. end
  898. sentences.map { |sentence|
  899. "#{" " * indent}#{sentence.join(' ')}"
  900. }.join "\n"
  901. end
  902. end
  903. end
  904. require "action_mailer"
  905. require "rails"
  906. require "abstract_controller/railties/routes_helpers"
  907. module ActionMailer
  908. class Railtie < Rails::Railtie # :nodoc:
  909. config.action_mailer = ActiveSupport::OrderedOptions.new
  910. config.eager_load_namespaces << ActionMailer
  911. initializer "action_mailer.logger" do
  912. ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger }
  913. end
  914. initializer "action_mailer.set_configs" do |app|
  915. paths = app.config.paths
  916. options = app.config.action_mailer
  917. options.assets_dir ||= paths["public"].first
  918. options.javascripts_dir ||= paths["public/javascripts"].first
  919. options.stylesheets_dir ||= paths["public/stylesheets"].first
  920. # make sure readers methods get compiled
  921. options.asset_host ||= app.config.asset_host
  922. options.relative_url_root ||= app.config.relative_url_root
  923. ActiveSupport.on_load(:action_mailer) do
  924. include AbstractController::UrlFor
  925. extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
  926. include app.routes.mounted_helpers
  927. register_interceptors(options.delete(:interceptors))
  928. register_observers(options.delete(:observers))
  929. options.each { |k,v| send("#{k}=", v) }
  930. end
  931. end
  932. initializer "action_mailer.compile_config_methods" do
  933. ActiveSupport.on_load(:action_mailer) do
  934. config.compile_methods! if config.respond_to?(:compile_methods!)
  935. end
  936. end
  937. end
  938. end
  939. require 'active_support/test_case'
  940. module ActionMailer
  941. class NonInferrableMailerError < ::StandardError
  942. def initialize(name)
  943. super "Unable to determine the mailer to test from #{name}. " +
  944. "You'll need to specify it using tests YourMailer in your " +
  945. "test case definition"
  946. end
  947. end
  948. class TestCase < ActiveSupport::TestCase
  949. module Behavior
  950. extend ActiveSupport::Concern
  951. include ActiveSupport::Testing::ConstantLookup
  952. include TestHelper
  953. included do
  954. class_attribute :_mailer_class
  955. setup :initialize_test_deliveries
  956. setup :set_expected_mail
  957. end
  958. module ClassMethods
  959. def tests(mailer)
  960. case mailer
  961. when String, Symbol
  962. self._mailer_class = mailer.to_s.camelize.constantize
  963. when Module
  964. self._mailer_class = mailer
  965. else
  966. raise NonInferrableMailerError.new(mailer)
  967. end
  968. end
  969. def mailer_class
  970. if mailer = self._mailer_class
  971. mailer
  972. else
  973. tests determine_default_mailer(name)
  974. end
  975. end
  976. def determine_default_mailer(name)
  977. mailer = determine_constant_from_test_name(name) do |constant|
  978. Class === constant && constant < ActionMailer::Base
  979. end
  980. raise NonInferrableMailerError.new(name) if mailer.nil?
  981. mailer
  982. end
  983. end
  984. protected
  985. def initialize_test_deliveries
  986. ActionMailer::Base.delivery_method = :test
  987. ActionMailer::Base.perform_deliveries = true
  988. ActionMailer::Base.deliveries.clear
  989. end
  990. def set_expected_mail
  991. @expected = Mail.new
  992. @expected.content_type ["text", "plain", { "charset" => charset }]
  993. @expected.mime_version = '1.0'
  994. end
  995. private
  996. def charset
  997. "UTF-8"
  998. end
  999. def encode(subject)
  1000. Mail::Encodings.q_value_encode(subject, charset)
  1001. end
  1002. def read_fixture(action)
  1003. IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
  1004. end
  1005. end
  1006. include Behavior
  1007. end
  1008. end
  1009. module ActionMailer
  1010. module TestHelper
  1011. # Asserts that the number of emails sent matches the given number.
  1012. #
  1013. # def test_emails
  1014. # assert_emails 0
  1015. # ContactMailer.welcome.deliver
  1016. # assert_emails 1
  1017. # ContactMailer.welcome.deliver
  1018. # assert_emails 2
  1019. # end
  1020. #
  1021. # If a block is passed, that block should cause the specified number of
  1022. # emails to be sent.
  1023. #
  1024. # def test_emails_again
  1025. # assert_emails 1 do
  1026. # ContactMailer.welcome.deliver
  1027. # end
  1028. #
  1029. # assert_emails 2 do
  1030. # ContactMailer.welcome.deliver
  1031. # ContactMailer.welcome.deliver
  1032. # end
  1033. # end
  1034. def assert_emails(number)
  1035. if block_given?
  1036. original_count = ActionMailer::Base.deliveries.size
  1037. yield
  1038. new_count = ActionMailer::Base.deliveries.size
  1039. assert_equal original_count + number, new_count, "#{number} emails expected, but #{new_count - original_count} were sent"
  1040. else
  1041. assert_equal number, ActionMailer::Base.deliveries.size
  1042. end
  1043. end
  1044. # Assert that no emails have been sent.
  1045. #
  1046. # def test_emails
  1047. # assert_no_emails
  1048. # ContactMailer.welcome.deliver
  1049. # assert_emails 1
  1050. # end
  1051. #
  1052. # If a block is passed, that block should not cause any emails to be sent.
  1053. #
  1054. # def test_emails_again
  1055. # assert_no_emails do
  1056. # # No emails should be sent from this block
  1057. # end
  1058. # end
  1059. #
  1060. # Note: This assertion is simply a shortcut for:
  1061. #
  1062. # assert_emails 0
  1063. def assert_no_emails(&block)
  1064. assert_emails 0, &block
  1065. end
  1066. end
  1067. end
  1068. module ActionMailer
  1069. module VERSION #:nodoc:
  1070. MAJOR = 4
  1071. MINOR = 0
  1072. TINY = 0
  1073. PRE = "beta"
  1074. STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
  1075. end
  1076. end
  1077. #--
  1078. # Copyright (c) 2004-2013 David Heinemeier Hansson
  1079. #
  1080. # Permission is hereby granted, free of charge, to any person obtaining
  1081. # a copy of this software and associated documentation files (the
  1082. # "Software"), to deal in the Software without restriction, including
  1083. # without limitation the rights to use, copy, modify, merge, publish,
  1084. # distribute, sublicense, and/or sell copies of the Software, and to
  1085. # permit persons to whom the Software is furnished to do so, subject to
  1086. # the following conditions:
  1087. #
  1088. # The above copyright notice and this permission notice shall be
  1089. # included in all copies or substantial portions of the Software.
  1090. #
  1091. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  1092. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  1093. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  1094. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  1095. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  1096. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  1097. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  1098. #++
  1099. require 'abstract_controller'
  1100. require 'action_view'
  1101. require 'action_mailer/version'
  1102. # Common Active Support usage in Action Mailer
  1103. require 'active_support/rails'
  1104. require 'active_support/core_ext/class'
  1105. require 'active_support/core_ext/module/attr_internal'
  1106. require 'active_support/core_ext/string/inflections'
  1107. require 'active_support/lazy_load_hooks'
  1108. module ActionMailer
  1109. extend ::ActiveSupport::Autoload
  1110. eager_autoload do
  1111. autoload :Collector
  1112. end
  1113. autoload :Base
  1114. autoload :DeliveryMethods
  1115. autoload :MailHelper
  1116. autoload :TestCase
  1117. autoload :TestHelper
  1118. end
  1119. module Rails
  1120. module Generators
  1121. class MailerGenerator < NamedBase
  1122. source_root File.expand_path("../templates", __FILE__)
  1123. argument :actions, type: :array, default: [], banner: "method method"
  1124. check_class_collision
  1125. def create_mailer_file
  1126. template "mailer.rb", File.join('app/mailers', class_path, "#{file_name}.rb")
  1127. end
  1128. hook_for :template_engine, :test_framework
  1129. end
  1130. end
  1131. end
  1132. <% module_namespacing do -%>
  1133. class <%= class_name %> < ActionMailer::Base
  1134. default from: "from@example.com"
  1135. <% actions.each do |action| -%>
  1136. # Subject can be set in your I18n file at config/locales/en.yml
  1137. # with the following lookup:
  1138. #
  1139. # en.<%= file_path.tr("/",".") %>.<%= action %>.subject
  1140. #
  1141. def <%= action %>
  1142. @greeting = "Hi"
  1143. mail to: "to@example.org"
  1144. end
  1145. <% end -%>
  1146. end
  1147. <% end -%>
  1148. module AbstractController
  1149. module AssetPaths #:nodoc:
  1150. extend ActiveSupport::Concern
  1151. included do
  1152. config_accessor :asset_host, :assets_dir, :javascripts_dir,
  1153. :stylesheets_dir, :default_asset_host_protocol, :relative_url_root
  1154. end
  1155. end
  1156. end
  1157. require 'erubis'
  1158. require 'set'
  1159. require 'active_support/configurable'
  1160. require 'active_support/descendants_tracker'
  1161. require 'active_support/core_ext/module/anonymous'
  1162. module AbstractController
  1163. class Error < StandardError #:nodoc:
  1164. end
  1165. class ActionNotFound < StandardError #:nodoc:
  1166. end
  1167. # <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
  1168. # using it directly, and subclasses (like ActionController::Base) are
  1169. # expected to provide their own +render+ method, since rendering means
  1170. # different things depending on the context.
  1171. class Base
  1172. attr_internal :response_body
  1173. attr_internal :action_name
  1174. attr_internal :formats
  1175. include ActiveSupport::Configurable
  1176. extend ActiveSupport::DescendantsTracker
  1177. undef_method :not_implemented
  1178. class << self
  1179. attr_reader :abstract
  1180. alias_method :abstract?, :abstract
  1181. # Define a controller as abstract. See internal_methods for more
  1182. # details.
  1183. def abstract!
  1184. @abstract = true
  1185. end
  1186. def inherited(klass) # :nodoc:
  1187. # define the abstract ivar on subclasses so that we don't get
  1188. # uninitialized ivar warnings
  1189. unless klass.instance_variable_defined?(:@abstract)
  1190. klass.instance_variable_set(:@abstract, false)
  1191. end
  1192. super
  1193. end
  1194. # A list of all internal methods for a controller. This finds the first
  1195. # abstract superclass of a controller, and gets a list of all public
  1196. # instance methods on that abstract class. Public instance methods of
  1197. # a controller would normally be considered action methods, so methods
  1198. # declared on abstract classes are being removed.
  1199. # (ActionController::Metal and ActionController::Base are defined as abstract)
  1200. def internal_methods
  1201. controller = self
  1202. controller = controller.superclass until controller.abstract?
  1203. controller.public_instance_methods(true)
  1204. end
  1205. # The list of hidden actions. Defaults to an empty array.
  1206. # This can be modified by other modules or subclasses
  1207. # to specify particular actions as hidden.
  1208. #
  1209. # ==== Returns
  1210. # * <tt>Array</tt> - An array of method names that should not be considered actions.
  1211. def hidden_actions
  1212. []
  1213. end
  1214. # A list of method names that should be considered actions. This
  1215. # includes all public instance methods on a controller, less
  1216. # any internal methods (see #internal_methods), adding back in
  1217. # any methods that are internal, but still exist on the class
  1218. # itself. Finally, #hidden_actions are removed.
  1219. #
  1220. # ==== Returns
  1221. # * <tt>Set</tt> - A set of all methods that should be considered actions.
  1222. def action_methods
  1223. @action_methods ||= begin
  1224. # All public instance methods of this class, including ancestors
  1225. methods = (public_instance_methods(true) -
  1226. # Except for public instance methods of Base and its ancestors
  1227. internal_methods +
  1228. # Be sure to include shadowed public instance methods of this class
  1229. public_instance_methods(false)).uniq.map { |x| x.to_s } -
  1230. # And always exclude explicitly hidden actions
  1231. hidden_actions.to_a
  1232. # Clear out AS callback method pollution
  1233. Set.new(methods.reject { |method| method =~ /_one_time_conditions/ })
  1234. end
  1235. end
  1236. # action_methods are cached and there is sometimes need to refresh
  1237. # them. clear_action_methods! allows you to do that, so next time
  1238. # you run action_methods, they will be recalculated
  1239. def clear_action_methods!
  1240. @action_methods = nil
  1241. end
  1242. # Returns the full controller name, underscored, without the ending Controller.
  1243. # For instance, MyApp::MyPostsController would return "my_app/my_posts" for
  1244. # controller_path.
  1245. #
  1246. # ==== Returns
  1247. # * <tt>String</tt>
  1248. def controller_path
  1249. @controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
  1250. end
  1251. # Refresh the cached action_methods when a new action_method is added.
  1252. def method_added(name)
  1253. super
  1254. clear_action_methods!
  1255. end
  1256. end
  1257. abstract!
  1258. # Calls the action going through the entire action dispatch stack.
  1259. #
  1260. # The actual method that is called is determined by calling
  1261. # #method_for_action. If no method can handle the action, then an
  1262. # ActionNotFound error is raised.
  1263. #
  1264. # ==== Returns
  1265. # * <tt>self</tt>
  1266. def process(action, *args)
  1267. @_action_name = action_name = action.to_s
  1268. unless action_name = method_for_action(action_name)
  1269. raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
  1270. end
  1271. @_response_body = nil
  1272. process_action(action_name, *args)
  1273. end
  1274. # Delegates to the class' #controller_path
  1275. def controller_path
  1276. self.class.controller_path
  1277. end
  1278. # Delegates to the class' #action_methods
  1279. def action_methods
  1280. self.class.action_methods
  1281. end
  1282. # Returns true if a method for the action is available and
  1283. # can be dispatched, false otherwise.
  1284. #
  1285. # Notice that <tt>action_methods.include?("foo")</tt> may return
  1286. # false and <tt>available_action?("foo")</tt> returns true because
  1287. # this method considers actions that are also available
  1288. # through other means, for example, implicit render ones.
  1289. #
  1290. # ==== Parameters
  1291. # * <tt>action_name</tt> - The name of an action to be tested
  1292. #
  1293. # ==== Returns
  1294. # * <tt>TrueClass</tt>, <tt>FalseClass</tt>
  1295. def available_action?(action_name)
  1296. method_for_action(action_name).present?
  1297. end
  1298. private
  1299. # Returns true if the name can be considered an action because
  1300. # it has a method defined in the controller.
  1301. #
  1302. # ==== Parameters
  1303. # * <tt>name</tt> - The name of an action to be tested
  1304. #
  1305. # ==== Returns
  1306. # * <tt>TrueClass</tt>, <tt>FalseClass</tt>
  1307. #
  1308. # :api: private
  1309. def action_method?(name)
  1310. self.class.action_methods.include?(name)
  1311. end
  1312. # Call the action. Override this in a subclass to modify the
  1313. # behavior around processing an action. This, and not #process,
  1314. # is the intended way to override action dispatching.
  1315. #
  1316. # Notice that the first argument is the method to be dispatched
  1317. # which is *not* necessarily the same as the action name.
  1318. def process_action(method_name, *args)
  1319. send_action(method_name, *args)
  1320. end
  1321. # Actually call the method associated with the action. Override
  1322. # this method if you wish to change how action methods are called,
  1323. # not to add additional behavior around it. For example, you would
  1324. # override #send_action if you want to inject arguments into the
  1325. # method.
  1326. alias send_action send
  1327. # If the action name was not found, but a method called "action_missing"
  1328. # was found, #method_for_action will return "_handle_action_missing".
  1329. # This method calls #action_missing with the current action name.
  1330. def _handle_action_missing(*args)
  1331. action_missing(@_action_name, *args)
  1332. end
  1333. # Takes an action name and returns the name of the method that will
  1334. # handle the action. In normal cases, this method returns the same
  1335. # name as it receives. By default, if #method_for_action receives
  1336. # a name that is not an action, it will look for an #action_missing
  1337. # method and return "_handle_action_missing" if one is found.
  1338. #
  1339. # Subclasses may override this method to add additional conditions
  1340. # that should be considered an action. For instance, an HTTP controller
  1341. # with a template matching the action name is considered to exist.
  1342. #
  1343. # If you override this method to handle additional cases, you may
  1344. # also provide a method (like _handle_method_missing) to handle
  1345. # the case.
  1346. #
  1347. # If none of these conditions are true, and method_for_action
  1348. # returns nil, an ActionNotFound exception will be raised.
  1349. #
  1350. # ==== Parameters
  1351. # * <tt>action_name</tt> - An action name to find a method name for
  1352. #
  1353. # ==== Returns
  1354. # * <tt>string</tt> - The name of the method that handles the action
  1355. # * <tt>nil</tt> - No method name could be found. Raise ActionNotFound.
  1356. def method_for_action(action_name)
  1357. if action_method?(action_name)
  1358. action_name
  1359. elsif respond_to?(:action_missing, true)
  1360. "_handle_action_missing"
  1361. end
  1362. end
  1363. end
  1364. end
  1365. module AbstractController
  1366. module Callbacks
  1367. extend ActiveSupport::Concern
  1368. # Uses ActiveSupport::Callbacks as the base functionality. For
  1369. # more details on the whole callback system, read the documentation
  1370. # for ActiveSupport::Callbacks.
  1371. include ActiveSupport::Callbacks
  1372. included do
  1373. define_callbacks :process_action, :terminator => "response_body", :skip_after_callbacks_if_terminated => true
  1374. end
  1375. # Override AbstractController::Base's process_action to run the
  1376. # process_action callbacks around the normal behavior.
  1377. def process_action(*args)
  1378. run_callbacks(:process_action) do
  1379. super
  1380. end
  1381. end
  1382. module ClassMethods
  1383. # If :only or :except are used, convert the options into the
  1384. # :unless and :if options of ActiveSupport::Callbacks.
  1385. # The basic idea is that :only => :index gets converted to
  1386. # :if => proc {|c| c.action_name == "index" }.
  1387. #
  1388. # ==== Options
  1389. # * <tt>only</tt> - The callback should be run only for this action
  1390. # * <tt>except</tt> - The callback should be run for all actions except this action
  1391. def _normalize_callback_options(options)
  1392. _normalize_callback_option(options, :only, :if)
  1393. _normalize_callback_option(options, :except, :unless)
  1394. end
  1395. def _normalize_callback_option(options, from, to) # :nodoc:
  1396. if from = options[from]
  1397. from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ")
  1398. options[to] = Array(options[to]) << from
  1399. end
  1400. end
  1401. # Skip before, after, and around action callbacks matching any of the names
  1402. # Aliased as skip_filter.
  1403. #
  1404. # ==== Parameters
  1405. # * <tt>names</tt> - A list of valid names that could be used for
  1406. # callbacks. Note that skipping uses Ruby equality, so it's
  1407. # impossible to skip a callback defined using an anonymous proc
  1408. # using #skip_filter
  1409. def skip_action_callback(*names)
  1410. skip_before_action(*names)
  1411. skip_after_action(*names)
  1412. skip_around_action(*names)
  1413. end
  1414. alias_method :skip_filter, :skip_action_callback
  1415. # Take callback names and an optional callback proc, normalize them,
  1416. # then call the block with each callback. This allows us to abstract
  1417. # the normalization across several methods that use it.
  1418. #
  1419. # ==== Parameters
  1420. # * <tt>callbacks</tt> - An array of callbacks, with an optional
  1421. # options hash as the last parameter.
  1422. # * <tt>block</tt> - A proc that should be added to the callbacks.
  1423. #
  1424. # ==== Block Parameters
  1425. # * <tt>name</tt> - The callback to be added
  1426. # * <tt>options</tt> - A hash of options to be used when adding the callback
  1427. def _insert_callbacks(callbacks, block = nil)
  1428. options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
  1429. _normalize_callback_options(options)
  1430. callbacks.push(block) if block
  1431. callbacks.each do |callback|
  1432. yield callback, options
  1433. end
  1434. end
  1435. ##
  1436. # :method: before_action
  1437. #
  1438. # :call-seq: before_action(names, block)
  1439. #
  1440. # Append a callback before actions. See _insert_callbacks for parameter details.
  1441. # Aliased as before_filter.
  1442. ##
  1443. # :method: prepend_before_action
  1444. #
  1445. # :call-seq: prepend_before_action(names, block)
  1446. #
  1447. # Prepend a callback before actions. See _insert_callbacks for parameter details.
  1448. # Aliased as prepend_before_filter.
  1449. ##
  1450. # :method: skip_before_action
  1451. #
  1452. # :call-seq: skip_before_action(names)
  1453. #
  1454. # Skip a callback before actions. See _insert_callbacks for parameter details.
  1455. # Aliased as skip_before_filter.
  1456. ##
  1457. # :method: append_before_action
  1458. #
  1459. # :call-seq: append_before_action(names, block)
  1460. #
  1461. # Append a callback before actions. See _insert_callbacks for parameter details.
  1462. # Aliased as append_before_filter.
  1463. ##
  1464. # :method: after_action
  1465. #
  1466. # :call-seq: after_action(names, block)
  1467. #
  1468. # Append a callback after actions. See _insert_callbacks for parameter details.
  1469. # Aliased as after_filter.
  1470. ##
  1471. # :method: prepend_after_action
  1472. #
  1473. # :call-seq: prepend_after_action(names, block)
  1474. #
  1475. # Prepend a callback after actions. See _insert_callbacks for parameter details.
  1476. # Aliased as prepend_after_filter.
  1477. ##
  1478. # :method: skip_after_action
  1479. #
  1480. # :call-seq: skip_after_action(names)
  1481. #
  1482. # Skip a callback after actions. See _insert_callbacks for parameter details.
  1483. # Aliased as skip_after_filter.
  1484. ##
  1485. # :method: append_after_action
  1486. #
  1487. # :call-seq: append_after_action(names, block)
  1488. #
  1489. # Append a callback after actions. See _insert_callbacks for parameter details.
  1490. # Aliased as append_after_filter.
  1491. ##
  1492. # :method: around_action
  1493. #
  1494. # :call-seq: around_action(names, block)
  1495. #
  1496. # Append a callback around actions. See _insert_callbacks for parameter details.
  1497. # Aliased as around_filter.
  1498. ##
  1499. # :method: prepend_around_action
  1500. #
  1501. # :call-seq: prepend_around_action(names, block)
  1502. #
  1503. # Prepend a callback around actions. See _insert_callbacks for parameter details.
  1504. # Aliased as prepend_around_filter.
  1505. ##
  1506. # :method: skip_around_action
  1507. #
  1508. # :call-seq: skip_around_action(names)
  1509. #
  1510. # Skip a callback around actions. See _insert_callbacks for parameter details.
  1511. # Aliased as skip_around_filter.
  1512. ##
  1513. # :method: append_around_action
  1514. #
  1515. # :call-seq: append_around_action(names, block)
  1516. #
  1517. # Append a callback around actions. See _insert_callbacks for parameter details.
  1518. # Aliased as append_around_filter.
  1519. # set up before_action, prepend_before_action, skip_before_action, etc.
  1520. # for each of before, after, and around.
  1521. [:before, :after, :around].each do |callback|
  1522. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  1523. # Append a before, after or around callback. See _insert_callbacks
  1524. # for details on the allowed parameters.
  1525. def #{callback}_action(*names, &blk) # def before_action(*names, &blk)
  1526. _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
  1527. set_callback(:process_action, :#{callback}, name, options) # set_callback(:process_action, :before, name, options)
  1528. end # end
  1529. end # end
  1530. alias_method :#{callback}_filter, :#{callback}_action
  1531. # Prepend a before, after or around callback. See _insert_callbacks
  1532. # for details on the allowed parameters.
  1533. def prepend_#{callback}_action(*names, &blk) # def prepend_before_action(*names, &blk)
  1534. _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
  1535. set_callback(:process_action, :#{callback}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
  1536. end # end
  1537. end # end
  1538. alias_method :prepend_#{callback}_filter, :prepend_#{callback}_action
  1539. # Skip a before, after or around callback. See _insert_callbacks
  1540. # for details on the allowed parameters.
  1541. def skip_#{callback}_action(*names) # def skip_before_action(*names)
  1542. _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
  1543. skip_callback(:process_action, :#{callback}, name, options) # skip_callback(:process_action, :before, name, options)
  1544. end # end
  1545. end # end
  1546. alias_method :skip_#{callback}_filter, :skip_#{callback}_action
  1547. # *_action is the same as append_*_action
  1548. alias_method :append_#{callback}_action, :#{callback}_action # alias_method :append_before_action, :before_action
  1549. alias_method :append_#{callback}_filter, :#{callback}_action # alias_method :append_before_filter, :before_action
  1550. RUBY_EVAL
  1551. end
  1552. end
  1553. end
  1554. end
  1555. require "action_dispatch/http/mime_type"
  1556. module AbstractController
  1557. module Collector
  1558. def self.generate_method_for_mime(mime)
  1559. sym = mime.is_a?(Symbol) ? mime : mime.to_sym
  1560. const = sym.upcase
  1561. class_eval <<-RUBY, __FILE__, __LINE__ + 1
  1562. def #{sym}(*args, &block) # def html(*args, &block)
  1563. custom(Mime::#{const}, *args, &block) # custom(Mime::HTML, *args, &block)
  1564. end # end
  1565. RUBY
  1566. end
  1567. Mime::SET.each do |mime|
  1568. generate_method_for_mime(mime)
  1569. end
  1570. Mime::Type.register_callback do |mime|
  1571. generate_method_for_mime(mime) unless self.instance_methods.include?(mime.to_sym)
  1572. end
  1573. protected
  1574. def method_missing(symbol, &block)
  1575. mime_constant = Mime.const_get(symbol.upcase)
  1576. if Mime::SET.include?(mime_constant)
  1577. AbstractController::Collector.generate_method_for_mime(mime_constant)
  1578. send(symbol, &block)
  1579. else
  1580. super
  1581. end
  1582. end
  1583. end
  1584. end
  1585. require 'active_support/dependencies'
  1586. module AbstractController
  1587. module Helpers
  1588. extend ActiveSupport::Concern
  1589. included do
  1590. class_attribute :_helpers
  1591. self._helpers = Module.new
  1592. class_attribute :_helper_methods
  1593. self._helper_methods = Array.new
  1594. end
  1595. module ClassMethods
  1596. # When a class is inherited, wrap its helper module in a new module.
  1597. # This ensures that the parent class's module can be changed
  1598. # independently of the child class's.
  1599. def inherited(klass)
  1600. helpers = _helpers
  1601. klass._helpers = Module.new { include helpers }
  1602. klass.class_eval { default_helper_module! } unless klass.anonymous?
  1603. super
  1604. end
  1605. # Declare a controller method as a helper. For example, the following
  1606. # makes the +current_user+ controller method available to the view:
  1607. # class ApplicationController < ActionController::Base
  1608. # helper_method :current_user, :logged_in?
  1609. #
  1610. # def current_user
  1611. # @current_user ||= User.find_by_id(session[:user])
  1612. # end
  1613. #
  1614. # def logged_in?
  1615. # current_user != nil
  1616. # end
  1617. # end
  1618. #
  1619. # In a view:
  1620. # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
  1621. #
  1622. # ==== Parameters
  1623. # * <tt>method[, method]</tt> - A name or names of a method on the controller
  1624. # to be made available on the view.
  1625. def helper_method(*meths)
  1626. meths.flatten!
  1627. self._helper_methods += meths
  1628. meths.each do |meth|
  1629. _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
  1630. def #{meth}(*args, &blk) # def current_user(*args, &blk)
  1631. controller.send(%(#{meth}), *args, &blk) # controller.send(:current_user, *args, &blk)
  1632. end # end
  1633. ruby_eval
  1634. end
  1635. end
  1636. # The +helper+ class method can take a series of helper module names, a block, or both.
  1637. #
  1638. # ==== Options
  1639. # * <tt>*args</tt> - Module, Symbol, String, :all
  1640. # * <tt>block</tt> - A block defining helper methods
  1641. #
  1642. # When the argument is a module it will be included directly in the template class.
  1643. # helper FooHelper # => includes FooHelper
  1644. #
  1645. # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
  1646. # and include the module in the template class. The second form illustrates how to include custom helpers
  1647. # when working with namespaced controllers, or other cases where the file containing the helper definition is not
  1648. # in one of Rails' standard load paths:
  1649. # helper :foo # => requires 'foo_helper' and includes FooHelper
  1650. # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
  1651. #
  1652. # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
  1653. # to the template.
  1654. #
  1655. # # One line
  1656. # helper { def hello() "Hello, world!" end }
  1657. #
  1658. # # Multi-line
  1659. # helper do
  1660. # def foo(bar)
  1661. # "#{bar} is the very best"
  1662. # end
  1663. # end
  1664. #
  1665. # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
  1666. # +symbols+, +strings+, +modules+ and blocks.
  1667. #
  1668. # helper(:three, BlindHelper) { def mice() 'mice' end }
  1669. #
  1670. def helper(*args, &block)
  1671. modules_for_helpers(args).each do |mod|
  1672. add_template_helper(mod)
  1673. end
  1674. _helpers.module_eval(&block) if block_given?
  1675. end
  1676. # Clears up all existing helpers in this class, only keeping the helper
  1677. # with the same name as this class.
  1678. def clear_helpers
  1679. inherited_helper_methods = _helper_methods
  1680. self._helpers = Module.new
  1681. self._helper_methods = Array.new
  1682. inherited_helper_methods.each { |meth| helper_method meth }
  1683. default_helper_module! unless anonymous?
  1684. end
  1685. # Returns a list of modules, normalized from the acceptable kinds of
  1686. # helpers with the following behavior:
  1687. #
  1688. # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
  1689. # and "foo_bar_helper.rb" is loaded using require_dependency.
  1690. #
  1691. # Module:: No further processing
  1692. #
  1693. # After loading the appropriate files, the corresponding modules
  1694. # are returned.
  1695. #
  1696. # ==== Parameters
  1697. # * <tt>args</tt> - An array of helpers
  1698. #
  1699. # ==== Returns
  1700. # * <tt>Array</tt> - A normalized list of modules for the list of
  1701. # helpers provided.
  1702. def modules_for_helpers(args)
  1703. args.flatten.map! do |arg|
  1704. case arg
  1705. when String, Symbol
  1706. file_name = "#{arg.to_s.underscore}_helper"
  1707. begin
  1708. require_dependency(file_name)
  1709. rescue LoadError => e
  1710. raise MissingHelperError.new(e, file_name)
  1711. end
  1712. file_name.camelize.constantize
  1713. when Module
  1714. arg
  1715. else
  1716. raise ArgumentError, "helper must be a String, Symbol, or Module"
  1717. end
  1718. end
  1719. end
  1720. class MissingHelperError < LoadError
  1721. def initialize(error, path)
  1722. @error = error
  1723. @path = "helpers/#{path}.rb"
  1724. set_backtrace error.backtrace
  1725. super("Missing helper file helpers/%s.rb" % path)
  1726. end
  1727. end
  1728. private
  1729. # Makes all the (instance) methods in the helper module available to templates
  1730. # rendered through this controller.
  1731. #
  1732. # ==== Parameters
  1733. # * <tt>module</tt> - The module to include into the current helper module
  1734. # for the class
  1735. def add_template_helper(mod)
  1736. _helpers.module_eval { include mod }
  1737. end
  1738. def default_helper_module!
  1739. module_name = name.sub(/Controller$/, '')
  1740. module_path = module_name.underscore
  1741. helper module_path
  1742. rescue MissingSourceFile => e
  1743. raise e unless e.is_missing? "helpers/#{module_path}_helper"
  1744. rescue NameError => e
  1745. raise e unless e.missing_name? "#{module_name}Helper"
  1746. end
  1747. end
  1748. end
  1749. end
  1750. require "active_support/core_ext/module/remove_method"
  1751. module AbstractController
  1752. # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
  1753. # repeated setups. The inclusion pattern has pages that look like this:
  1754. #
  1755. # <%= render "shared/header" %>
  1756. # Hello World
  1757. # <%= render "shared/footer" %>
  1758. #
  1759. # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
  1760. # and if you ever want to change the structure of these two includes, you'll have to change all the templates.
  1761. #
  1762. # With layouts, you can flip it around and have the common structure know where to insert changing content. This means
  1763. # that the header and footer are only mentioned in one place, like this:
  1764. #
  1765. # // The header part of this layout
  1766. # <%= yield %>
  1767. # // The footer part of this layout
  1768. #
  1769. # And then you have content pages that look like this:
  1770. #
  1771. # hello world
  1772. #
  1773. # At rendering time, the content page is computed and then inserted in the layout, like this:
  1774. #
  1775. # // The header part of this layout
  1776. # hello world
  1777. # // The footer part of this layout
  1778. #
  1779. # == Accessing shared variables
  1780. #
  1781. # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
  1782. # references that won't materialize before rendering time:
  1783. #
  1784. # <h1><%= @page_title %></h1>
  1785. # <%= yield %>
  1786. #
  1787. # ...and content pages that fulfill these references _at_ rendering time:
  1788. #
  1789. # <% @page_title = "Welcome" %>
  1790. # Off-world colonies offers you a chance to start a new life
  1791. #
  1792. # The result after rendering is:
  1793. #
  1794. # <h1>Welcome</h1>
  1795. # Off-world colonies offers you a chance to start a new life
  1796. #
  1797. # == Layout assignment
  1798. #
  1799. # You can either specify a layout declaratively (using the #layout class method) or give
  1800. # it the same name as your controller, and place it in <tt>app/views/layouts</tt>.
  1801. # If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance.
  1802. #
  1803. # For instance, if you have PostsController and a template named <tt>app/views/layouts/posts.html.erb</tt>,
  1804. # that template will be used for all actions in PostsController and controllers inheriting
  1805. # from PostsController.
  1806. #
  1807. # If you use a module, for instance Weblog::PostsController, you will need a template named
  1808. # <tt>app/views/layouts/weblog/posts.html.erb</tt>.
  1809. #
  1810. # Since all your controllers inherit from ApplicationController, they will use
  1811. # <tt>app/views/layouts/application.html.erb</tt> if no other layout is specified
  1812. # or provided.
  1813. #
  1814. # == Inheritance Examples
  1815. #
  1816. # class BankController < ActionController::Base
  1817. # # bank.html.erb exists
  1818. #
  1819. # class ExchangeController < BankController
  1820. # # exchange.html.erb exists
  1821. #
  1822. # class CurrencyController < BankController
  1823. #
  1824. # class InformationController < BankController
  1825. # layout "information"
  1826. #
  1827. # class TellerController < InformationController
  1828. # # teller.html.erb exists
  1829. #
  1830. # class EmployeeController < InformationController
  1831. # # employee.html.erb exists
  1832. # layout nil
  1833. #
  1834. # class VaultController < BankController
  1835. # layout :access_level_layout
  1836. #
  1837. # class TillController < BankController
  1838. # layout false
  1839. #
  1840. # In these examples, we have three implicit lookup scenarios:
  1841. # * The BankController uses the "bank" layout.
  1842. # * The ExchangeController uses the "exchange" layout.
  1843. # * The CurrencyController inherits the layout from BankController.
  1844. #
  1845. # However, when a layout is explicitly set, the explicitly set layout wins:
  1846. # * The InformationController uses the "information" layout, explicitly set.
  1847. # * The TellerController also uses the "information" layout, because the parent explicitly set it.
  1848. # * The EmployeeController uses the "employee" layout, because it set the layout to nil, resetting the parent configuration.
  1849. # * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
  1850. # * The TillController does not use a layout at all.
  1851. #
  1852. # == Types of layouts
  1853. #
  1854. # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
  1855. # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
  1856. # be done either by specifying a method reference as a symbol or using an inline method (as a proc).
  1857. #
  1858. # The method reference is the preferred approach to variable layouts and is used like this:
  1859. #
  1860. # class WeblogController < ActionController::Base
  1861. # layout :writers_and_readers
  1862. #
  1863. # def index
  1864. # # fetching posts
  1865. # end
  1866. #
  1867. # private
  1868. # def writers_and_readers
  1869. # logged_in? ? "writer_layout" : "reader_layout"
  1870. # end
  1871. # end
  1872. #
  1873. # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
  1874. # is logged in or not.
  1875. #
  1876. # If you want to use an inline method, such as a proc, do something like this:
  1877. #
  1878. # class WeblogController < ActionController::Base
  1879. # layout proc { |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
  1880. # end
  1881. #
  1882. # If an argument isn't given to the proc, it's evaluated in the context of
  1883. # the current controller anyway.
  1884. #
  1885. # class WeblogController < ActionController::Base
  1886. # layout proc { logged_in? ? "writer_layout" : "reader_layout" }
  1887. # end
  1888. #
  1889. # Of course, the most common way of specifying a layout is still just as a plain template name:
  1890. #
  1891. # class WeblogController < ActionController::Base
  1892. # layout "weblog_standard"
  1893. # end
  1894. #
  1895. # The template will be looked always in <tt>app/views/layouts/</tt> folder. But you can point
  1896. # <tt>layouts</tt> folder direct also. <tt>layout "layouts/demo"</tt> is the same as <tt>layout "demo"</tt>.
  1897. #
  1898. # Setting the layout to nil forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
  1899. # Setting it to nil is useful to re-enable template lookup overriding a previous configuration set in the parent:
  1900. #
  1901. # class ApplicationController < ActionController::Base
  1902. # layout "application"
  1903. # end
  1904. #
  1905. # class PostsController < ApplicationController
  1906. # # Will use "application" layout
  1907. # end
  1908. #
  1909. # class CommentsController < ApplicationController
  1910. # # Will search for "comments" layout and fallback "application" layout
  1911. # layout nil
  1912. # end
  1913. #
  1914. # == Conditional layouts
  1915. #
  1916. # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
  1917. # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
  1918. # <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
  1919. #
  1920. # class WeblogController < ActionController::Base
  1921. # layout "weblog_standard", except: :rss
  1922. #
  1923. # # ...
  1924. #
  1925. # end
  1926. #
  1927. # This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
  1928. # be rendered directly, without wrapping a layout around the rendered view.
  1929. #
  1930. # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
  1931. # #<tt>except: [ :rss, :text_only ]</tt> is valid, as is <tt>except: :rss</tt>.
  1932. #
  1933. # == Using a different layout in the action render call
  1934. #
  1935. # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
  1936. # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
  1937. # You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
  1938. #
  1939. # class WeblogController < ActionController::Base
  1940. # layout "weblog_standard"
  1941. #
  1942. # def help
  1943. # render action: "help", layout: "help"
  1944. # end
  1945. # end
  1946. #
  1947. # This will override the controller-wide "weblog_standard" layout, and will render the help action with the "help" layout instead.
  1948. module Layouts
  1949. extend ActiveSupport::Concern
  1950. include Rendering
  1951. included do
  1952. class_attribute :_layout, :_layout_conditions, :instance_accessor => false
  1953. self._layout = nil
  1954. self._layout_conditions = {}
  1955. _write_layout_method
  1956. end
  1957. delegate :_layout_conditions, to: :class
  1958. module ClassMethods
  1959. def inherited(klass) # :nodoc:
  1960. super
  1961. klass._write_layout_method
  1962. end
  1963. # This module is mixed in if layout conditions are provided. This means
  1964. # that if no layout conditions are used, this method is not used
  1965. module LayoutConditions # :nodoc:
  1966. private
  1967. # Determines whether the current action has a layout definition by
  1968. # checking the action name against the :only and :except conditions
  1969. # set by the <tt>layout</tt> method.
  1970. #
  1971. # ==== Returns
  1972. # * <tt> Boolean</tt> - True if the action has a layout definition, false otherwise.
  1973. def _conditional_layout?
  1974. return unless super
  1975. conditions = _layout_conditions
  1976. if only = conditions[:only]
  1977. only.include?(action_name)
  1978. elsif except = conditions[:except]
  1979. !except.include?(action_name)
  1980. else
  1981. true
  1982. end
  1983. end
  1984. end
  1985. # Specify the layout to use for this class.
  1986. #
  1987. # If the specified layout is a:
  1988. # String:: the String is the template name
  1989. # Symbol:: call the method specified by the symbol, which will return the template name
  1990. # false:: There is no layout
  1991. # true:: raise an ArgumentError
  1992. # nil:: Force default layout behavior with inheritance
  1993. #
  1994. # ==== Parameters
  1995. # * <tt>layout</tt> - The layout to use.
  1996. #
  1997. # ==== Options (conditions)
  1998. # * :only - A list of actions to apply this layout to.
  1999. # * :except - Apply this layout to all actions but this one.
  2000. def layout(layout, conditions = {})
  2001. include LayoutConditions unless conditions.empty?
  2002. conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
  2003. self._layout_conditions = conditions
  2004. self._layout = layout
  2005. _write_layout_method
  2006. end
  2007. # If no layout is supplied, look for a template named the return
  2008. # value of this method.
  2009. #
  2010. # ==== Returns
  2011. # * <tt>String</tt> - A template name
  2012. def _implied_layout_name # :nodoc:
  2013. controller_path
  2014. end
  2015. # Creates a _layout method to be called by _default_layout .
  2016. #
  2017. # If a layout is not explicitly mentioned then look for a layout with the controller's name.
  2018. # if nothing is found then try same procedure to find super class's layout.
  2019. def _write_layout_method # :nodoc:
  2020. remove_possible_method(:_layout)
  2021. prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
  2022. name_clause = if name
  2023. <<-RUBY
  2024. lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first || super
  2025. RUBY
  2026. else
  2027. <<-RUBY
  2028. super
  2029. RUBY
  2030. end
  2031. layout_definition = case _layout
  2032. when String
  2033. _layout.inspect
  2034. when Symbol
  2035. <<-RUBY
  2036. #{_layout}.tap do |layout|
  2037. unless layout.is_a?(String) || !layout
  2038. raise ArgumentError, "Your layout method :#{_layout} returned \#{layout}. It " \
  2039. "should have returned a String, false, or nil"
  2040. end
  2041. end
  2042. RUBY
  2043. when Proc
  2044. define_method :_layout_from_proc, &_layout
  2045. _layout.arity == 0 ? "_layout_from_proc" : "_layout_from_proc(self)"
  2046. when false
  2047. nil
  2048. when true
  2049. raise ArgumentError, "Layouts must be specified as a String, Symbol, Proc, false, or nil"
  2050. when nil
  2051. name_clause
  2052. end
  2053. self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
  2054. def _layout
  2055. if _conditional_layout?
  2056. #{layout_definition}
  2057. else
  2058. #{name_clause}
  2059. end
  2060. end
  2061. private :_layout
  2062. RUBY
  2063. end
  2064. end
  2065. def _normalize_options(options) # :nodoc:
  2066. super
  2067. if _include_layout?(options)
  2068. layout = options.delete(:layout) { :default }
  2069. options[:layout] = _layout_for_option(layout)
  2070. end
  2071. end
  2072. attr_internal_writer :action_has_layout
  2073. def initialize(*) # :nodoc:
  2074. @_action_has_layout = true
  2075. super
  2076. end
  2077. # Controls whether an action should be rendered using a layout.
  2078. # If you want to disable any <tt>layout</tt> settings for the
  2079. # current action so that it is rendered without a layout then
  2080. # either override this method in your controller to return false
  2081. # for that action or set the <tt>action_has_layout</tt> attribute
  2082. # to false before rendering.
  2083. def action_has_layout?
  2084. @_action_has_layout
  2085. end
  2086. private
  2087. def _conditional_layout?
  2088. true
  2089. end
  2090. # This will be overwritten by _write_layout_method
  2091. def _layout; end
  2092. # Determine the layout for a given name, taking into account the name type.
  2093. #
  2094. # ==== Parameters
  2095. # * <tt>name</tt> - The name of the template
  2096. def _layout_for_option(name)
  2097. case name
  2098. when String then _normalize_layout(name)
  2099. when Proc then name
  2100. when true then Proc.new { _default_layout(true) }
  2101. when :default then Proc.new { _default_layout(false) }
  2102. when false, nil then nil
  2103. else
  2104. raise ArgumentError,
  2105. "String, Proc, :default, true, or false, expected for `layout'; you passed #{name.inspect}"
  2106. end
  2107. end
  2108. def _normalize_layout(value)
  2109. value.is_a?(String) && value !~ /\blayouts/ ? "layouts/#{value}" : value
  2110. end
  2111. # Returns the default layout for this controller.
  2112. # Optionally raises an exception if the layout could not be found.
  2113. #
  2114. # ==== Parameters
  2115. # * <tt>require_layout</tt> - If set to true and layout is not found,
  2116. # an ArgumentError exception is raised (defaults to false)
  2117. #
  2118. # ==== Returns
  2119. # * <tt>template</tt> - The template object for the default layout (or nil)
  2120. def _default_layout(require_layout = false)
  2121. begin
  2122. value = _layout if action_has_layout?
  2123. rescue NameError => e
  2124. raise e, "Could not render layout: #{e.message}"
  2125. end
  2126. if require_layout && action_has_layout? && !value
  2127. raise ArgumentError,
  2128. "There was no default layout for #{self.class} in #{view_paths.inspect}"
  2129. end
  2130. _normalize_layout(value)
  2131. end
  2132. def _include_layout?(options)
  2133. (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
  2134. end
  2135. end
  2136. end
  2137. require "active_support/benchmarkable"
  2138. module AbstractController
  2139. module Logger #:nodoc:
  2140. extend ActiveSupport::Concern
  2141. included do
  2142. config_accessor :logger
  2143. include ActiveSupport::Benchmarkable
  2144. end
  2145. end
  2146. end
  2147. module AbstractController
  2148. module Railties
  2149. module RoutesHelpers
  2150. def self.with(routes)
  2151. Module.new do
  2152. define_method(:inherited) do |klass|
  2153. super(klass)
  2154. if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
  2155. klass.send(:include, namespace.railtie_routes_url_helpers)
  2156. else
  2157. klass.send(:include, routes.url_helpers)
  2158. end
  2159. end
  2160. end
  2161. end
  2162. end
  2163. end
  2164. end
  2165. require "abstract_controller/base"
  2166. require "action_view"
  2167. module AbstractController
  2168. class DoubleRenderError < Error
  2169. DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
  2170. def initialize(message = nil)
  2171. super(message || DEFAULT_MESSAGE)
  2172. end
  2173. end
  2174. # This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
  2175. # it will trigger the lookup_context and consequently expire the cache.
  2176. class I18nProxy < ::I18n::Config #:nodoc:
  2177. attr_reader :original_config, :lookup_context
  2178. def initialize(original_config, lookup_context)
  2179. original_config = original_config.original_config if original_config.respond_to?(:original_config)
  2180. @original_config, @lookup_context = original_config, lookup_context
  2181. end
  2182. def locale
  2183. @original_config.locale
  2184. end
  2185. def locale=(value)
  2186. @lookup_context.locale = value
  2187. end
  2188. end
  2189. module Rendering
  2190. extend ActiveSupport::Concern
  2191. include AbstractController::ViewPaths
  2192. included do
  2193. class_attribute :protected_instance_variables
  2194. self.protected_instance_variables = []
  2195. end
  2196. # Overwrite process to setup I18n proxy.
  2197. def process(*) #:nodoc:
  2198. old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
  2199. super
  2200. ensure
  2201. I18n.config = old_config
  2202. end
  2203. module ClassMethods
  2204. def view_context_class
  2205. @view_context_class ||= begin
  2206. routes = respond_to?(:_routes) && _routes
  2207. helpers = respond_to?(:_helpers) && _helpers
  2208. Class.new(ActionView::Base) do
  2209. if routes
  2210. include routes.url_helpers
  2211. include routes.mounted_helpers
  2212. end
  2213. if helpers
  2214. include helpers
  2215. end
  2216. end
  2217. end
  2218. end
  2219. end
  2220. attr_internal_writer :view_context_class
  2221. def view_context_class
  2222. @_view_context_class ||= self.class.view_context_class
  2223. end
  2224. # An instance of a view class. The default view class is ActionView::Base
  2225. #
  2226. # The view class must have the following methods:
  2227. # View.new[lookup_context, assigns, controller]
  2228. # Create a new ActionView instance for a controller
  2229. # View#render[options]
  2230. # Returns String with the rendered template
  2231. #
  2232. # Override this method in a module to change the default behavior.
  2233. def view_context
  2234. view_context_class.new(view_renderer, view_assigns, self)
  2235. end
  2236. # Returns an object that is able to render templates.
  2237. def view_renderer
  2238. @_view_renderer ||= ActionView::Renderer.new(lookup_context)
  2239. end
  2240. # Normalize arguments, options and then delegates render_to_body and
  2241. # sticks the result in self.response_body.
  2242. def render(*args, &block)
  2243. options = _normalize_render(*args, &block)
  2244. self.response_body = render_to_body(options)
  2245. end
  2246. # Raw rendering of a template to a string. Just convert the results of
  2247. # render_response into a String.
  2248. # :api: plugin
  2249. def render_to_string(*args, &block)
  2250. options = _normalize_render(*args, &block)
  2251. render_to_body(options)
  2252. end
  2253. # Raw rendering of a template to a Rack-compatible body.
  2254. # :api: plugin
  2255. def render_to_body(options = {})
  2256. _process_options(options)
  2257. _render_template(options)
  2258. end
  2259. # Find and renders a template based on the options given.
  2260. # :api: private
  2261. def _render_template(options) #:nodoc:
  2262. lookup_context.rendered_format = nil if options[:formats]
  2263. view_renderer.render(view_context, options)
  2264. end
  2265. DEFAULT_PROTECTED_INSTANCE_VARIABLES = [
  2266. :@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config,
  2267. :@_view_context_class, :@_view_renderer, :@_lookup_context
  2268. ]
  2269. # This method should return a hash with assigns.
  2270. # You can overwrite this configuration per controller.
  2271. # :api: public
  2272. def view_assigns
  2273. hash = {}
  2274. variables = instance_variables
  2275. variables -= protected_instance_variables
  2276. variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES
  2277. variables.each { |name| hash[name[1..-1]] = instance_variable_get(name) }
  2278. hash
  2279. end
  2280. private
  2281. # Normalize args and options.
  2282. # :api: private
  2283. def _normalize_render(*args, &block)
  2284. options = _normalize_args(*args, &block)
  2285. _normalize_options(options)
  2286. options
  2287. end
  2288. # Normalize args by converting render "foo" to render :action => "foo" and
  2289. # render "foo/bar" to render :file => "foo/bar".
  2290. # :api: plugin
  2291. def _normalize_args(action=nil, options={})
  2292. case action
  2293. when NilClass
  2294. when Hash
  2295. options = action
  2296. when String, Symbol
  2297. action = action.to_s
  2298. key = action.include?(?/) ? :file : :action
  2299. options[key] = action
  2300. else
  2301. options[:partial] = action
  2302. end
  2303. options
  2304. end
  2305. # Normalize options.
  2306. # :api: plugin
  2307. def _normalize_options(options)
  2308. if options[:partial] == true
  2309. options[:partial] = action_name
  2310. end
  2311. if (options.keys & [:partial, :file, :template]).empty?
  2312. options[:prefixes] ||= _prefixes
  2313. end
  2314. options[:template] ||= (options[:action] || action_name).to_s
  2315. options
  2316. end
  2317. # Process extra options.
  2318. # :api: plugin
  2319. def _process_options(options)
  2320. end
  2321. end
  2322. end
  2323. module AbstractController
  2324. module Translation
  2325. # Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>.
  2326. #
  2327. # When the given key starts with a period, it will be scoped by the current
  2328. # controller and action. So if you call <tt>translate(".foo")</tt> from
  2329. # <tt>PeopleController#index</tt>, it will convert the call to
  2330. # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
  2331. # to translate many keys within the same controller / action and gives you a
  2332. # simple framework for scoping them consistently.
  2333. def translate(*args)
  2334. key = args.first
  2335. if key.is_a?(String) && (key[0] == '.')
  2336. key = "#{ controller_path.gsub('/', '.') }.#{ action_name }#{ key }"
  2337. args[0] = key
  2338. end
  2339. I18n.translate(*args)
  2340. end
  2341. alias :t :translate
  2342. # Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>.
  2343. def localize(*args)
  2344. I18n.localize(*args)
  2345. end
  2346. alias :l :localize
  2347. end
  2348. end
  2349. module AbstractController
  2350. # Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
  2351. # has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
  2352. # exception will be raised.
  2353. #
  2354. # Note that this module is completely decoupled from HTTP - the only requirement is a valid
  2355. # <tt>_routes</tt> implementation.
  2356. module UrlFor
  2357. extend ActiveSupport::Concern
  2358. include ActionDispatch::Routing::UrlFor
  2359. def _routes
  2360. raise "In order to use #url_for, you must include routing helpers explicitly. " \
  2361. "For instance, `include Rails.application.routes.url_helpers"
  2362. end
  2363. module ClassMethods
  2364. def _routes
  2365. nil
  2366. end
  2367. def action_methods
  2368. @action_methods ||= begin
  2369. if _routes
  2370. super - _routes.named_routes.helper_names
  2371. else
  2372. super
  2373. end
  2374. end
  2375. end
  2376. end
  2377. end
  2378. end
  2379. require 'action_view/base'
  2380. module AbstractController
  2381. module ViewPaths
  2382. extend ActiveSupport::Concern
  2383. included do
  2384. class_attribute :_view_paths
  2385. self._view_paths = ActionView::PathSet.new
  2386. self._view_paths.freeze
  2387. end
  2388. delegate :template_exists?, :view_paths, :formats, :formats=,
  2389. :locale, :locale=, :to => :lookup_context
  2390. module ClassMethods
  2391. def parent_prefixes
  2392. @parent_prefixes ||= begin
  2393. parent_controller = superclass
  2394. prefixes = []
  2395. until parent_controller.abstract?
  2396. prefixes << parent_controller.controller_path
  2397. parent_controller = parent_controller.superclass
  2398. end
  2399. prefixes
  2400. end
  2401. end
  2402. end
  2403. # The prefixes used in render "foo" shortcuts.
  2404. def _prefixes
  2405. @_prefixes ||= begin
  2406. parent_prefixes = self.class.parent_prefixes
  2407. parent_prefixes.dup.unshift(controller_path)
  2408. end
  2409. end
  2410. # LookupContext is the object responsible to hold all information required to lookup
  2411. # templates, i.e. view paths and details. Check ActionView::LookupContext for more
  2412. # information.
  2413. def lookup_context
  2414. @_lookup_context ||=
  2415. ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
  2416. end
  2417. def details_for_lookup
  2418. { }
  2419. end
  2420. def append_view_path(path)
  2421. lookup_context.view_paths.push(*path)
  2422. end
  2423. def prepend_view_path(path)
  2424. lookup_context.view_paths.unshift(*path)
  2425. end
  2426. module ClassMethods
  2427. # Append a path to the list of view paths for this controller.
  2428. #
  2429. # ==== Parameters
  2430. # * <tt>path</tt> - If a String is provided, it gets converted into
  2431. # the default view path. You may also provide a custom view path
  2432. # (see ActionView::PathSet for more information)
  2433. def append_view_path(path)
  2434. self._view_paths = view_paths + Array(path)
  2435. end
  2436. # Prepend a path to the list of view paths for this controller.
  2437. #
  2438. # ==== Parameters
  2439. # * <tt>path</tt> - If a String is provided, it gets converted into
  2440. # the default view path. You may also provide a custom view path
  2441. # (see ActionView::PathSet for more information)
  2442. def prepend_view_path(path)
  2443. self._view_paths = ActionView::PathSet.new(Array(path) + view_paths)
  2444. end
  2445. # A list of all of the default view paths for this controller.
  2446. def view_paths
  2447. _view_paths
  2448. end
  2449. # Set the view paths.
  2450. #
  2451. # ==== Parameters
  2452. # * <tt>paths</tt> - If a PathSet is provided, use that;
  2453. # otherwise, process the parameter into a PathSet.
  2454. def view_paths=(paths)
  2455. self._view_paths = ActionView::PathSet.new(Array(paths))
  2456. end
  2457. end
  2458. end
  2459. end
  2460. require 'action_pack'
  2461. require 'active_support/rails'
  2462. require 'active_support/core_ext/module/attr_internal'
  2463. require 'active_support/core_ext/module/anonymous'
  2464. require 'active_support/i18n'
  2465. module AbstractController
  2466. extend ActiveSupport::Autoload
  2467. autoload :Base
  2468. autoload :Callbacks
  2469. autoload :Collector
  2470. autoload :Helpers
  2471. autoload :Layouts
  2472. autoload :Logger
  2473. autoload :Rendering
  2474. autoload :Translation
  2475. autoload :AssetPaths
  2476. autoload :ViewPaths
  2477. autoload :UrlFor
  2478. end
  2479. require "action_controller/log_subscriber"
  2480. require "action_controller/metal/params_wrapper"
  2481. module ActionController
  2482. # Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
  2483. # on request and then either render a template or redirect to another action. An action is defined as a public method
  2484. # on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
  2485. #
  2486. # By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
  2487. # controllers in turn inherit from ApplicationController. This gives you one class to configure things such as
  2488. # request forgery protection and filtering of sensitive request parameters.
  2489. #
  2490. # A sample controller could look like this:
  2491. #
  2492. # class PostsController < ApplicationController
  2493. # def index
  2494. # @posts = Post.all
  2495. # end
  2496. #
  2497. # def create
  2498. # @post = Post.create params[:post]
  2499. # redirect_to posts_path
  2500. # end
  2501. # end
  2502. #
  2503. # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
  2504. # after executing code in the action. For example, the +index+ action of the PostsController would render the
  2505. # template <tt>app/views/posts/index.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
  2506. #
  2507. # Unlike index, the create action will not render a template. After performing its main purpose (creating a
  2508. # new post), it initiates a redirect instead. This redirect works by returning an external
  2509. # "302 Moved" HTTP response that takes the user to the index action.
  2510. #
  2511. # These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
  2512. # Most actions are variations on these themes.
  2513. #
  2514. # == Requests
  2515. #
  2516. # For every request, the router determines the value of the +controller+ and +action+ keys. These determine which controller
  2517. # and action are called. The remaining request parameters, the session (if one is available), and the full request with
  2518. # all the HTTP headers are made available to the action through accessor methods. Then the action is performed.
  2519. #
  2520. # The full request object is available via the request accessor and is primarily used to query for HTTP headers:
  2521. #
  2522. # def server_ip
  2523. # location = request.env["SERVER_ADDR"]
  2524. # render text: "This server hosted at #{location}"
  2525. # end
  2526. #
  2527. # == Parameters
  2528. #
  2529. # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
  2530. # which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
  2531. # <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
  2532. #
  2533. # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
  2534. #
  2535. # <input type="text" name="post[name]" value="david">
  2536. # <input type="text" name="post[address]" value="hyacintvej">
  2537. #
  2538. # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
  2539. # If the address input had been named "post[address][street]", the params would have included
  2540. # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
  2541. #
  2542. # == Sessions
  2543. #
  2544. # Sessions allow you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
  2545. # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
  2546. # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
  2547. # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
  2548. #
  2549. # You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
  2550. #
  2551. # session[:person] = Person.authenticate(user_name, password)
  2552. #
  2553. # And retrieved again through the same hash:
  2554. #
  2555. # Hello #{session[:person]}
  2556. #
  2557. # For removing objects from the session, you can either assign a single key to +nil+:
  2558. #
  2559. # # removes :person from session
  2560. # session[:person] = nil
  2561. #
  2562. # or you can remove the entire session with +reset_session+.
  2563. #
  2564. # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
  2565. # This prevents the user from tampering with the session but also allows him to see its contents.
  2566. #
  2567. # Do not put secret information in cookie-based sessions!
  2568. #
  2569. # == Responses
  2570. #
  2571. # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
  2572. # object is generated automatically through the use of renders and redirects and requires no user intervention.
  2573. #
  2574. # == Renders
  2575. #
  2576. # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
  2577. # of a template. Included in the Action Pack is the Action View, which enables rendering of ERB templates. It's automatically configured.
  2578. # The controller passes objects to the view by assigning instance variables:
  2579. #
  2580. # def show
  2581. # @post = Post.find(params[:id])
  2582. # end
  2583. #
  2584. # Which are then automatically available to the view:
  2585. #
  2586. # Title: <%= @post.title %>
  2587. #
  2588. # You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
  2589. # will use the manual rendering methods:
  2590. #
  2591. # def search
  2592. # @results = Search.find(params[:query])
  2593. # case @results.count
  2594. # when 0 then render action: "no_results"
  2595. # when 1 then render action: "show"
  2596. # when 2..10 then render action: "show_many"
  2597. # end
  2598. # end
  2599. #
  2600. # Read more about writing ERB and Builder templates in ActionView::Base.
  2601. #
  2602. # == Redirects
  2603. #
  2604. # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the
  2605. # database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
  2606. # going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
  2607. #
  2608. # def create
  2609. # @entry = Entry.new(params[:entry])
  2610. # if @entry.save
  2611. # # The entry was saved correctly, redirect to show
  2612. # redirect_to action: 'show', id: @entry.id
  2613. # else
  2614. # # things didn't go so well, do something else
  2615. # end
  2616. # end
  2617. #
  2618. # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method, which is then executed.
  2619. # Note that this is an external HTTP-level redirection which will cause the browser to make a second request (a GET to the show action),
  2620. # and not some internal re-routing which calls both "create" and then "show" within one request.
  2621. #
  2622. # Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
  2623. #
  2624. # == Calling multiple redirects or renders
  2625. #
  2626. # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
  2627. #
  2628. # def do_something
  2629. # redirect_to action: "elsewhere"
  2630. # render action: "overthere" # raises DoubleRenderError
  2631. # end
  2632. #
  2633. # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
  2634. #
  2635. # def do_something
  2636. # redirect_to(action: "elsewhere") and return if monkeys.nil?
  2637. # render action: "overthere" # won't be called if monkeys is nil
  2638. # end
  2639. #
  2640. class Base < Metal
  2641. abstract!
  2642. # We document the request and response methods here because albeit they are
  2643. # implemented in ActionController::Metal, the type of the returned objects
  2644. # is unknown at that level.
  2645. ##
  2646. # :method: request
  2647. #
  2648. # Returns an ActionDispatch::Request instance that represents the
  2649. # current request.
  2650. ##
  2651. # :method: response
  2652. #
  2653. # Returns an ActionDispatch::Response that represents the current
  2654. # response.
  2655. # Shortcut helper that returns all the modules included in
  2656. # ActionController::Base except the ones passed as arguments:
  2657. #
  2658. # class MetalController
  2659. # ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
  2660. # include left
  2661. # end
  2662. # end
  2663. #
  2664. # This gives better control over what you want to exclude and makes it
  2665. # easier to create a bare controller class, instead of listing the modules
  2666. # required manually.
  2667. def self.without_modules(*modules)
  2668. modules = modules.map do |m|
  2669. m.is_a?(Symbol) ? ActionController.const_get(m) : m
  2670. end
  2671. MODULES - modules
  2672. end
  2673. MODULES = [
  2674. AbstractController::Layouts,
  2675. AbstractController::Translation,
  2676. AbstractController::AssetPaths,
  2677. Helpers,
  2678. HideActions,
  2679. UrlFor,
  2680. Redirecting,
  2681. Rendering,
  2682. Renderers::All,
  2683. ConditionalGet,
  2684. RackDelegation,
  2685. Caching,
  2686. MimeResponds,
  2687. ImplicitRender,
  2688. StrongParameters,
  2689. Cookies,
  2690. Flash,
  2691. RequestForgeryProtection,
  2692. ForceSSL,
  2693. Streaming,
  2694. DataStreaming,
  2695. RecordIdentifier,
  2696. HttpAuthentication::Basic::ControllerMethods,
  2697. HttpAuthentication::Digest::ControllerMethods,
  2698. HttpAuthentication::Token::ControllerMethods,
  2699. # Before callbacks should also be executed the earliest as possible, so
  2700. # also include them at the bottom.
  2701. AbstractController::Callbacks,
  2702. # Append rescue at the bottom to wrap as much as possible.
  2703. Rescue,
  2704. # Add instrumentations hooks at the bottom, to ensure they instrument
  2705. # all the methods properly.
  2706. Instrumentation,
  2707. # Params wrapper should come before instrumentation so they are
  2708. # properly showed in logs
  2709. ParamsWrapper
  2710. ]
  2711. MODULES.each do |mod|
  2712. include mod
  2713. end
  2714. # Define some internal variables that should not be propagated to the view.
  2715. self.protected_instance_variables = [
  2716. :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
  2717. :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout
  2718. ]
  2719. ActiveSupport.run_load_hooks(:action_controller, self)
  2720. end
  2721. end
  2722. module ActionController
  2723. module Caching
  2724. # Fragment caching is used for caching various blocks within
  2725. # views without caching the entire action as a whole. This is
  2726. # useful when certain elements of an action change frequently or
  2727. # depend on complicated state while other parts rarely change or
  2728. # can be shared amongst multiple parties. The caching is done using
  2729. # the +cache+ helper available in the Action View. See
  2730. # ActionView::Helpers::CacheHelper for more information.
  2731. #
  2732. # While it's strongly recommended that you use key-based cache
  2733. # expiration (see links in CacheHelper for more information),
  2734. # it is also possible to manually expire caches. For example:
  2735. #
  2736. # expire_fragment('name_of_cache')
  2737. module Fragments
  2738. # Given a key (as described in +expire_fragment+), returns
  2739. # a key suitable for use in reading, writing, or expiring a
  2740. # cached fragment. All keys are prefixed with <tt>views/</tt> and uses
  2741. # ActiveSupport::Cache.expand_cache_key for the expansion.
  2742. def fragment_cache_key(key)
  2743. ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
  2744. end
  2745. # Writes +content+ to the location signified by
  2746. # +key+ (see +expire_fragment+ for acceptable formats).
  2747. def write_fragment(key, content, options = nil)
  2748. return content unless cache_configured?
  2749. key = fragment_cache_key(key)
  2750. instrument_fragment_cache :write_fragment, key do
  2751. content = content.to_str
  2752. cache_store.write(key, content, options)
  2753. end
  2754. content
  2755. end
  2756. # Reads a cached fragment from the location signified by +key+
  2757. # (see +expire_fragment+ for acceptable formats).
  2758. def read_fragment(key, options = nil)
  2759. return unless cache_configured?
  2760. key = fragment_cache_key(key)
  2761. instrument_fragment_cache :read_fragment, key do
  2762. result = cache_store.read(key, options)
  2763. result.respond_to?(:html_safe) ? result.html_safe : result
  2764. end
  2765. end
  2766. # Check if a cached fragment from the location signified by
  2767. # +key+ exists (see +expire_fragment+ for acceptable formats).
  2768. def fragment_exist?(key, options = nil)
  2769. return unless cache_configured?
  2770. key = fragment_cache_key(key)
  2771. instrument_fragment_cache :exist_fragment?, key do
  2772. cache_store.exist?(key, options)
  2773. end
  2774. end
  2775. # Removes fragments from the cache.
  2776. #
  2777. # +key+ can take one of three forms:
  2778. #
  2779. # * String - This would normally take the form of a path, like
  2780. # <tt>pages/45/notes</tt>.
  2781. # * Hash - Treated as an implicit call to +url_for+, like
  2782. # <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
  2783. # * Regexp - Will remove any fragment that matches, so
  2784. # <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
  2785. # don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
  2786. # the actual filename matched looks like
  2787. # <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
  2788. # only supported on caches that can iterate over all keys (unlike
  2789. # memcached).
  2790. #
  2791. # +options+ is passed through to the cache store's +delete+
  2792. # method (or <tt>delete_matched</tt>, for Regexp keys).
  2793. def expire_fragment(key, options = nil)
  2794. return unless cache_configured?
  2795. key = fragment_cache_key(key) unless key.is_a?(Regexp)
  2796. instrument_fragment_cache :expire_fragment, key do
  2797. if key.is_a?(Regexp)
  2798. cache_store.delete_matched(key, options)
  2799. else
  2800. cache_store.delete(key, options)
  2801. end
  2802. end
  2803. end
  2804. def instrument_fragment_cache(name, key) # :nodoc:
  2805. ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
  2806. end
  2807. end
  2808. end
  2809. end
  2810. require 'fileutils'
  2811. require 'uri'
  2812. require 'set'
  2813. module ActionController
  2814. # \Caching is a cheap way of speeding up slow applications by keeping the result of
  2815. # calculations, renderings, and database calls around for subsequent requests.
  2816. #
  2817. # You can read more about each approach by clicking the modules below.
  2818. #
  2819. # Note: To turn off all caching, set
  2820. # config.action_controller.perform_caching = false.
  2821. #
  2822. # == \Caching stores
  2823. #
  2824. # All the caching stores from ActiveSupport::Cache are available to be used as backends
  2825. # for Action Controller caching.
  2826. #
  2827. # Configuration examples (MemoryStore is the default):
  2828. #
  2829. # config.action_controller.cache_store = :memory_store
  2830. # config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
  2831. # config.action_controller.cache_store = :mem_cache_store, 'localhost'
  2832. # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
  2833. # config.action_controller.cache_store = MyOwnStore.new('parameter')
  2834. module Caching
  2835. extend ActiveSupport::Concern
  2836. extend ActiveSupport::Autoload
  2837. eager_autoload do
  2838. autoload :Fragments
  2839. end
  2840. module ConfigMethods
  2841. def cache_store
  2842. config.cache_store
  2843. end
  2844. def cache_store=(store)
  2845. config.cache_store = ActiveSupport::Cache.lookup_store(store)
  2846. end
  2847. private
  2848. def cache_configured?
  2849. perform_caching && cache_store
  2850. end
  2851. end
  2852. include RackDelegation
  2853. include AbstractController::Callbacks
  2854. include ConfigMethods
  2855. include Fragments
  2856. included do
  2857. extend ConfigMethods
  2858. config_accessor :default_static_extension
  2859. self.default_static_extension ||= '.html'
  2860. def self.page_cache_extension=(extension)
  2861. ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
  2862. self.default_static_extension = extension
  2863. end
  2864. def self.page_cache_extension
  2865. ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
  2866. default_static_extension
  2867. end
  2868. config_accessor :perform_caching
  2869. self.perform_caching = true if perform_caching.nil?
  2870. class_attribute :_view_cache_dependencies
  2871. self._view_cache_dependencies = []
  2872. helper_method :view_cache_dependencies if respond_to?(:helper_method)
  2873. end
  2874. module ClassMethods
  2875. def view_cache_dependency(&dependency)
  2876. self._view_cache_dependencies += [dependency]
  2877. end
  2878. end
  2879. def view_cache_dependencies
  2880. self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
  2881. end
  2882. protected
  2883. # Convenience accessor.
  2884. def cache(key, options = {}, &block)
  2885. if cache_configured?
  2886. cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
  2887. else
  2888. yield
  2889. end
  2890. end
  2891. end
  2892. end
  2893. ActionController::Integration = ActionDispatch::Integration
  2894. ActionController::IntegrationTest = ActionDispatch::IntegrationTest
  2895. ActiveSupport::Deprecation.warn 'ActionController::Integration is deprecated and will be removed, use ActionDispatch::Integration instead.'
  2896. ActiveSupport::Deprecation.warn 'ActionController::IntegrationTest is deprecated and will be removed, use ActionDispatch::IntegrationTest instead.'
  2897. ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
  2898. ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
  2899. ActionController::Routing = ActionDispatch::Routing
  2900. ActiveSupport::Deprecation.warn 'ActionController::AbstractRequest and ActionController::Request are deprecated and will be removed, use ActionDispatch::Request instead.'
  2901. ActiveSupport::Deprecation.warn 'ActionController::AbstractResponse and ActionController::Response are deprecated and will be removed, use ActionDispatch::Response instead.'
  2902. ActiveSupport::Deprecation.warn 'ActionController::Routing is deprecated and will be removed, use ActionDispatch::Routing instead.'
  2903. module ActionController
  2904. class LogSubscriber < ActiveSupport::LogSubscriber
  2905. INTERNAL_PARAMS = %w(controller action format _method only_path)
  2906. def start_processing(event)
  2907. return unless logger.info?
  2908. payload = event.payload
  2909. params = payload[:params].except(*INTERNAL_PARAMS)
  2910. format = payload[:format]
  2911. format = format.to_s.upcase if format.is_a?(Symbol)
  2912. info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
  2913. info " Parameters: #{params.inspect}" unless params.empty?
  2914. end
  2915. def process_action(event)
  2916. return unless logger.info?
  2917. payload = event.payload
  2918. additions = ActionController::Base.log_process_action(payload)
  2919. status = payload[:status]
  2920. if status.nil? && payload[:exception].present?
  2921. exception_class_name = payload[:exception].first
  2922. status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
  2923. end
  2924. message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
  2925. message << " (#{additions.join(" | ")})" unless additions.blank?
  2926. info(message)
  2927. end
  2928. def halted_callback(event)
  2929. info("Filter chain halted as #{event.payload[:filter]} rendered or redirected")
  2930. end
  2931. def send_file(event)
  2932. info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
  2933. end
  2934. def redirect_to(event)
  2935. info("Redirected to #{event.payload[:location]}")
  2936. end
  2937. def send_data(event)
  2938. info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
  2939. end
  2940. %w(write_fragment read_fragment exist_fragment?
  2941. expire_fragment expire_page write_page).each do |method|
  2942. class_eval <<-METHOD, __FILE__, __LINE__ + 1
  2943. def #{method}(event)
  2944. return unless logger.info?
  2945. key_or_path = event.payload[:key] || event.payload[:path]
  2946. human_name = #{method.to_s.humanize.inspect}
  2947. info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
  2948. end
  2949. METHOD
  2950. end
  2951. def logger
  2952. ActionController::Base.logger
  2953. end
  2954. end
  2955. end
  2956. ActionController::LogSubscriber.attach_to :action_controller
  2957. require 'active_support/core_ext/hash/keys'
  2958. module ActionController
  2959. module ConditionalGet
  2960. extend ActiveSupport::Concern
  2961. include RackDelegation
  2962. include Head
  2963. included do
  2964. class_attribute :etaggers
  2965. self.etaggers = []
  2966. end
  2967. module ClassMethods
  2968. # Allows you to consider additional controller-wide information when generating an etag.
  2969. # For example, if you serve pages tailored depending on who's logged in at the moment, you
  2970. # may want to add the current user id to be part of the etag to prevent authorized displaying
  2971. # of cached pages.
  2972. #
  2973. # class InvoicesController < ApplicationController
  2974. # etag { current_user.try :id }
  2975. #
  2976. # def show
  2977. # # Etag will differ even for the same invoice when it's viewed by a different current_user
  2978. # @invoice = Invoice.find(params[:id])
  2979. # fresh_when(@invoice)
  2980. # end
  2981. # end
  2982. def etag(&etagger)
  2983. self.etaggers += [etagger]
  2984. end
  2985. end
  2986. # Sets the etag, +last_modified+, or both on the response and renders a
  2987. # <tt>304 Not Modified</tt> response if the request is already fresh.
  2988. #
  2989. # === Parameters:
  2990. #
  2991. # * <tt>:etag</tt>.
  2992. # * <tt>:last_modified</tt>.
  2993. # * <tt>:public</tt> By default the Cache-Control header is private, set this to
  2994. # +true+ if you want your application to be cachable by other devices (proxy caches).
  2995. #
  2996. # === Example:
  2997. #
  2998. # def show
  2999. # @article = Article.find(params[:id])
  3000. # fresh_when(etag: @article, last_modified: @article.created_at, public: true)
  3001. # end
  3002. #
  3003. # This will render the show template if the request isn't sending a matching etag or
  3004. # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
  3005. #
  3006. # You can also just pass a record where +last_modified+ will be set by calling
  3007. # +updated_at+ and the etag by passing the object itself.
  3008. #
  3009. # def show
  3010. # @article = Article.find(params[:id])
  3011. # fresh_when(@article)
  3012. # end
  3013. #
  3014. # When passing a record, you can still set whether the public header:
  3015. #
  3016. # def show
  3017. # @article = Article.find(params[:id])
  3018. # fresh_when(@article, public: true)
  3019. # end
  3020. def fresh_when(record_or_options, additional_options = {})
  3021. if record_or_options.is_a? Hash
  3022. options = record_or_options
  3023. options.assert_valid_keys(:etag, :last_modified, :public)
  3024. else
  3025. record = record_or_options
  3026. options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
  3027. end
  3028. response.etag = combine_etags(options[:etag]) if options[:etag]
  3029. response.last_modified = options[:last_modified] if options[:last_modified]
  3030. response.cache_control[:public] = true if options[:public]
  3031. head :not_modified if request.fresh?(response)
  3032. end
  3033. # Sets the +etag+ and/or +last_modified+ on the response and checks it against
  3034. # the client request. If the request doesn't match the options provided, the
  3035. # request is considered stale and should be generated from scratch. Otherwise,
  3036. # it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
  3037. #
  3038. # === Parameters:
  3039. #
  3040. # * <tt>:etag</tt>.
  3041. # * <tt>:last_modified</tt>.
  3042. # * <tt>:public</tt> By default the Cache-Control header is private, set this to
  3043. # +true+ if you want your application to be cachable by other devices (proxy caches).
  3044. #
  3045. # === Example:
  3046. #
  3047. # def show
  3048. # @article = Article.find(params[:id])
  3049. #
  3050. # if stale?(etag: @article, last_modified: @article.created_at)
  3051. # @statistics = @article.really_expensive_call
  3052. # respond_to do |format|
  3053. # # all the supported formats
  3054. # end
  3055. # end
  3056. # end
  3057. #
  3058. # You can also just pass a record where +last_modified+ will be set by calling
  3059. # updated_at and the etag by passing the object itself.
  3060. #
  3061. # def show
  3062. # @article = Article.find(params[:id])
  3063. #
  3064. # if stale?(@article)
  3065. # @statistics = @article.really_expensive_call
  3066. # respond_to do |format|
  3067. # # all the supported formats
  3068. # end
  3069. # end
  3070. # end
  3071. #
  3072. # When passing a record, you can still set whether the public header:
  3073. #
  3074. # def show
  3075. # @article = Article.find(params[:id])
  3076. #
  3077. # if stale?(@article, public: true)
  3078. # @statistics = @article.really_expensive_call
  3079. # respond_to do |format|
  3080. # # all the supported formats
  3081. # end
  3082. # end
  3083. # end
  3084. def stale?(record_or_options, additional_options = {})
  3085. fresh_when(record_or_options, additional_options)
  3086. !request.fresh?(response)
  3087. end
  3088. # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
  3089. # instruction, so that intermediate caches must not cache the response.
  3090. #
  3091. # expires_in 20.minutes
  3092. # expires_in 3.hours, public: true
  3093. # expires_in 3.hours, public: true, must_revalidate: true
  3094. #
  3095. # This method will overwrite an existing Cache-Control header.
  3096. # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
  3097. #
  3098. # The method will also ensure a HTTP Date header for client compatibility.
  3099. def expires_in(seconds, options = {})
  3100. response.cache_control.merge!(
  3101. :max_age => seconds,
  3102. :public => options.delete(:public),
  3103. :must_revalidate => options.delete(:must_revalidate)
  3104. )
  3105. options.delete(:private)
  3106. response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
  3107. response.date = Time.now unless response.date?
  3108. end
  3109. # Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
  3110. # occur by the browser or intermediate caches (like caching proxy servers).
  3111. def expires_now
  3112. response.cache_control.replace(:no_cache => true)
  3113. end
  3114. private
  3115. def combine_etags(etag)
  3116. [ etag, *etaggers.map { |etagger| instance_exec(&etagger) }.compact ]
  3117. end
  3118. end
  3119. end
  3120. module ActionController #:nodoc:
  3121. module Cookies
  3122. extend ActiveSupport::Concern
  3123. include RackDelegation
  3124. included do
  3125. helper_method :cookies
  3126. end
  3127. private
  3128. def cookies
  3129. request.cookie_jar
  3130. end
  3131. end
  3132. end
  3133. require 'action_controller/metal/exceptions'
  3134. module ActionController #:nodoc:
  3135. # Methods for sending arbitrary data and for streaming files to the browser,
  3136. # instead of rendering.
  3137. module DataStreaming
  3138. extend ActiveSupport::Concern
  3139. include ActionController::Rendering
  3140. DEFAULT_SEND_FILE_TYPE = 'application/octet-stream'.freeze #:nodoc:
  3141. DEFAULT_SEND_FILE_DISPOSITION = 'attachment'.freeze #:nodoc:
  3142. protected
  3143. # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
  3144. # via the Rack::Sendfile middleware. The header to use is set via
  3145. # +config.action_dispatch.x_sendfile_header+.
  3146. # Your server can also configure this for you by setting the X-Sendfile-Type header.
  3147. #
  3148. # Be careful to sanitize the path parameter if it is coming from a web
  3149. # page. <tt>send_file(params[:path])</tt> allows a malicious user to
  3150. # download any file on your server.
  3151. #
  3152. # Options:
  3153. # * <tt>:filename</tt> - suggests a filename for the browser to use.
  3154. # Defaults to <tt>File.basename(path)</tt>.
  3155. # * <tt>:type</tt> - specifies an HTTP content type.
  3156. # You can specify either a string or a symbol for a registered type register with
  3157. # <tt>Mime::Type.register</tt>, for example :json
  3158. # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
  3159. # If no content type is registered for the extension, default type 'application/octet-stream' will be used.
  3160. # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
  3161. # Valid values are 'inline' and 'attachment' (default).
  3162. # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
  3163. # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
  3164. # the URL, which is necessary for i18n filenames on certain browsers
  3165. # (setting <tt>:filename</tt> overrides this option).
  3166. #
  3167. # The default Content-Type and Content-Disposition headers are
  3168. # set to download arbitrary binary files in as many browsers as
  3169. # possible. IE versions 4, 5, 5.5, and 6 are all known to have
  3170. # a variety of quirks (especially when downloading over SSL).
  3171. #
  3172. # Simple download:
  3173. #
  3174. # send_file '/path/to.zip'
  3175. #
  3176. # Show a JPEG in the browser:
  3177. #
  3178. # send_file '/path/to.jpeg', type: 'image/jpeg', disposition: 'inline'
  3179. #
  3180. # Show a 404 page in the browser:
  3181. #
  3182. # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
  3183. #
  3184. # Read about the other Content-* HTTP headers if you'd like to
  3185. # provide the user with more information (such as Content-Description) in
  3186. # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
  3187. #
  3188. # Also be aware that the document may be cached by proxies and browsers.
  3189. # The Pragma and Cache-Control headers declare how the file may be cached
  3190. # by intermediaries. They default to require clients to validate with
  3191. # the server before releasing cached responses. See
  3192. # http://www.mnot.net/cache_docs/ for an overview of web caching and
  3193. # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
  3194. # for the Cache-Control header spec.
  3195. def send_file(path, options = {}) #:doc:
  3196. raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
  3197. options[:filename] ||= File.basename(path) unless options[:url_based_filename]
  3198. send_file_headers! options
  3199. self.status = options[:status] || 200
  3200. self.content_type = options[:content_type] if options.key?(:content_type)
  3201. self.response_body = FileBody.new(path)
  3202. end
  3203. # Avoid having to pass an open file handle as the response body.
  3204. # Rack::Sendfile will usually intercept the response and uses
  3205. # the path directly, so there is no reason to open the file.
  3206. class FileBody #:nodoc:
  3207. attr_reader :to_path
  3208. def initialize(path)
  3209. @to_path = path
  3210. end
  3211. # Stream the file's contents if Rack::Sendfile isn't present.
  3212. def each
  3213. File.open(to_path, 'rb') do |file|
  3214. while chunk = file.read(16384)
  3215. yield chunk
  3216. end
  3217. end
  3218. end
  3219. end
  3220. # Sends the given binary data to the browser. This method is similar to
  3221. # <tt>render text: data</tt>, but also allows you to specify whether
  3222. # the browser should display the response as a file attachment (i.e. in a
  3223. # download dialog) or as inline data. You may also set the content type,
  3224. # the apparent file name, and other things.
  3225. #
  3226. # Options:
  3227. # * <tt>:filename</tt> - suggests a filename for the browser to use.
  3228. # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
  3229. # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
  3230. # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
  3231. # If no content type is registered for the extension, default type 'application/octet-stream' will be used.
  3232. # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
  3233. # Valid values are 'inline' and 'attachment' (default).
  3234. # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
  3235. #
  3236. # Generic data download:
  3237. #
  3238. # send_data buffer
  3239. #
  3240. # Download a dynamically-generated tarball:
  3241. #
  3242. # send_data generate_tgz('dir'), filename: 'dir.tgz'
  3243. #
  3244. # Display an image Active Record in the browser:
  3245. #
  3246. # send_data image.data, type: image.content_type, disposition: 'inline'
  3247. #
  3248. # See +send_file+ for more information on HTTP Content-* headers and caching.
  3249. def send_data(data, options = {}) #:doc:
  3250. send_file_headers! options
  3251. render options.slice(:status, :content_type).merge(:text => data)
  3252. end
  3253. private
  3254. def send_file_headers!(options)
  3255. type_provided = options.has_key?(:type)
  3256. content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
  3257. raise ArgumentError, ":type option required" if content_type.nil?
  3258. if content_type.is_a?(Symbol)
  3259. extension = Mime[content_type]
  3260. raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
  3261. self.content_type = extension
  3262. else
  3263. if !type_provided && options[:filename]
  3264. # If type wasn't provided, try guessing from file extension.
  3265. content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete('.')) || content_type
  3266. end
  3267. self.content_type = content_type
  3268. end
  3269. disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
  3270. unless disposition.nil?
  3271. disposition = disposition.to_s
  3272. disposition += %(; filename="#{options[:filename]}") if options[:filename]
  3273. headers['Content-Disposition'] = disposition
  3274. end
  3275. headers['Content-Transfer-Encoding'] = 'binary'
  3276. response.sending_file = true
  3277. # Fix a problem with IE 6.0 on opening downloaded files:
  3278. # If Cache-Control: no-cache is set (which Rails does by default),
  3279. # IE removes the file it just downloaded from its cache immediately
  3280. # after it displays the "open/save" dialog, which means that if you
  3281. # hit "open" the file isn't there anymore when the application that
  3282. # is called for handling the download is run, so let's workaround that
  3283. response.cache_control[:public] ||= false
  3284. end
  3285. end
  3286. end
  3287. module ActionController
  3288. class ActionControllerError < StandardError #:nodoc:
  3289. end
  3290. class BadRequest < ActionControllerError #:nodoc:
  3291. attr_reader :original_exception
  3292. def initialize(type = nil, e = nil)
  3293. return super() unless type && e
  3294. super("Invalid #{type} parameters: #{e.message}")
  3295. @original_exception = e
  3296. set_backtrace e.backtrace
  3297. end
  3298. end
  3299. class RenderError < ActionControllerError #:nodoc:
  3300. end
  3301. class RoutingError < ActionControllerError #:nodoc:
  3302. attr_reader :failures
  3303. def initialize(message, failures=[])
  3304. super(message)
  3305. @failures = failures
  3306. end
  3307. end
  3308. class ActionController::UrlGenerationError < RoutingError #:nodoc:
  3309. end
  3310. class MethodNotAllowed < ActionControllerError #:nodoc:
  3311. def initialize(*allowed_methods)
  3312. super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
  3313. end
  3314. end
  3315. class NotImplemented < MethodNotAllowed #:nodoc:
  3316. end
  3317. class UnknownController < ActionControllerError #:nodoc:
  3318. end
  3319. class MissingFile < ActionControllerError #:nodoc:
  3320. end
  3321. class SessionOverflowError < ActionControllerError #:nodoc:
  3322. DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
  3323. def initialize(message = nil)
  3324. super(message || DEFAULT_MESSAGE)
  3325. end
  3326. end
  3327. class UnknownHttpMethod < ActionControllerError #:nodoc:
  3328. end
  3329. class UnknownFormat < ActionControllerError #:nodoc:
  3330. end
  3331. end
  3332. module ActionController #:nodoc:
  3333. module Flash
  3334. extend ActiveSupport::Concern
  3335. included do
  3336. class_attribute :_flash_types, instance_accessor: false
  3337. self._flash_types = []
  3338. delegate :flash, to: :request
  3339. add_flash_types(:alert, :notice)
  3340. end
  3341. module ClassMethods
  3342. def add_flash_types(*types)
  3343. types.each do |type|
  3344. next if _flash_types.include?(type)
  3345. define_method(type) do
  3346. request.flash[type]
  3347. end
  3348. helper_method type
  3349. _flash_types << type
  3350. end
  3351. end
  3352. end
  3353. protected
  3354. def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
  3355. self.class._flash_types.each do |flash_type|
  3356. if type = response_status_and_flash.delete(flash_type)
  3357. flash[flash_type] = type
  3358. end
  3359. end
  3360. if other_flashes = response_status_and_flash.delete(:flash)
  3361. flash.update(other_flashes)
  3362. end
  3363. super(options, response_status_and_flash)
  3364. end
  3365. end
  3366. end
  3367. module ActionController
  3368. # This module provides a method which will redirect browser to use HTTPS
  3369. # protocol. This will ensure that user's sensitive information will be
  3370. # transferred safely over the internet. You _should_ always force browser
  3371. # to use HTTPS when you're transferring sensitive information such as
  3372. # user authentication, account information, or credit card information.
  3373. #
  3374. # Note that if you are really concerned about your application security,
  3375. # you might consider using +config.force_ssl+ in your config file instead.
  3376. # That will ensure all the data transferred via HTTPS protocol and prevent
  3377. # user from getting session hijacked when accessing the site under unsecured
  3378. # HTTP protocol.
  3379. module ForceSSL
  3380. extend ActiveSupport::Concern
  3381. include AbstractController::Callbacks
  3382. module ClassMethods
  3383. # Force the request to this particular controller or specified actions to be
  3384. # under HTTPS protocol.
  3385. #
  3386. # If you need to disable this for any reason (e.g. development) then you can use
  3387. # an +:if+ or +:unless+ condition.
  3388. #
  3389. # class AccountsController < ApplicationController
  3390. # force_ssl if: :ssl_configured?
  3391. #
  3392. # def ssl_configured?
  3393. # !Rails.env.development?
  3394. # end
  3395. # end
  3396. #
  3397. # ==== Options
  3398. # * <tt>host</tt> - Redirect to a different host name
  3399. # * <tt>only</tt> - The callback should be run only for this action
  3400. # * <tt>except</tt> - The callback should be run for all actions except this action
  3401. # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
  3402. # will be called only when it returns a true value.
  3403. # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
  3404. # will be called only when it returns a false value.
  3405. def force_ssl(options = {})
  3406. host = options.delete(:host)
  3407. before_action(options) do
  3408. force_ssl_redirect(host)
  3409. end
  3410. end
  3411. end
  3412. # Redirect the existing request to use the HTTPS protocol.
  3413. #
  3414. # ==== Parameters
  3415. # * <tt>host</tt> - Redirect to a different host name
  3416. def force_ssl_redirect(host = nil)
  3417. unless request.ssl?
  3418. redirect_options = {:protocol => 'https://', :status => :moved_permanently}
  3419. redirect_options.merge!(:host => host) if host
  3420. redirect_options.merge!(:params => request.query_parameters)
  3421. flash.keep if respond_to?(:flash)
  3422. redirect_to redirect_options
  3423. end
  3424. end
  3425. end
  3426. end
  3427. module ActionController
  3428. module Head
  3429. extend ActiveSupport::Concern
  3430. # Return a response that has no content (merely headers). The options
  3431. # argument is interpreted to be a hash of header names and values.
  3432. # This allows you to easily return a response that consists only of
  3433. # significant headers:
  3434. #
  3435. # head :created, location: person_path(@person)
  3436. #
  3437. # head :created, location: @person
  3438. #
  3439. # It can also be used to return exceptional conditions:
  3440. #
  3441. # return head(:method_not_allowed) unless request.post?
  3442. # return head(:bad_request) unless valid_request?
  3443. # render
  3444. def head(status, options = {})
  3445. options, status = status, nil if status.is_a?(Hash)
  3446. status ||= options.delete(:status) || :ok
  3447. location = options.delete(:location)
  3448. content_type = options.delete(:content_type)
  3449. options.each do |key, value|
  3450. headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
  3451. end
  3452. self.status = status
  3453. self.location = url_for(location) if location
  3454. if include_content?(self.status)
  3455. self.content_type = content_type || (Mime[formats.first] if formats)
  3456. self.response.charset = false if self.response
  3457. self.response_body = " "
  3458. else
  3459. headers.delete('Content-Type')
  3460. headers.delete('Content-Length')
  3461. self.response_body = ""
  3462. end
  3463. end
  3464. private
  3465. # :nodoc:
  3466. def include_content?(status)
  3467. case status
  3468. when 100..199
  3469. false
  3470. when 204, 205, 304
  3471. false
  3472. else
  3473. true
  3474. end
  3475. end
  3476. end
  3477. end
  3478. module ActionController
  3479. # The \Rails framework provides a large number of helpers for working with assets, dates, forms,
  3480. # numbers and model objects, to name a few. These helpers are available to all templates
  3481. # by default.
  3482. #
  3483. # In addition to using the standard template helpers provided, creating custom helpers to
  3484. # extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
  3485. # will include all helpers.
  3486. #
  3487. # In previous versions of \Rails the controller will include a helper whose
  3488. # name matches that of the controller, e.g., <tt>MyController</tt> will automatically
  3489. # include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
  3490. #
  3491. # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
  3492. # controller which inherits from it.
  3493. #
  3494. # The +to_s+ method from the \Time class can be wrapped in a helper method to display a custom message if
  3495. # a \Time object is blank:
  3496. #
  3497. # module FormattedTimeHelper
  3498. # def format_time(time, format=:long, blank_message="&nbsp;")
  3499. # time.blank? ? blank_message : time.to_s(format)
  3500. # end
  3501. # end
  3502. #
  3503. # FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
  3504. #
  3505. # class EventsController < ActionController::Base
  3506. # helper FormattedTimeHelper
  3507. # def index
  3508. # @events = Event.all
  3509. # end
  3510. # end
  3511. #
  3512. # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
  3513. #
  3514. # <% @events.each do |event| -%>
  3515. # <p>
  3516. # <%= format_time(event.time, :short, "N/A") %> | <%= event.name %>
  3517. # </p>
  3518. # <% end -%>
  3519. #
  3520. # Finally, assuming we have two event instances, one which has a time and one which does not,
  3521. # the output might look like this:
  3522. #
  3523. # 23 Aug 11:30 | Carolina Railhawks Soccer Match
  3524. # N/A | Carolina Railhaws Training Workshop
  3525. #
  3526. module Helpers
  3527. extend ActiveSupport::Concern
  3528. class << self; attr_accessor :helpers_path; end
  3529. include AbstractController::Helpers
  3530. included do
  3531. class_attribute :helpers_path, :include_all_helpers
  3532. self.helpers_path ||= []
  3533. self.include_all_helpers = true
  3534. end
  3535. module ClassMethods
  3536. # Declares helper accessors for controller attributes. For example, the
  3537. # following adds new +name+ and <tt>name=</tt> instance methods to a
  3538. # controller and makes them available to the view:
  3539. # attr_accessor :name
  3540. # helper_attr :name
  3541. #
  3542. # ==== Parameters
  3543. # * <tt>attrs</tt> - Names of attributes to be converted into helpers.
  3544. def helper_attr(*attrs)
  3545. attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
  3546. end
  3547. # Provides a proxy to access helpers methods from outside the view.
  3548. def helpers
  3549. @helper_proxy ||= ActionView::Base.new.extend(_helpers)
  3550. end
  3551. # Overwrite modules_for_helpers to accept :all as argument, which loads
  3552. # all helpers in helpers_path.
  3553. #
  3554. # ==== Parameters
  3555. # * <tt>args</tt> - A list of helpers
  3556. #
  3557. # ==== Returns
  3558. # * <tt>array</tt> - A normalized list of modules for the list of helpers provided.
  3559. def modules_for_helpers(args)
  3560. args += all_application_helpers if args.delete(:all)
  3561. super(args)
  3562. end
  3563. def all_helpers_from_path(path)
  3564. helpers = Array(path).flat_map do |_path|
  3565. extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
  3566. names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
  3567. names.sort!
  3568. names
  3569. end
  3570. helpers.uniq!
  3571. helpers
  3572. end
  3573. private
  3574. # Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
  3575. def all_application_helpers
  3576. all_helpers_from_path(helpers_path)
  3577. end
  3578. end
  3579. end
  3580. end
  3581. module ActionController
  3582. # Adds the ability to prevent public methods on a controller to be called as actions.
  3583. module HideActions
  3584. extend ActiveSupport::Concern
  3585. included do
  3586. class_attribute :hidden_actions
  3587. self.hidden_actions = Set.new.freeze
  3588. end
  3589. private
  3590. # Overrides AbstractController::Base#action_method? to return false if the
  3591. # action name is in the list of hidden actions.
  3592. def method_for_action(action_name)
  3593. self.class.visible_action?(action_name) && super
  3594. end
  3595. module ClassMethods
  3596. # Sets all of the actions passed in as hidden actions.
  3597. #
  3598. # ==== Parameters
  3599. # * <tt>args</tt> - A list of actions
  3600. def hide_action(*args)
  3601. self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
  3602. end
  3603. def visible_action?(action_name)
  3604. action_methods.include?(action_name)
  3605. end
  3606. # Overrides AbstractController::Base#action_methods to remove any methods
  3607. # that are listed as hidden methods.
  3608. def action_methods
  3609. @action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) }).freeze
  3610. end
  3611. end
  3612. end
  3613. end
  3614. require 'base64'
  3615. module ActionController
  3616. # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
  3617. module HttpAuthentication
  3618. # Makes it dead easy to do HTTP \Basic authentication.
  3619. #
  3620. # === Simple \Basic example
  3621. #
  3622. # class PostsController < ApplicationController
  3623. # http_basic_authenticate_with name: "dhh", password: "secret", except: :index
  3624. #
  3625. # def index
  3626. # render text: "Everyone can see me!"
  3627. # end
  3628. #
  3629. # def edit
  3630. # render text: "I'm only accessible if you know the password"
  3631. # end
  3632. # end
  3633. #
  3634. # === Advanced \Basic example
  3635. #
  3636. # Here is a more advanced \Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
  3637. # the regular HTML interface is protected by a session approach:
  3638. #
  3639. # class ApplicationController < ActionController::Base
  3640. # before_action :set_account, :authenticate
  3641. #
  3642. # protected
  3643. # def set_account
  3644. # @account = Account.find_by_url_name(request.subdomains.first)
  3645. # end
  3646. #
  3647. # def authenticate
  3648. # case request.format
  3649. # when Mime::XML, Mime::ATOM
  3650. # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
  3651. # @current_user = user
  3652. # else
  3653. # request_http_basic_authentication
  3654. # end
  3655. # else
  3656. # if session_authenticated?
  3657. # @current_user = @account.users.find(session[:authenticated][:user_id])
  3658. # else
  3659. # redirect_to(login_url) and return false
  3660. # end
  3661. # end
  3662. # end
  3663. # end
  3664. #
  3665. # In your integration tests, you can do something like this:
  3666. #
  3667. # def test_access_granted_from_xml
  3668. # get(
  3669. # "/notes/1.xml", nil,
  3670. # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
  3671. # )
  3672. #
  3673. # assert_equal 200, status
  3674. # end
  3675. module Basic
  3676. extend self
  3677. module ControllerMethods
  3678. extend ActiveSupport::Concern
  3679. module ClassMethods
  3680. def http_basic_authenticate_with(options = {})
  3681. before_action(options.except(:name, :password, :realm)) do
  3682. authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
  3683. name == options[:name] && password == options[:password]
  3684. end
  3685. end
  3686. end
  3687. end
  3688. def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
  3689. authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
  3690. end
  3691. def authenticate_with_http_basic(&login_procedure)
  3692. HttpAuthentication::Basic.authenticate(request, &login_procedure)
  3693. end
  3694. def request_http_basic_authentication(realm = "Application")
  3695. HttpAuthentication::Basic.authentication_request(self, realm)
  3696. end
  3697. end
  3698. def authenticate(request, &login_procedure)
  3699. unless request.authorization.blank?
  3700. login_procedure.call(*user_name_and_password(request))
  3701. end
  3702. end
  3703. def user_name_and_password(request)
  3704. decode_credentials(request).split(/:/, 2)
  3705. end
  3706. def decode_credentials(request)
  3707. ::Base64.decode64(request.authorization.split(' ', 2).last || '')
  3708. end
  3709. def encode_credentials(user_name, password)
  3710. "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
  3711. end
  3712. def authentication_request(controller, realm)
  3713. controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
  3714. controller.response_body = "HTTP Basic: Access denied.\n"
  3715. controller.status = 401
  3716. end
  3717. end
  3718. # Makes it dead easy to do HTTP \Digest authentication.
  3719. #
  3720. # === Simple \Digest example
  3721. #
  3722. # require 'digest/md5'
  3723. # class PostsController < ApplicationController
  3724. # REALM = "SuperSecret"
  3725. # USERS = {"dhh" => "secret", #plain text password
  3726. # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
  3727. #
  3728. # before_action :authenticate, except: [:index]
  3729. #
  3730. # def index
  3731. # render text: "Everyone can see me!"
  3732. # end
  3733. #
  3734. # def edit
  3735. # render text: "I'm only accessible if you know the password"
  3736. # end
  3737. #
  3738. # private
  3739. # def authenticate
  3740. # authenticate_or_request_with_http_digest(REALM) do |username|
  3741. # USERS[username]
  3742. # end
  3743. # end
  3744. # end
  3745. #
  3746. # === Notes
  3747. #
  3748. # The +authenticate_or_request_with_http_digest+ block must return the user's password
  3749. # or the ha1 digest hash so the framework can appropriately hash to check the user's
  3750. # credentials. Returning +nil+ will cause authentication to fail.
  3751. #
  3752. # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
  3753. # the password file or database is compromised, the attacker would be able to use the ha1 hash to
  3754. # authenticate as the user at this +realm+, but would not have the user's password to try using at
  3755. # other sites.
  3756. #
  3757. # In rare instances, web servers or front proxies strip authorization headers before
  3758. # they reach your application. You can debug this situation by logging all environment
  3759. # variables, and check for HTTP_AUTHORIZATION, amongst others.
  3760. module Digest
  3761. extend self
  3762. module ControllerMethods
  3763. def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
  3764. authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm)
  3765. end
  3766. # Authenticate with HTTP Digest, returns true or false
  3767. def authenticate_with_http_digest(realm = "Application", &password_procedure)
  3768. HttpAuthentication::Digest.authenticate(request, realm, &password_procedure)
  3769. end
  3770. # Render output including the HTTP Digest authentication header
  3771. def request_http_digest_authentication(realm = "Application", message = nil)
  3772. HttpAuthentication::Digest.authentication_request(self, realm, message)
  3773. end
  3774. end
  3775. # Returns false on a valid response, true otherwise
  3776. def authenticate(request, realm, &password_procedure)
  3777. request.authorization && validate_digest_response(request, realm, &password_procedure)
  3778. end
  3779. # Returns false unless the request credentials response value matches the expected value.
  3780. # First try the password as a ha1 digest password. If this fails, then try it as a plain
  3781. # text password.
  3782. def validate_digest_response(request, realm, &password_procedure)
  3783. secret_key = secret_token(request)
  3784. credentials = decode_credentials_header(request)
  3785. valid_nonce = validate_nonce(secret_key, request, credentials[:nonce])
  3786. if valid_nonce && realm == credentials[:realm] && opaque(secret_key) == credentials[:opaque]
  3787. password = password_procedure.call(credentials[:username])
  3788. return false unless password
  3789. method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
  3790. uri = credentials[:uri]
  3791. [true, false].any? do |trailing_question_mark|
  3792. [true, false].any? do |password_is_ha1|
  3793. _uri = trailing_question_mark ? uri + "?" : uri
  3794. expected = expected_response(method, _uri, credentials, password, password_is_ha1)
  3795. expected == credentials[:response]
  3796. end
  3797. end
  3798. end
  3799. end
  3800. # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
  3801. # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
  3802. # of a plain-text password.
  3803. def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
  3804. ha1 = password_is_ha1 ? password : ha1(credentials, password)
  3805. ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
  3806. ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
  3807. end
  3808. def ha1(credentials, password)
  3809. ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
  3810. end
  3811. def encode_credentials(http_method, credentials, password, password_is_ha1)
  3812. credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
  3813. "Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
  3814. end
  3815. def decode_credentials_header(request)
  3816. decode_credentials(request.authorization)
  3817. end
  3818. def decode_credentials(header)
  3819. ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, '').split(',').map do |pair|
  3820. key, value = pair.split('=', 2)
  3821. [key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
  3822. end]
  3823. end
  3824. def authentication_header(controller, realm)
  3825. secret_key = secret_token(controller.request)
  3826. nonce = self.nonce(secret_key)
  3827. opaque = opaque(secret_key)
  3828. controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
  3829. end
  3830. def authentication_request(controller, realm, message = nil)
  3831. message ||= "HTTP Digest: Access denied.\n"
  3832. authentication_header(controller, realm)
  3833. controller.response_body = message
  3834. controller.status = 401
  3835. end
  3836. def secret_token(request)
  3837. key_generator = request.env["action_dispatch.key_generator"]
  3838. http_auth_salt = request.env["action_dispatch.http_auth_salt"]
  3839. key_generator.generate_key(http_auth_salt)
  3840. end
  3841. # Uses an MD5 digest based on time to generate a value to be used only once.
  3842. #
  3843. # A server-specified data string which should be uniquely generated each time a 401 response is made.
  3844. # It is recommended that this string be base64 or hexadecimal data.
  3845. # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed.
  3846. #
  3847. # The contents of the nonce are implementation dependent.
  3848. # The quality of the implementation depends on a good choice.
  3849. # A nonce might, for example, be constructed as the base 64 encoding of
  3850. #
  3851. # time-stamp H(time-stamp ":" ETag ":" private-key)
  3852. #
  3853. # where time-stamp is a server-generated time or other non-repeating value,
  3854. # ETag is the value of the HTTP ETag header associated with the requested entity,
  3855. # and private-key is data known only to the server.
  3856. # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and
  3857. # reject the request if it did not match the nonce from that header or
  3858. # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity.
  3859. # The inclusion of the ETag prevents a replay request for an updated version of the resource.
  3860. # (Note: including the IP address of the client in the nonce would appear to offer the server the ability
  3861. # to limit the reuse of the nonce to the same client that originally got it.
  3862. # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm.
  3863. # Also, IP address spoofing is not that hard.)
  3864. #
  3865. # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
  3866. # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
  3867. # POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
  3868. # of this document.
  3869. #
  3870. # The nonce is opaque to the client. Composed of Time, and hash of Time with secret
  3871. # key from the Rails session secret generated upon creation of project. Ensures
  3872. # the time cannot be modified by client.
  3873. def nonce(secret_key, time = Time.now)
  3874. t = time.to_i
  3875. hashed = [t, secret_key]
  3876. digest = ::Digest::MD5.hexdigest(hashed.join(":"))
  3877. ::Base64.strict_encode64("#{t}:#{digest}")
  3878. end
  3879. # Might want a shorter timeout depending on whether the request
  3880. # is a PATCH, PUT, or POST, and if client is browser or web service.
  3881. # Can be much shorter if the Stale directive is implemented. This would
  3882. # allow a user to use new nonce without prompting user again for their
  3883. # username and password.
  3884. def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
  3885. t = ::Base64.decode64(value).split(":").first.to_i
  3886. nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
  3887. end
  3888. # Opaque based on random generation - but changing each request?
  3889. def opaque(secret_key)
  3890. ::Digest::MD5.hexdigest(secret_key)
  3891. end
  3892. end
  3893. # Makes it dead easy to do HTTP Token authentication.
  3894. #
  3895. # Simple Token example:
  3896. #
  3897. # class PostsController < ApplicationController
  3898. # TOKEN = "secret"
  3899. #
  3900. # before_action :authenticate, except: [ :index ]
  3901. #
  3902. # def index
  3903. # render text: "Everyone can see me!"
  3904. # end
  3905. #
  3906. # def edit
  3907. # render text: "I'm only accessible if you know the password"
  3908. # end
  3909. #
  3910. # private
  3911. # def authenticate
  3912. # authenticate_or_request_with_http_token do |token, options|
  3913. # token == TOKEN
  3914. # end
  3915. # end
  3916. # end
  3917. #
  3918. #
  3919. # Here is a more advanced Token example where only Atom feeds and the XML API is protected by HTTP token authentication,
  3920. # the regular HTML interface is protected by a session approach:
  3921. #
  3922. # class ApplicationController < ActionController::Base
  3923. # before_action :set_account, :authenticate
  3924. #
  3925. # protected
  3926. # def set_account
  3927. # @account = Account.find_by_url_name(request.subdomains.first)
  3928. # end
  3929. #
  3930. # def authenticate
  3931. # case request.format
  3932. # when Mime::XML, Mime::ATOM
  3933. # if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
  3934. # @current_user = user
  3935. # else
  3936. # request_http_token_authentication
  3937. # end
  3938. # else
  3939. # if session_authenticated?
  3940. # @current_user = @account.users.find(session[:authenticated][:user_id])
  3941. # else
  3942. # redirect_to(login_url) and return false
  3943. # end
  3944. # end
  3945. # end
  3946. # end
  3947. #
  3948. #
  3949. # In your integration tests, you can do something like this:
  3950. #
  3951. # def test_access_granted_from_xml
  3952. # get(
  3953. # "/notes/1.xml", nil,
  3954. # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
  3955. # )
  3956. #
  3957. # assert_equal 200, status
  3958. # end
  3959. #
  3960. #
  3961. # On shared hosts, Apache sometimes doesn't pass authentication headers to
  3962. # FCGI instances. If your environment matches this description and you cannot
  3963. # authenticate, try this rule in your Apache setup:
  3964. #
  3965. # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
  3966. module Token
  3967. TOKEN_REGEX = /^Token /
  3968. AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
  3969. extend self
  3970. module ControllerMethods
  3971. def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
  3972. authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
  3973. end
  3974. def authenticate_with_http_token(&login_procedure)
  3975. Token.authenticate(self, &login_procedure)
  3976. end
  3977. def request_http_token_authentication(realm = "Application")
  3978. Token.authentication_request(self, realm)
  3979. end
  3980. end
  3981. # If token Authorization header is present, call the login
  3982. # procedure with the present token and options.
  3983. #
  3984. # [controller]
  3985. # ActionController::Base instance for the current request.
  3986. #
  3987. # [login_procedure]
  3988. # Proc to call if a token is present. The Proc should take two arguments:
  3989. #
  3990. # authenticate(controller) { |token, options| ... }
  3991. #
  3992. # Returns the return value of <tt>login_procedure</tt> if a
  3993. # token is found. Returns <tt>nil</tt> if no token is found.
  3994. def authenticate(controller, &login_procedure)
  3995. token, options = token_and_options(controller.request)
  3996. unless token.blank?
  3997. login_procedure.call(token, options)
  3998. end
  3999. end
  4000. # Parses the token and options out of the token authorization header. If
  4001. # the header looks like this:
  4002. # Authorization: Token token="abc", nonce="def"
  4003. # Then the returned token is "abc", and the options is {nonce: "def"}
  4004. #
  4005. # request - ActionDispatch::Request instance with the current headers.
  4006. #
  4007. # Returns an Array of [String, Hash] if a token is present.
  4008. # Returns nil if no token is found.
  4009. def token_and_options(request)
  4010. authorization_request = request.authorization.to_s
  4011. if authorization_request[TOKEN_REGEX]
  4012. params = token_params_from authorization_request
  4013. [params.shift.last, Hash[params].with_indifferent_access]
  4014. end
  4015. end
  4016. def token_params_from(auth)
  4017. rewrite_param_values params_array_from raw_params auth
  4018. end
  4019. # Takes raw_params and turns it into an array of parameters
  4020. def params_array_from(raw_params)
  4021. raw_params.map { |param| param.split %r/=(.+)?/ }
  4022. end
  4023. # This removes the `"` characters wrapping the value.
  4024. def rewrite_param_values(array_params)
  4025. array_params.each { |param| param.last.gsub! %r/^"|"$/, '' }
  4026. end
  4027. # This method takes an authorization body and splits up the key-value
  4028. # pairs by the standardized `:`, `;`, or `\t` delimiters defined in
  4029. # `AUTHN_PAIR_DELIMITERS`.
  4030. def raw_params(auth)
  4031. auth.sub(TOKEN_REGEX, '').split(/"\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
  4032. end
  4033. # Encodes the given token and options into an Authorization header value.
  4034. #
  4035. # token - String token.
  4036. # options - optional Hash of the options.
  4037. #
  4038. # Returns String.
  4039. def encode_credentials(token, options = {})
  4040. values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
  4041. "#{key}=#{value.to_s.inspect}"
  4042. end
  4043. "Token #{values * ", "}"
  4044. end
  4045. # Sets a WWW-Authenticate to let the client know a token is desired.
  4046. #
  4047. # controller - ActionController::Base instance for the outgoing response.
  4048. # realm - String realm to use in the header.
  4049. #
  4050. # Returns nothing.
  4051. def authentication_request(controller, realm)
  4052. controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
  4053. controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
  4054. end
  4055. end
  4056. end
  4057. end
  4058. module ActionController
  4059. module ImplicitRender
  4060. def send_action(method, *args)
  4061. ret = super
  4062. default_render unless performed?
  4063. ret
  4064. end
  4065. def default_render(*args)
  4066. render(*args)
  4067. end
  4068. def method_for_action(action_name)
  4069. super || if template_exists?(action_name.to_s, _prefixes)
  4070. "default_render"
  4071. end
  4072. end
  4073. end
  4074. end
  4075. require 'benchmark'
  4076. require 'abstract_controller/logger'
  4077. module ActionController
  4078. # Adds instrumentation to several ends in ActionController::Base. It also provides
  4079. # some hooks related with process_action, this allows an ORM like Active Record
  4080. # and/or DataMapper to plug in ActionController and show related information.
  4081. #
  4082. # Check ActiveRecord::Railties::ControllerRuntime for an example.
  4083. module Instrumentation
  4084. extend ActiveSupport::Concern
  4085. include AbstractController::Logger
  4086. include ActionController::RackDelegation
  4087. attr_internal :view_runtime
  4088. def process_action(*args)
  4089. raw_payload = {
  4090. :controller => self.class.name,
  4091. :action => self.action_name,
  4092. :params => request.filtered_parameters,
  4093. :format => request.format.try(:ref),
  4094. :method => request.method,
  4095. :path => (request.fullpath rescue "unknown")
  4096. }
  4097. ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
  4098. ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
  4099. result = super
  4100. payload[:status] = response.status
  4101. append_info_to_payload(payload)
  4102. result
  4103. end
  4104. end
  4105. def render(*args)
  4106. render_output = nil
  4107. self.view_runtime = cleanup_view_runtime do
  4108. Benchmark.ms { render_output = super }
  4109. end
  4110. render_output
  4111. end
  4112. def send_file(path, options={})
  4113. ActiveSupport::Notifications.instrument("send_file.action_controller",
  4114. options.merge(:path => path)) do
  4115. super
  4116. end
  4117. end
  4118. def send_data(data, options = {})
  4119. ActiveSupport::Notifications.instrument("send_data.action_controller", options) do
  4120. super
  4121. end
  4122. end
  4123. def redirect_to(*args)
  4124. ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
  4125. result = super
  4126. payload[:status] = response.status
  4127. payload[:location] = response.filtered_location
  4128. result
  4129. end
  4130. end
  4131. private
  4132. # A hook invoked everytime a before callback is halted.
  4133. def halted_callback_hook(filter)
  4134. ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
  4135. end
  4136. # A hook which allows you to clean up any time taken into account in
  4137. # views wrongly, like database querying time.
  4138. #
  4139. # def cleanup_view_runtime
  4140. # super - time_taken_in_something_expensive
  4141. # end
  4142. #
  4143. # :api: plugin
  4144. def cleanup_view_runtime #:nodoc:
  4145. yield
  4146. end
  4147. # Every time after an action is processed, this method is invoked
  4148. # with the payload, so you can add more information.
  4149. # :api: plugin
  4150. def append_info_to_payload(payload) #:nodoc:
  4151. payload[:view_runtime] = view_runtime
  4152. end
  4153. module ClassMethods
  4154. # A hook which allows other frameworks to log what happened during
  4155. # controller process action. This method should return an array
  4156. # with the messages to be added.
  4157. # :api: plugin
  4158. def log_process_action(payload) #:nodoc:
  4159. messages, view_runtime = [], payload[:view_runtime]
  4160. messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
  4161. messages
  4162. end
  4163. end
  4164. end
  4165. end
  4166. require 'action_dispatch/http/response'
  4167. require 'delegate'
  4168. module ActionController
  4169. # Mix this module in to your controller, and all actions in that controller
  4170. # will be able to stream data to the client as it's written.
  4171. #
  4172. # class MyController < ActionController::Base
  4173. # include ActionController::Live
  4174. #
  4175. # def stream
  4176. # response.headers['Content-Type'] = 'text/event-stream'
  4177. # 100.times {
  4178. # response.stream.write "hello world\n"
  4179. # sleep 1
  4180. # }
  4181. # response.stream.close
  4182. # end
  4183. # end
  4184. #
  4185. # There are a few caveats with this use. You *cannot* write headers after the
  4186. # response has been committed (Response#committed? will return truthy).
  4187. # Calling +write+ or +close+ on the response stream will cause the response
  4188. # object to be committed. Make sure all headers are set before calling write
  4189. # or close on your stream.
  4190. #
  4191. # You *must* call close on your stream when you're finished, otherwise the
  4192. # socket may be left open forever.
  4193. #
  4194. # The final caveat is that your actions are executed in a separate thread than
  4195. # the main thread. Make sure your actions are thread safe, and this shouldn't
  4196. # be a problem (don't share state across threads, etc).
  4197. module Live
  4198. class Buffer < ActionDispatch::Response::Buffer #:nodoc:
  4199. def initialize(response)
  4200. super(response, SizedQueue.new(10))
  4201. end
  4202. def write(string)
  4203. unless @response.committed?
  4204. @response.headers["Cache-Control"] = "no-cache"
  4205. @response.headers.delete "Content-Length"
  4206. end
  4207. super
  4208. end
  4209. def each
  4210. while str = @buf.pop
  4211. yield str
  4212. end
  4213. end
  4214. def close
  4215. super
  4216. @buf.push nil
  4217. end
  4218. end
  4219. class Response < ActionDispatch::Response #:nodoc: all
  4220. class Header < DelegateClass(Hash)
  4221. def initialize(response, header)
  4222. @response = response
  4223. super(header)
  4224. end
  4225. def []=(k,v)
  4226. if @response.committed?
  4227. raise ActionDispatch::IllegalStateError, 'header already sent'
  4228. end
  4229. super
  4230. end
  4231. def merge(other)
  4232. self.class.new @response, __getobj__.merge(other)
  4233. end
  4234. def to_hash
  4235. __getobj__.dup
  4236. end
  4237. end
  4238. def commit!
  4239. headers.freeze
  4240. super
  4241. end
  4242. private
  4243. def build_buffer(response, body)
  4244. buf = Live::Buffer.new response
  4245. body.each { |part| buf.write part }
  4246. buf
  4247. end
  4248. def merge_default_headers(original, default)
  4249. Header.new self, super
  4250. end
  4251. end
  4252. def process(name)
  4253. t1 = Thread.current
  4254. locals = t1.keys.map { |key| [key, t1[key]] }
  4255. # This processes the action in a child thread. It lets us return the
  4256. # response code and headers back up the rack stack, and still process
  4257. # the body in parallel with sending data to the client
  4258. Thread.new {
  4259. t2 = Thread.current
  4260. t2.abort_on_exception = true
  4261. # Since we're processing the view in a different thread, copy the
  4262. # thread locals from the main thread to the child thread. :'(
  4263. locals.each { |k,v| t2[k] = v }
  4264. begin
  4265. super(name)
  4266. ensure
  4267. @_response.commit!
  4268. end
  4269. }
  4270. @_response.await_commit
  4271. end
  4272. def response_body=(body)
  4273. super
  4274. response.stream.close if response
  4275. end
  4276. def set_response!(request)
  4277. if request.env["HTTP_VERSION"] == "HTTP/1.0"
  4278. super
  4279. else
  4280. @_response = Live::Response.new
  4281. @_response.request = request
  4282. end
  4283. end
  4284. end
  4285. end
  4286. require 'active_support/core_ext/array/extract_options'
  4287. require 'abstract_controller/collector'
  4288. module ActionController #:nodoc:
  4289. module MimeResponds
  4290. extend ActiveSupport::Concern
  4291. included do
  4292. class_attribute :responder, :mimes_for_respond_to
  4293. self.responder = ActionController::Responder
  4294. clear_respond_to
  4295. end
  4296. module ClassMethods
  4297. # Defines mime types that are rendered by default when invoking
  4298. # <tt>respond_with</tt>.
  4299. #
  4300. # respond_to :html, :xml, :json
  4301. #
  4302. # Specifies that all actions in the controller respond to requests
  4303. # for <tt>:html</tt>, <tt>:xml</tt> and <tt>:json</tt>.
  4304. #
  4305. # To specify on per-action basis, use <tt>:only</tt> and
  4306. # <tt>:except</tt> with an array of actions or a single action:
  4307. #
  4308. # respond_to :html
  4309. # respond_to :xml, :json, except: [ :edit ]
  4310. #
  4311. # This specifies that all actions respond to <tt>:html</tt>
  4312. # and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
  4313. # <tt>:json</tt>.
  4314. #
  4315. # respond_to :json, only: :create
  4316. #
  4317. # This specifies that the <tt>:create</tt> action and no other responds
  4318. # to <tt>:json</tt>.
  4319. def respond_to(*mimes)
  4320. options = mimes.extract_options!
  4321. only_actions = Array(options.delete(:only)).map(&:to_s)
  4322. except_actions = Array(options.delete(:except)).map(&:to_s)
  4323. new = mimes_for_respond_to.dup
  4324. mimes.each do |mime|
  4325. mime = mime.to_sym
  4326. new[mime] = {}
  4327. new[mime][:only] = only_actions unless only_actions.empty?
  4328. new[mime][:except] = except_actions unless except_actions.empty?
  4329. end
  4330. self.mimes_for_respond_to = new.freeze
  4331. end
  4332. # Clear all mime types in <tt>respond_to</tt>.
  4333. #
  4334. def clear_respond_to
  4335. self.mimes_for_respond_to = Hash.new.freeze
  4336. end
  4337. end
  4338. # Without web-service support, an action which collects the data for displaying a list of people
  4339. # might look something like this:
  4340. #
  4341. # def index
  4342. # @people = Person.all
  4343. # end
  4344. #
  4345. # Here's the same action, with web-service support baked in:
  4346. #
  4347. # def index
  4348. # @people = Person.all
  4349. #
  4350. # respond_to do |format|
  4351. # format.html
  4352. # format.xml { render xml: @people }
  4353. # end
  4354. # end
  4355. #
  4356. # What that says is, "if the client wants HTML in response to this action, just respond as we
  4357. # would have before, but if the client wants XML, return them the list of people in XML format."
  4358. # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
  4359. #
  4360. # Supposing you have an action that adds a new person, optionally creating their company
  4361. # (by name) if it does not already exist, without web-services, it might look like this:
  4362. #
  4363. # def create
  4364. # @company = Company.find_or_create_by(name: params[:company][:name])
  4365. # @person = @company.people.create(params[:person])
  4366. #
  4367. # redirect_to(person_list_url)
  4368. # end
  4369. #
  4370. # Here's the same action, with web-service support baked in:
  4371. #
  4372. # def create
  4373. # company = params[:person].delete(:company)
  4374. # @company = Company.find_or_create_by(name: company[:name])
  4375. # @person = @company.people.create(params[:person])
  4376. #
  4377. # respond_to do |format|
  4378. # format.html { redirect_to(person_list_url) }
  4379. # format.js
  4380. # format.xml { render xml: @person.to_xml(include: @company) }
  4381. # end
  4382. # end
  4383. #
  4384. # If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
  4385. # then it is an Ajax request and we render the JavaScript template associated with this action.
  4386. # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
  4387. # include the person's company in the rendered XML, so you get something like this:
  4388. #
  4389. # <person>
  4390. # <id>...</id>
  4391. # ...
  4392. # <company>
  4393. # <id>...</id>
  4394. # <name>...</name>
  4395. # ...
  4396. # </company>
  4397. # </person>
  4398. #
  4399. # Note, however, the extra bit at the top of that action:
  4400. #
  4401. # company = params[:person].delete(:company)
  4402. # @company = Company.find_or_create_by(name: company[:name])
  4403. #
  4404. # This is because the incoming XML document (if a web-service request is in process) can only contain a
  4405. # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
  4406. #
  4407. # person[name]=...&person[company][name]=...&...
  4408. #
  4409. # And, like this (xml-encoded):
  4410. #
  4411. # <person>
  4412. # <name>...</name>
  4413. # <company>
  4414. # <name>...</name>
  4415. # </company>
  4416. # </person>
  4417. #
  4418. # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
  4419. # we extract the company data from the request, find or create the company, and then create the new person
  4420. # with the remaining data.
  4421. #
  4422. # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
  4423. # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
  4424. # and accept Rails' defaults, life will be much easier.
  4425. #
  4426. # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
  4427. # config/initializers/mime_types.rb as follows.
  4428. #
  4429. # Mime::Type.register "image/jpg", :jpg
  4430. #
  4431. # Respond to also allows you to specify a common block for different formats by using any:
  4432. #
  4433. # def index
  4434. # @people = Person.all
  4435. #
  4436. # respond_to do |format|
  4437. # format.html
  4438. # format.any(:xml, :json) { render request.format.to_sym => @people }
  4439. # end
  4440. # end
  4441. #
  4442. # In the example above, if the format is xml, it will render:
  4443. #
  4444. # render xml: @people
  4445. #
  4446. # Or if the format is json:
  4447. #
  4448. # render json: @people
  4449. #
  4450. # Since this is a common pattern, you can use the class method respond_to
  4451. # with the respond_with method to have the same results:
  4452. #
  4453. # class PeopleController < ApplicationController
  4454. # respond_to :html, :xml, :json
  4455. #
  4456. # def index
  4457. # @people = Person.all
  4458. # respond_with(@people)
  4459. # end
  4460. # end
  4461. #
  4462. # Be sure to check the documentation of +respond_with+ and
  4463. # <tt>ActionController::MimeResponds.respond_to</tt> for more examples.
  4464. def respond_to(*mimes, &block)
  4465. raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
  4466. if collector = retrieve_collector_from_mimes(mimes, &block)
  4467. response = collector.response
  4468. response ? response.call : render({})
  4469. end
  4470. end
  4471. # For a given controller action, respond_with generates an appropriate
  4472. # response based on the mime-type requested by the client.
  4473. #
  4474. # If the method is called with just a resource, as in this example -
  4475. #
  4476. # class PeopleController < ApplicationController
  4477. # respond_to :html, :xml, :json
  4478. #
  4479. # def index
  4480. # @people = Person.all
  4481. # respond_with @people
  4482. # end
  4483. # end
  4484. #
  4485. # then the mime-type of the response is typically selected based on the
  4486. # request's Accept header and the set of available formats declared
  4487. # by previous calls to the controller's class method +respond_to+. Alternatively
  4488. # the mime-type can be selected by explicitly setting <tt>request.format</tt> in
  4489. # the controller.
  4490. #
  4491. # If an acceptable format is not identified, the application returns a
  4492. # '406 - not acceptable' status. Otherwise, the default response is to render
  4493. # a template named after the current action and the selected format,
  4494. # e.g. <tt>index.html.erb</tt>. If no template is available, the behavior
  4495. # depends on the selected format:
  4496. #
  4497. # * for an html response - if the request method is +get+, an exception
  4498. # is raised but for other requests such as +post+ the response
  4499. # depends on whether the resource has any validation errors (i.e.
  4500. # assuming that an attempt has been made to save the resource,
  4501. # e.g. by a +create+ action) -
  4502. # 1. If there are no errors, i.e. the resource
  4503. # was saved successfully, the response +redirect+'s to the resource
  4504. # i.e. its +show+ action.
  4505. # 2. If there are validation errors, the response
  4506. # renders a default action, which is <tt>:new</tt> for a
  4507. # +post+ request or <tt>:edit</tt> for +patch+ or +put+.
  4508. # Thus an example like this -
  4509. #
  4510. # respond_to :html, :xml
  4511. #
  4512. # def create
  4513. # @user = User.new(params[:user])
  4514. # flash[:notice] = 'User was successfully created.' if @user.save
  4515. # respond_with(@user)
  4516. # end
  4517. #
  4518. # is equivalent, in the absence of <tt>create.html.erb</tt>, to -
  4519. #
  4520. # def create
  4521. # @user = User.new(params[:user])
  4522. # respond_to do |format|
  4523. # if @user.save
  4524. # flash[:notice] = 'User was successfully created.'
  4525. # format.html { redirect_to(@user) }
  4526. # format.xml { render xml: @user }
  4527. # else
  4528. # format.html { render action: "new" }
  4529. # format.xml { render xml: @user }
  4530. # end
  4531. # end
  4532. # end
  4533. #
  4534. # * for a javascript request - if the template isn't found, an exception is
  4535. # raised.
  4536. # * for other requests - i.e. data formats such as xml, json, csv etc, if
  4537. # the resource passed to +respond_with+ responds to <code>to_<format></code>,
  4538. # the method attempts to render the resource in the requested format
  4539. # directly, e.g. for an xml request, the response is equivalent to calling
  4540. # <code>render xml: resource</code>.
  4541. #
  4542. # === Nested resources
  4543. #
  4544. # As outlined above, the +resources+ argument passed to +respond_with+
  4545. # can play two roles. It can be used to generate the redirect url
  4546. # for successful html requests (e.g. for +create+ actions when
  4547. # no template exists), while for formats other than html and javascript
  4548. # it is the object that gets rendered, by being converted directly to the
  4549. # required format (again assuming no template exists).
  4550. #
  4551. # For redirecting successful html requests, +respond_with+ also supports
  4552. # the use of nested resources, which are supplied in the same way as
  4553. # in <code>form_for</code> and <code>polymorphic_url</code>. For example -
  4554. #
  4555. # def create
  4556. # @project = Project.find(params[:project_id])
  4557. # @task = @project.comments.build(params[:task])
  4558. # flash[:notice] = 'Task was successfully created.' if @task.save
  4559. # respond_with(@project, @task)
  4560. # end
  4561. #
  4562. # This would cause +respond_with+ to redirect to <code>project_task_url</code>
  4563. # instead of <code>task_url</code>. For request formats other than html or
  4564. # javascript, if multiple resources are passed in this way, it is the last
  4565. # one specified that is rendered.
  4566. #
  4567. # === Customizing response behavior
  4568. #
  4569. # Like +respond_to+, +respond_with+ may also be called with a block that
  4570. # can be used to overwrite any of the default responses, e.g. -
  4571. #
  4572. # def create
  4573. # @user = User.new(params[:user])
  4574. # flash[:notice] = "User was successfully created." if @user.save
  4575. #
  4576. # respond_with(@user) do |format|
  4577. # format.html { render }
  4578. # end
  4579. # end
  4580. #
  4581. # The argument passed to the block is an ActionController::MimeResponds::Collector
  4582. # object which stores the responses for the formats defined within the
  4583. # block. Note that formats with responses defined explicitly in this way
  4584. # do not have to first be declared using the class method +respond_to+.
  4585. #
  4586. # Also, a hash passed to +respond_with+ immediately after the specified
  4587. # resource(s) is interpreted as a set of options relevant to all
  4588. # formats. Any option accepted by +render+ can be used, e.g.
  4589. # respond_with @people, status: 200
  4590. # However, note that these options are ignored after an unsuccessful attempt
  4591. # to save a resource, e.g. when automatically rendering <tt>:new</tt>
  4592. # after a post request.
  4593. #
  4594. # Two additional options are relevant specifically to +respond_with+ -
  4595. # 1. <tt>:location</tt> - overwrites the default redirect location used after
  4596. # a successful html +post+ request.
  4597. # 2. <tt>:action</tt> - overwrites the default render action used after an
  4598. # unsuccessful html +post+ request.
  4599. def respond_with(*resources, &block)
  4600. raise "In order to use respond_with, first you need to declare the formats your " \
  4601. "controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
  4602. if collector = retrieve_collector_from_mimes(&block)
  4603. options = resources.size == 1 ? {} : resources.extract_options!
  4604. options[:default_response] = collector.response
  4605. (options.delete(:responder) || self.class.responder).call(self, resources, options)
  4606. end
  4607. end
  4608. protected
  4609. # Collect mimes declared in the class method respond_to valid for the
  4610. # current action.
  4611. def collect_mimes_from_class_level #:nodoc:
  4612. action = action_name.to_s
  4613. self.class.mimes_for_respond_to.keys.select do |mime|
  4614. config = self.class.mimes_for_respond_to[mime]
  4615. if config[:except]
  4616. !config[:except].include?(action)
  4617. elsif config[:only]
  4618. config[:only].include?(action)
  4619. else
  4620. true
  4621. end
  4622. end
  4623. end
  4624. # Returns a Collector object containing the appropriate mime-type response
  4625. # for the current request, based on the available responses defined by a block.
  4626. # In typical usage this is the block passed to +respond_with+ or +respond_to+.
  4627. #
  4628. # Sends :not_acceptable to the client and returns nil if no suitable format
  4629. # is available.
  4630. def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
  4631. mimes ||= collect_mimes_from_class_level
  4632. collector = Collector.new(mimes)
  4633. block.call(collector) if block_given?
  4634. format = collector.negotiate_format(request)
  4635. if format
  4636. self.content_type ||= format.to_s
  4637. lookup_context.formats = [format.to_sym]
  4638. lookup_context.rendered_format = lookup_context.formats.first
  4639. collector
  4640. else
  4641. raise ActionController::UnknownFormat
  4642. end
  4643. end
  4644. # A container for responses available from the current controller for
  4645. # requests for different mime-types sent to a particular action.
  4646. #
  4647. # The public controller methods +respond_with+ and +respond_to+ may be called
  4648. # with a block that is used to define responses to different mime-types, e.g.
  4649. # for +respond_to+ :
  4650. #
  4651. # respond_to do |format|
  4652. # format.html
  4653. # format.xml { render xml: @people }
  4654. # end
  4655. #
  4656. # In this usage, the argument passed to the block (+format+ above) is an
  4657. # instance of the ActionController::MimeResponds::Collector class. This
  4658. # object serves as a container in which available responses can be stored by
  4659. # calling any of the dynamically generated, mime-type-specific methods such
  4660. # as +html+, +xml+ etc on the Collector. Each response is represented by a
  4661. # corresponding block if present.
  4662. #
  4663. # A subsequent call to #negotiate_format(request) will enable the Collector
  4664. # to determine which specific mime-type it should respond with for the current
  4665. # request, with this response then being accessible by calling #response.
  4666. class Collector
  4667. include AbstractController::Collector
  4668. attr_accessor :order, :format
  4669. def initialize(mimes)
  4670. @order, @responses = [], {}
  4671. mimes.each { |mime| send(mime) }
  4672. end
  4673. def any(*args, &block)
  4674. if args.any?
  4675. args.each { |type| send(type, &block) }
  4676. else
  4677. custom(Mime::ALL, &block)
  4678. end
  4679. end
  4680. alias :all :any
  4681. def custom(mime_type, &block)
  4682. mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
  4683. @order << mime_type
  4684. @responses[mime_type] ||= block
  4685. end
  4686. def response
  4687. @responses[format] || @responses[Mime::ALL]
  4688. end
  4689. def negotiate_format(request)
  4690. @format = request.negotiate_mime(order)
  4691. end
  4692. end
  4693. end
  4694. end
  4695. require 'active_support/core_ext/hash/slice'
  4696. require 'active_support/core_ext/hash/except'
  4697. require 'active_support/core_ext/module/anonymous'
  4698. require 'active_support/core_ext/struct'
  4699. require 'action_dispatch/http/mime_type'
  4700. module ActionController
  4701. # Wraps the parameters hash into a nested hash. This will allow clients to submit
  4702. # POST requests without having to specify any root elements.
  4703. #
  4704. # This functionality is enabled in +config/initializers/wrap_parameters.rb+
  4705. # and can be customized. If you are upgrading to \Rails 3.1, this file will
  4706. # need to be created for the functionality to be enabled.
  4707. #
  4708. # You could also turn it on per controller by setting the format array to
  4709. # a non-empty array:
  4710. #
  4711. # class UsersController < ApplicationController
  4712. # wrap_parameters format: [:json, :xml]
  4713. # end
  4714. #
  4715. # If you enable +ParamsWrapper+ for +:json+ format, instead of having to
  4716. # send JSON parameters like this:
  4717. #
  4718. # {"user": {"name": "Konata"}}
  4719. #
  4720. # You can send parameters like this:
  4721. #
  4722. # {"name": "Konata"}
  4723. #
  4724. # And it will be wrapped into a nested hash with the key name matching the
  4725. # controller's name. For example, if you're posting to +UsersController+,
  4726. # your new +params+ hash will look like this:
  4727. #
  4728. # {"name" => "Konata", "user" => {"name" => "Konata"}}
  4729. #
  4730. # You can also specify the key in which the parameters should be wrapped to,
  4731. # and also the list of attributes it should wrap by using either +:include+ or
  4732. # +:exclude+ options like this:
  4733. #
  4734. # class UsersController < ApplicationController
  4735. # wrap_parameters :person, include: [:username, :password]
  4736. # end
  4737. #
  4738. # On ActiveRecord models with no +:include+ or +:exclude+ option set,
  4739. # it will only wrap the parameters returned by the class method
  4740. # <tt>attribute_names</tt>.
  4741. #
  4742. # If you're going to pass the parameters to an +ActiveModel+ object (such as
  4743. # <tt>User.new(params[:user])</tt>), you might consider passing the model class to
  4744. # the method instead. The +ParamsWrapper+ will actually try to determine the
  4745. # list of attribute names from the model and only wrap those attributes:
  4746. #
  4747. # class UsersController < ApplicationController
  4748. # wrap_parameters Person
  4749. # end
  4750. #
  4751. # You still could pass +:include+ and +:exclude+ to set the list of attributes
  4752. # you want to wrap.
  4753. #
  4754. # By default, if you don't specify the key in which the parameters would be
  4755. # wrapped to, +ParamsWrapper+ will actually try to determine if there's
  4756. # a model related to it or not. This controller, for example:
  4757. #
  4758. # class Admin::UsersController < ApplicationController
  4759. # end
  4760. #
  4761. # will try to check if <tt>Admin::User</tt> or +User+ model exists, and use it to
  4762. # determine the wrapper key respectively. If both models don't exist,
  4763. # it will then fallback to use +user+ as the key.
  4764. module ParamsWrapper
  4765. extend ActiveSupport::Concern
  4766. EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
  4767. require 'mutex_m'
  4768. class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc:
  4769. include Mutex_m
  4770. def self.from_hash(hash)
  4771. name = hash[:name]
  4772. format = Array(hash[:format])
  4773. include = hash[:include] && Array(hash[:include]).collect(&:to_s)
  4774. exclude = hash[:exclude] && Array(hash[:exclude]).collect(&:to_s)
  4775. new name, format, include, exclude, nil, nil
  4776. end
  4777. def initialize(name, format, include, exclude, klass, model) # nodoc
  4778. super
  4779. @include_set = include
  4780. @name_set = name
  4781. end
  4782. def model
  4783. super || synchronize { super || self.model = _default_wrap_model }
  4784. end
  4785. def include
  4786. return super if @include_set
  4787. m = model
  4788. synchronize do
  4789. return super if @include_set
  4790. @include_set = true
  4791. unless super || exclude
  4792. if m.respond_to?(:attribute_names) && m.attribute_names.any?
  4793. self.include = m.attribute_names
  4794. end
  4795. end
  4796. end
  4797. end
  4798. def name
  4799. return super if @name_set
  4800. m = model
  4801. synchronize do
  4802. return super if @name_set
  4803. @name_set = true
  4804. unless super || klass.anonymous?
  4805. self.name = m ? m.to_s.demodulize.underscore :
  4806. klass.controller_name.singularize
  4807. end
  4808. end
  4809. end
  4810. private
  4811. # Determine the wrapper model from the controller's name. By convention,
  4812. # this could be done by trying to find the defined model that has the
  4813. # same singularize name as the controller. For example, +UsersController+
  4814. # will try to find if the +User+ model exists.
  4815. #
  4816. # This method also does namespace lookup. Foo::Bar::UsersController will
  4817. # try to find Foo::Bar::User, Foo::User and finally User.
  4818. def _default_wrap_model #:nodoc:
  4819. return nil if klass.anonymous?
  4820. model_name = klass.name.sub(/Controller$/, '').classify
  4821. begin
  4822. if model_klass = model_name.safe_constantize
  4823. model_klass
  4824. else
  4825. namespaces = model_name.split("::")
  4826. namespaces.delete_at(-2)
  4827. break if namespaces.last == model_name
  4828. model_name = namespaces.join("::")
  4829. end
  4830. end until model_klass
  4831. model_klass
  4832. end
  4833. end
  4834. included do
  4835. class_attribute :_wrapper_options
  4836. self._wrapper_options = Options.from_hash(format: [])
  4837. end
  4838. module ClassMethods
  4839. def _set_wrapper_options(options)
  4840. self._wrapper_options = Options.from_hash(options)
  4841. end
  4842. # Sets the name of the wrapper key, or the model which +ParamsWrapper+
  4843. # would use to determine the attribute names from.
  4844. #
  4845. # ==== Examples
  4846. # wrap_parameters format: :xml
  4847. # # enables the parameter wrapper for XML format
  4848. #
  4849. # wrap_parameters :person
  4850. # # wraps parameters into +params[:person]+ hash
  4851. #
  4852. # wrap_parameters Person
  4853. # # wraps parameters by determining the wrapper key from Person class
  4854. # (+person+, in this case) and the list of attribute names
  4855. #
  4856. # wrap_parameters include: [:username, :title]
  4857. # # wraps only +:username+ and +:title+ attributes from parameters.
  4858. #
  4859. # wrap_parameters false
  4860. # # disables parameters wrapping for this controller altogether.
  4861. #
  4862. # ==== Options
  4863. # * <tt>:format</tt> - The list of formats in which the parameters wrapper
  4864. # will be enabled.
  4865. # * <tt>:include</tt> - The list of attribute names which parameters wrapper
  4866. # will wrap into a nested hash.
  4867. # * <tt>:exclude</tt> - The list of attribute names which parameters wrapper
  4868. # will exclude from a nested hash.
  4869. def wrap_parameters(name_or_model_or_options, options = {})
  4870. model = nil
  4871. case name_or_model_or_options
  4872. when Hash
  4873. options = name_or_model_or_options
  4874. when false
  4875. options = options.merge(:format => [])
  4876. when Symbol, String
  4877. options = options.merge(:name => name_or_model_or_options)
  4878. else
  4879. model = name_or_model_or_options
  4880. end
  4881. opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
  4882. opts.model = model
  4883. opts.klass = self
  4884. self._wrapper_options = opts
  4885. end
  4886. # Sets the default wrapper key or model which will be used to determine
  4887. # wrapper key and attribute names. Will be called automatically when the
  4888. # module is inherited.
  4889. def inherited(klass)
  4890. if klass._wrapper_options.format.any?
  4891. params = klass._wrapper_options.dup
  4892. params.klass = klass
  4893. klass._wrapper_options = params
  4894. end
  4895. super
  4896. end
  4897. end
  4898. # Performs parameters wrapping upon the request. Will be called automatically
  4899. # by the metal call stack.
  4900. def process_action(*args)
  4901. if _wrapper_enabled?
  4902. wrapped_hash = _wrap_parameters request.request_parameters
  4903. wrapped_keys = request.request_parameters.keys
  4904. wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
  4905. # This will make the wrapped hash accessible from controller and view
  4906. request.parameters.merge! wrapped_hash
  4907. request.request_parameters.merge! wrapped_hash
  4908. # This will make the wrapped hash displayed in the log file
  4909. request.filtered_parameters.merge! wrapped_filtered_hash
  4910. end
  4911. super
  4912. end
  4913. private
  4914. # Returns the wrapper key which will use to stored wrapped parameters.
  4915. def _wrapper_key
  4916. _wrapper_options.name
  4917. end
  4918. # Returns the list of enabled formats.
  4919. def _wrapper_formats
  4920. _wrapper_options.format
  4921. end
  4922. # Returns the list of parameters which will be selected for wrapped.
  4923. def _wrap_parameters(parameters)
  4924. value = if include_only = _wrapper_options.include
  4925. parameters.slice(*include_only)
  4926. else
  4927. exclude = _wrapper_options.exclude || []
  4928. parameters.except(*(exclude + EXCLUDE_PARAMETERS))
  4929. end
  4930. { _wrapper_key => value }
  4931. end
  4932. # Checks if we should perform parameters wrapping.
  4933. def _wrapper_enabled?
  4934. ref = request.content_mime_type.try(:ref)
  4935. _wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
  4936. end
  4937. end
  4938. end
  4939. require 'action_dispatch/http/request'
  4940. require 'action_dispatch/http/response'
  4941. module ActionController
  4942. module RackDelegation
  4943. extend ActiveSupport::Concern
  4944. delegate :headers, :status=, :location=, :content_type=,
  4945. :status, :location, :content_type, :to => "@_response"
  4946. def dispatch(action, request)
  4947. set_response!(request)
  4948. super(action, request)
  4949. end
  4950. def response_body=(body)
  4951. response.body = body if response
  4952. super
  4953. end
  4954. def reset_session
  4955. @_request.reset_session
  4956. end
  4957. private
  4958. def set_response!(request)
  4959. @_response = ActionDispatch::Response.new
  4960. @_response.request = request
  4961. end
  4962. end
  4963. end
  4964. module ActionController
  4965. class RedirectBackError < AbstractController::Error #:nodoc:
  4966. DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
  4967. def initialize(message = nil)
  4968. super(message || DEFAULT_MESSAGE)
  4969. end
  4970. end
  4971. module Redirecting
  4972. extend ActiveSupport::Concern
  4973. include AbstractController::Logger
  4974. include ActionController::RackDelegation
  4975. include ActionController::UrlFor
  4976. # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
  4977. #
  4978. # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
  4979. # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
  4980. # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection.
  4981. # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
  4982. # * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
  4983. # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
  4984. # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
  4985. #
  4986. # redirect_to action: "show", id: 5
  4987. # redirect_to post
  4988. # redirect_to "http://www.rubyonrails.org"
  4989. # redirect_to "/images/screenshot.jpg"
  4990. # redirect_to articles_url
  4991. # redirect_to :back
  4992. # redirect_to proc { edit_post_url(@post) }
  4993. #
  4994. # The redirection happens as a "302 Found" header unless otherwise specified.
  4995. #
  4996. # redirect_to post_url(@post), status: :found
  4997. # redirect_to action: 'atom', status: :moved_permanently
  4998. # redirect_to post_url(@post), status: 301
  4999. # redirect_to action: 'atom', status: 302
  5000. #
  5001. # The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
  5002. # integer, or a symbol representing the downcased, underscored and symbolized description.
  5003. # Note that the status code must be a 3xx HTTP code, or redirection will not occur.
  5004. #
  5005. # If you are using XHR requests other than GET or POST and redirecting after the
  5006. # request then some browsers will follow the redirect using the original request
  5007. # method. This may lead to undesirable behavior such as a double DELETE. To work
  5008. # around this you can return a <tt>303 See Other</tt> status code which will be
  5009. # followed using a GET request.
  5010. #
  5011. # redirect_to posts_url, status: :see_other
  5012. # redirect_to action: 'index', status: 303
  5013. #
  5014. # It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
  5015. # +alert+ and +notice+ as well as a general purpose +flash+ bucket.
  5016. #
  5017. # redirect_to post_url(@post), alert: "Watch it, mister!"
  5018. # redirect_to post_url(@post), status: :found, notice: "Pay attention to the road"
  5019. # redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
  5020. # redirect_to { action: 'atom' }, alert: "Something serious happened"
  5021. #
  5022. # When using <tt>redirect_to :back</tt>, if there is no referrer, ActionController::RedirectBackError will be raised. You may specify some fallback
  5023. # behavior for this case by rescuing ActionController::RedirectBackError.
  5024. def redirect_to(options = {}, response_status = {}) #:doc:
  5025. raise ActionControllerError.new("Cannot redirect to nil!") unless options
  5026. raise AbstractController::DoubleRenderError if response_body
  5027. self.status = _extract_redirect_to_status(options, response_status)
  5028. self.location = _compute_redirect_to_location(options)
  5029. self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
  5030. end
  5031. private
  5032. def _extract_redirect_to_status(options, response_status)
  5033. if options.is_a?(Hash) && options.key?(:status)
  5034. Rack::Utils.status_code(options.delete(:status))
  5035. elsif response_status.key?(:status)
  5036. Rack::Utils.status_code(response_status[:status])
  5037. else
  5038. 302
  5039. end
  5040. end
  5041. def _compute_redirect_to_location(options)
  5042. case options
  5043. # The scheme name consist of a letter followed by any combination of
  5044. # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
  5045. # characters; and is terminated by a colon (":").
  5046. # The protocol relative scheme starts with a double slash "//"
  5047. when %r{\A(\w[\w+.-]*:|//).*}
  5048. options
  5049. when String
  5050. request.protocol + request.host_with_port + options
  5051. when :back
  5052. request.headers["Referer"] or raise RedirectBackError
  5053. when Proc
  5054. _compute_redirect_to_location options.call
  5055. else
  5056. url_for(options)
  5057. end.delete("\0\r\n")
  5058. end
  5059. end
  5060. end
  5061. require 'set'
  5062. module ActionController
  5063. # See <tt>Renderers.add</tt>
  5064. def self.add_renderer(key, &block)
  5065. Renderers.add(key, &block)
  5066. end
  5067. module Renderers
  5068. extend ActiveSupport::Concern
  5069. included do
  5070. class_attribute :_renderers
  5071. self._renderers = Set.new.freeze
  5072. end
  5073. module ClassMethods
  5074. def use_renderers(*args)
  5075. renderers = _renderers + args
  5076. self._renderers = renderers.freeze
  5077. end
  5078. alias use_renderer use_renderers
  5079. end
  5080. def render_to_body(options)
  5081. _handle_render_options(options) || super
  5082. end
  5083. def _handle_render_options(options)
  5084. _renderers.each do |name|
  5085. if options.key?(name)
  5086. _process_options(options)
  5087. return send("_render_option_#{name}", options.delete(name), options)
  5088. end
  5089. end
  5090. nil
  5091. end
  5092. # Hash of available renderers, mapping a renderer name to its proc.
  5093. # Default keys are :json, :js, :xml.
  5094. RENDERERS = Set.new
  5095. # Adds a new renderer to call within controller actions.
  5096. # A renderer is invoked by passing its name as an option to
  5097. # <tt>AbstractController::Rendering#render</tt>. To create a renderer
  5098. # pass it a name and a block. The block takes two arguments, the first
  5099. # is the value paired with its key and the second is the remaining
  5100. # hash of options passed to +render+.
  5101. #
  5102. # Create a csv renderer:
  5103. #
  5104. # ActionController::Renderers.add :csv do |obj, options|
  5105. # filename = options[:filename] || 'data'
  5106. # str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
  5107. # send_data str, type: Mime::CSV,
  5108. # disposition: "attachment; filename=#{filename}.csv"
  5109. # end
  5110. #
  5111. # Note that we used Mime::CSV for the csv mime type as it comes with Rails.
  5112. # For a custom renderer, you'll need to register a mime type with
  5113. # <tt>Mime::Type.register</tt>.
  5114. #
  5115. # To use the csv renderer in a controller action:
  5116. #
  5117. # def show
  5118. # @csvable = Csvable.find(params[:id])
  5119. # respond_to do |format|
  5120. # format.html
  5121. # format.csv { render csv: @csvable, filename: @csvable.name }
  5122. # }
  5123. # end
  5124. # To use renderers and their mime types in more concise ways, see
  5125. # <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
  5126. # <tt>ActionController::MimeResponds#respond_with</tt>
  5127. def self.add(key, &block)
  5128. define_method("_render_option_#{key}", &block)
  5129. RENDERERS << key.to_sym
  5130. end
  5131. module All
  5132. extend ActiveSupport::Concern
  5133. include Renderers
  5134. included do
  5135. self._renderers = RENDERERS
  5136. end
  5137. end
  5138. add :json do |json, options|
  5139. json = json.to_json(options) unless json.kind_of?(String)
  5140. if options[:callback].present?
  5141. self.content_type ||= Mime::JS
  5142. "#{options[:callback]}(#{json})"
  5143. else
  5144. self.content_type ||= Mime::JSON
  5145. json
  5146. end
  5147. end
  5148. add :js do |js, options|
  5149. self.content_type ||= Mime::JS
  5150. js.respond_to?(:to_js) ? js.to_js(options) : js
  5151. end
  5152. add :xml do |xml, options|
  5153. self.content_type ||= Mime::XML
  5154. xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
  5155. end
  5156. end
  5157. end
  5158. module ActionController
  5159. module Rendering
  5160. extend ActiveSupport::Concern
  5161. include AbstractController::Rendering
  5162. # Before processing, set the request formats in current controller formats.
  5163. def process_action(*) #:nodoc:
  5164. self.formats = request.formats.map { |x| x.ref }
  5165. super
  5166. end
  5167. # Check for double render errors and set the content_type after rendering.
  5168. def render(*args) #:nodoc:
  5169. raise ::AbstractController::DoubleRenderError if response_body
  5170. super
  5171. self.content_type ||= Mime[lookup_context.rendered_format].to_s
  5172. response_body
  5173. end
  5174. # Overwrite render_to_string because body can now be set to a rack body.
  5175. def render_to_string(*)
  5176. if self.response_body = super
  5177. string = ""
  5178. response_body.each { |r| string << r }
  5179. string
  5180. end
  5181. ensure
  5182. self.response_body = nil
  5183. end
  5184. def render_to_body(*)
  5185. super || " "
  5186. end
  5187. private
  5188. # Normalize arguments by catching blocks and setting them on :update.
  5189. def _normalize_args(action=nil, options={}, &blk) #:nodoc:
  5190. options = super
  5191. options[:update] = blk if block_given?
  5192. options
  5193. end
  5194. # Normalize both text and status options.
  5195. def _normalize_options(options) #:nodoc:
  5196. if options.key?(:text) && options[:text].respond_to?(:to_text)
  5197. options[:text] = options[:text].to_text
  5198. end
  5199. if options.delete(:nothing) || (options.key?(:text) && options[:text].nil?)
  5200. options[:text] = " "
  5201. end
  5202. if options[:status]
  5203. options[:status] = Rack::Utils.status_code(options[:status])
  5204. end
  5205. super
  5206. end
  5207. # Process controller specific options, as status, content-type and location.
  5208. def _process_options(options) #:nodoc:
  5209. status, content_type, location = options.values_at(:status, :content_type, :location)
  5210. self.status = status if status
  5211. self.content_type = content_type if content_type
  5212. self.headers["Location"] = url_for(location) if location
  5213. super
  5214. end
  5215. end
  5216. end
  5217. require 'rack/session/abstract/id'
  5218. require 'action_controller/metal/exceptions'
  5219. module ActionController #:nodoc:
  5220. class InvalidAuthenticityToken < ActionControllerError #:nodoc:
  5221. end
  5222. # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
  5223. # by including a token in the rendered html for your application. This token is
  5224. # stored as a random string in the session, to which an attacker does not have
  5225. # access. When a request reaches your application, \Rails verifies the received
  5226. # token with the token in the session. Only HTML and JavaScript requests are checked,
  5227. # so this will not protect your XML API (presumably you'll have a different
  5228. # authentication scheme there anyway). Also, GET requests are not protected as these
  5229. # should be idempotent.
  5230. #
  5231. # It's important to remember that XML or JSON requests are also affected and if
  5232. # you're building an API you'll need something like:
  5233. #
  5234. # class ApplicationController < ActionController::Base
  5235. # protect_from_forgery
  5236. # skip_before_action :verify_authenticity_token, if: :json_request?
  5237. #
  5238. # protected
  5239. #
  5240. # def json_request?
  5241. # request.format.json?
  5242. # end
  5243. # end
  5244. #
  5245. # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
  5246. # which checks the token and resets the session if it doesn't match what was expected.
  5247. # A call to this method is generated for new \Rails applications by default.
  5248. #
  5249. # The token parameter is named <tt>authenticity_token</tt> by default. The name and
  5250. # value of this token must be added to every layout that renders forms by including
  5251. # <tt>csrf_meta_tags</tt> in the html +head+.
  5252. #
  5253. # Learn more about CSRF attacks and securing your application in the
  5254. # {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
  5255. module RequestForgeryProtection
  5256. extend ActiveSupport::Concern
  5257. include AbstractController::Helpers
  5258. include AbstractController::Callbacks
  5259. included do
  5260. # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
  5261. # sets it to <tt>:authenticity_token</tt> by default.
  5262. config_accessor :request_forgery_protection_token
  5263. self.request_forgery_protection_token ||= :authenticity_token
  5264. # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
  5265. config_accessor :allow_forgery_protection
  5266. self.allow_forgery_protection = true if allow_forgery_protection.nil?
  5267. helper_method :form_authenticity_token
  5268. helper_method :protect_against_forgery?
  5269. end
  5270. module ClassMethods
  5271. # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
  5272. #
  5273. # class FooController < ApplicationController
  5274. # protect_from_forgery except: :index
  5275. #
  5276. # You can disable csrf protection on controller-by-controller basis:
  5277. #
  5278. # skip_before_action :verify_authenticity_token
  5279. #
  5280. # It can also be disabled for specific controller actions:
  5281. #
  5282. # skip_before_action :verify_authenticity_token, except: [:create]
  5283. #
  5284. # Valid Options:
  5285. #
  5286. # * <tt>:only/:except</tt> - Passed to the <tt>before_action</tt> call. Set which actions are verified.
  5287. # * <tt>:with</tt> - Set the method to handle unverified request.
  5288. #
  5289. # Valid unverified request handling methods are:
  5290. # * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
  5291. # * <tt>:reset_session</tt> - Resets the session.
  5292. # * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
  5293. def protect_from_forgery(options = {})
  5294. include protection_method_module(options[:with] || :null_session)
  5295. self.request_forgery_protection_token ||= :authenticity_token
  5296. prepend_before_action :verify_authenticity_token, options
  5297. end
  5298. private
  5299. def protection_method_module(name)
  5300. ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
  5301. rescue NameError
  5302. raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session'
  5303. end
  5304. end
  5305. module ProtectionMethods
  5306. module NullSession
  5307. protected
  5308. # This is the method that defines the application behavior when a request is found to be unverified.
  5309. def handle_unverified_request
  5310. request.session = NullSessionHash.new(request.env)
  5311. request.env['action_dispatch.request.flash_hash'] = nil
  5312. request.env['rack.session.options'] = { skip: true }
  5313. request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
  5314. end
  5315. class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
  5316. def initialize(env)
  5317. super(nil, env)
  5318. @data = {}
  5319. @loaded = true
  5320. end
  5321. def exists?
  5322. true
  5323. end
  5324. end
  5325. class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
  5326. def self.build(request)
  5327. key_generator = request.env[ActionDispatch::Cookies::GENERATOR_KEY]
  5328. host = request.host
  5329. secure = request.ssl?
  5330. new(key_generator, host, secure, options_for_env({}))
  5331. end
  5332. def write(*)
  5333. # nothing
  5334. end
  5335. end
  5336. end
  5337. module ResetSession
  5338. protected
  5339. def handle_unverified_request
  5340. reset_session
  5341. end
  5342. end
  5343. module Exception
  5344. protected
  5345. def handle_unverified_request
  5346. raise ActionController::InvalidAuthenticityToken
  5347. end
  5348. end
  5349. end
  5350. protected
  5351. # The actual before_action that is used. Modify this to change how you handle unverified requests.
  5352. def verify_authenticity_token
  5353. unless verified_request?
  5354. logger.warn "Can't verify CSRF token authenticity" if logger
  5355. handle_unverified_request
  5356. end
  5357. end
  5358. # Returns true or false if a request is verified. Checks:
  5359. #
  5360. # * is it a GET or HEAD request? Gets should be safe and idempotent
  5361. # * Does the form_authenticity_token match the given token value from the params?
  5362. # * Does the X-CSRF-Token header match the form_authenticity_token
  5363. def verified_request?
  5364. !protect_against_forgery? || request.get? || request.head? ||
  5365. form_authenticity_token == params[request_forgery_protection_token] ||
  5366. form_authenticity_token == request.headers['X-CSRF-Token']
  5367. end
  5368. # Sets the token value for the current session.
  5369. def form_authenticity_token
  5370. session[:_csrf_token] ||= SecureRandom.base64(32)
  5371. end
  5372. # The form's authenticity parameter. Override to provide your own.
  5373. def form_authenticity_param
  5374. params[request_forgery_protection_token]
  5375. end
  5376. def protect_against_forgery?
  5377. allow_forgery_protection
  5378. end
  5379. end
  5380. end
  5381. module ActionController #:nodoc:
  5382. # This module is responsible to provide `rescue_from` helpers
  5383. # to controllers and configure when detailed exceptions must be
  5384. # shown.
  5385. module Rescue
  5386. extend ActiveSupport::Concern
  5387. include ActiveSupport::Rescuable
  5388. def rescue_with_handler(exception)
  5389. if (exception.respond_to?(:original_exception) &&
  5390. (orig_exception = exception.original_exception) &&
  5391. handler_for_rescue(orig_exception))
  5392. exception = orig_exception
  5393. end
  5394. super(exception)
  5395. end
  5396. # Override this method if you want to customize when detailed
  5397. # exceptions must be shown. This method is only called when
  5398. # consider_all_requests_local is false. By default, it returns
  5399. # false, but someone may set it to `request.local?` so local
  5400. # requests in production still shows the detailed exception pages.
  5401. def show_detailed_exceptions?
  5402. false
  5403. end
  5404. private
  5405. def process_action(*args)
  5406. super
  5407. rescue Exception => exception
  5408. request.env['action_dispatch.show_detailed_exceptions'] ||= show_detailed_exceptions?
  5409. rescue_with_handler(exception) || raise(exception)
  5410. end
  5411. end
  5412. end
  5413. require 'active_support/json'
  5414. module ActionController #:nodoc:
  5415. # Responsible for exposing a resource to different mime requests,
  5416. # usually depending on the HTTP verb. The responder is triggered when
  5417. # <code>respond_with</code> is called. The simplest case to study is a GET request:
  5418. #
  5419. # class PeopleController < ApplicationController
  5420. # respond_to :html, :xml, :json
  5421. #
  5422. # def index
  5423. # @people = Person.all
  5424. # respond_with(@people)
  5425. # end
  5426. # end
  5427. #
  5428. # When a request comes in, for example for an XML response, three steps happen:
  5429. #
  5430. # 1) the responder searches for a template at people/index.xml;
  5431. #
  5432. # 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
  5433. #
  5434. # 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
  5435. #
  5436. # === Builtin HTTP verb semantics
  5437. #
  5438. # The default \Rails responder holds semantics for each HTTP verb. Depending on the
  5439. # content type, verb and the resource status, it will behave differently.
  5440. #
  5441. # Using \Rails default responder, a POST request for creating an object could
  5442. # be written as:
  5443. #
  5444. # def create
  5445. # @user = User.new(params[:user])
  5446. # flash[:notice] = 'User was successfully created.' if @user.save
  5447. # respond_with(@user)
  5448. # end
  5449. #
  5450. # Which is exactly the same as:
  5451. #
  5452. # def create
  5453. # @user = User.new(params[:user])
  5454. #
  5455. # respond_to do |format|
  5456. # if @user.save
  5457. # flash[:notice] = 'User was successfully created.'
  5458. # format.html { redirect_to(@user) }
  5459. # format.xml { render xml: @user, status: :created, location: @user }
  5460. # else
  5461. # format.html { render action: "new" }
  5462. # format.xml { render xml: @user.errors, status: :unprocessable_entity }
  5463. # end
  5464. # end
  5465. # end
  5466. #
  5467. # The same happens for PATCH/PUT and DELETE requests.
  5468. #
  5469. # === Nested resources
  5470. #
  5471. # You can supply nested resources as you do in <code>form_for</code> and <code>polymorphic_url</code>.
  5472. # Consider the project has many tasks example. The create action for
  5473. # TasksController would be like:
  5474. #
  5475. # def create
  5476. # @project = Project.find(params[:project_id])
  5477. # @task = @project.tasks.build(params[:task])
  5478. # flash[:notice] = 'Task was successfully created.' if @task.save
  5479. # respond_with(@project, @task)
  5480. # end
  5481. #
  5482. # Giving several resources ensures that the responder will redirect to
  5483. # <code>project_task_url</code> instead of <code>task_url</code>.
  5484. #
  5485. # Namespaced and singleton resources require a symbol to be given, as in
  5486. # polymorphic urls. If a project has one manager which has many tasks, it
  5487. # should be invoked as:
  5488. #
  5489. # respond_with(@project, :manager, @task)
  5490. #
  5491. # Note that if you give an array, it will be treated as a collection,
  5492. # so the following is not equivalent:
  5493. #
  5494. # respond_with [@project, :manager, @task]
  5495. #
  5496. # === Custom options
  5497. #
  5498. # <code>respond_with</code> also allows you to pass options that are forwarded
  5499. # to the underlying render call. Those options are only applied for success
  5500. # scenarios. For instance, you can do the following in the create method above:
  5501. #
  5502. # def create
  5503. # @project = Project.find(params[:project_id])
  5504. # @task = @project.tasks.build(params[:task])
  5505. # flash[:notice] = 'Task was successfully created.' if @task.save
  5506. # respond_with(@project, @task, status: 201)
  5507. # end
  5508. #
  5509. # This will return status 201 if the task was saved successfully. If not,
  5510. # it will simply ignore the given options and return status 422 and the
  5511. # resource errors. To customize the failure scenario, you can pass a
  5512. # a block to <code>respond_with</code>:
  5513. #
  5514. # def create
  5515. # @project = Project.find(params[:project_id])
  5516. # @task = @project.tasks.build(params[:task])
  5517. # respond_with(@project, @task, status: 201) do |format|
  5518. # if @task.save
  5519. # flash[:notice] = 'Task was successfully created.'
  5520. # else
  5521. # format.html { render "some_special_template" }
  5522. # end
  5523. # end
  5524. # end
  5525. #
  5526. # Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>.
  5527. class Responder
  5528. attr_reader :controller, :request, :format, :resource, :resources, :options
  5529. DEFAULT_ACTIONS_FOR_VERBS = {
  5530. :post => :new,
  5531. :patch => :edit,
  5532. :put => :edit
  5533. }
  5534. def initialize(controller, resources, options={})
  5535. @controller = controller
  5536. @request = @controller.request
  5537. @format = @controller.formats.first
  5538. @resource = resources.last
  5539. @resources = resources
  5540. @options = options
  5541. @action = options.delete(:action)
  5542. @default_response = options.delete(:default_response)
  5543. end
  5544. delegate :head, :render, :redirect_to, :to => :controller
  5545. delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request
  5546. # Undefine :to_json and :to_yaml since it's defined on Object
  5547. undef_method(:to_json) if method_defined?(:to_json)
  5548. undef_method(:to_yaml) if method_defined?(:to_yaml)
  5549. # Initializes a new responder an invoke the proper format. If the format is
  5550. # not defined, call to_format.
  5551. #
  5552. def self.call(*args)
  5553. new(*args).respond
  5554. end
  5555. # Main entry point for responder responsible to dispatch to the proper format.
  5556. #
  5557. def respond
  5558. method = "to_#{format}"
  5559. respond_to?(method) ? send(method) : to_format
  5560. end
  5561. # HTML format does not render the resource, it always attempt to render a
  5562. # template.
  5563. #
  5564. def to_html
  5565. default_render
  5566. rescue ActionView::MissingTemplate => e
  5567. navigation_behavior(e)
  5568. end
  5569. # to_js simply tries to render a template. If no template is found, raises the error.
  5570. def to_js
  5571. default_render
  5572. end
  5573. # All other formats follow the procedure below. First we try to render a
  5574. # template, if the template is not available, we verify if the resource
  5575. # responds to :to_format and display it.
  5576. #
  5577. def to_format
  5578. if get? || !has_errors? || response_overridden?
  5579. default_render
  5580. else
  5581. display_errors
  5582. end
  5583. rescue ActionView::MissingTemplate => e
  5584. api_behavior(e)
  5585. end
  5586. protected
  5587. # This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
  5588. def navigation_behavior(error)
  5589. if get?
  5590. raise error
  5591. elsif has_errors? && default_action
  5592. render :action => default_action
  5593. else
  5594. redirect_to navigation_location
  5595. end
  5596. end
  5597. # This is the common behavior for formats associated with APIs, such as :xml and :json.
  5598. def api_behavior(error)
  5599. raise error unless resourceful?
  5600. if get?
  5601. display resource
  5602. elsif post?
  5603. display resource, :status => :created, :location => api_location
  5604. else
  5605. head :no_content
  5606. end
  5607. end
  5608. # Checks whether the resource responds to the current format or not.
  5609. #
  5610. def resourceful?
  5611. resource.respond_to?("to_#{format}")
  5612. end
  5613. # Returns the resource location by retrieving it from the options or
  5614. # returning the resources array.
  5615. #
  5616. def resource_location
  5617. options[:location] || resources
  5618. end
  5619. alias :navigation_location :resource_location
  5620. alias :api_location :resource_location
  5621. # If a response block was given, use it, otherwise call render on
  5622. # controller.
  5623. #
  5624. def default_render
  5625. if @default_response
  5626. @default_response.call(options)
  5627. else
  5628. controller.default_render(options)
  5629. end
  5630. end
  5631. # Display is just a shortcut to render a resource with the current format.
  5632. #
  5633. # display @user, status: :ok
  5634. #
  5635. # For XML requests it's equivalent to:
  5636. #
  5637. # render xml: @user, status: :ok
  5638. #
  5639. # Options sent by the user are also used:
  5640. #
  5641. # respond_with(@user, status: :created)
  5642. # display(@user, status: :ok)
  5643. #
  5644. # Results in:
  5645. #
  5646. # render xml: @user, status: :created
  5647. #
  5648. def display(resource, given_options={})
  5649. controller.render given_options.merge!(options).merge!(format => resource)
  5650. end
  5651. def display_errors
  5652. controller.render format => resource_errors, :status => :unprocessable_entity
  5653. end
  5654. # Check whether the resource has errors.
  5655. #
  5656. def has_errors?
  5657. resource.respond_to?(:errors) && !resource.errors.empty?
  5658. end
  5659. # By default, render the <code>:edit</code> action for HTML requests with errors, unless
  5660. # the verb was POST.
  5661. #
  5662. def default_action
  5663. @action ||= DEFAULT_ACTIONS_FOR_VERBS[request.request_method_symbol]
  5664. end
  5665. def resource_errors
  5666. respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors
  5667. end
  5668. def json_resource_errors
  5669. {:errors => resource.errors}
  5670. end
  5671. def response_overridden?
  5672. @default_response.present?
  5673. end
  5674. end
  5675. end
  5676. require 'rack/chunked'
  5677. module ActionController #:nodoc:
  5678. # Allows views to be streamed back to the client as they are rendered.
  5679. #
  5680. # The default way Rails renders views is by first rendering the template
  5681. # and then the layout. The response is sent to the client after the whole
  5682. # template is rendered, all queries are made, and the layout is processed.
  5683. #
  5684. # Streaming inverts the rendering flow by rendering the layout first and
  5685. # streaming each part of the layout as they are processed. This allows the
  5686. # header of the HTML (which is usually in the layout) to be streamed back
  5687. # to client very quickly, allowing JavaScripts and stylesheets to be loaded
  5688. # earlier than usual.
  5689. #
  5690. # This approach was introduced in Rails 3.1 and is still improving. Several
  5691. # Rack middlewares may not work and you need to be careful when streaming.
  5692. # Those points are going to be addressed soon.
  5693. #
  5694. # In order to use streaming, you will need to use a Ruby version that
  5695. # supports fibers (fibers are supported since version 1.9.2 of the main
  5696. # Ruby implementation).
  5697. #
  5698. # Streaming can be added to a given template easily, all you need to do is
  5699. # to pass the :stream option.
  5700. #
  5701. # class PostsController
  5702. # def index
  5703. # @posts = Post.all
  5704. # render stream: true
  5705. # end
  5706. # end
  5707. #
  5708. # == When to use streaming
  5709. #
  5710. # Streaming may be considered to be overkill for lightweight actions like
  5711. # +new+ or +edit+. The real benefit of streaming is on expensive actions
  5712. # that, for example, do a lot of queries on the database.
  5713. #
  5714. # In such actions, you want to delay queries execution as much as you can.
  5715. # For example, imagine the following +dashboard+ action:
  5716. #
  5717. # def dashboard
  5718. # @posts = Post.all
  5719. # @pages = Page.all
  5720. # @articles = Article.all
  5721. # end
  5722. #
  5723. # Most of the queries here are happening in the controller. In order to benefit
  5724. # from streaming you would want to rewrite it as:
  5725. #
  5726. # def dashboard
  5727. # # Allow lazy execution of the queries
  5728. # @posts = Post.all
  5729. # @pages = Page.all
  5730. # @articles = Article.all
  5731. # render stream: true
  5732. # end
  5733. #
  5734. # Notice that :stream only works with templates. Rendering :json
  5735. # or :xml with :stream won't work.
  5736. #
  5737. # == Communication between layout and template
  5738. #
  5739. # When streaming, rendering happens top-down instead of inside-out.
  5740. # Rails starts with the layout, and the template is rendered later,
  5741. # when its +yield+ is reached.
  5742. #
  5743. # This means that, if your application currently relies on instance
  5744. # variables set in the template to be used in the layout, they won't
  5745. # work once you move to streaming. The proper way to communicate
  5746. # between layout and template, regardless of whether you use streaming
  5747. # or not, is by using +content_for+, +provide+ and +yield+.
  5748. #
  5749. # Take a simple example where the layout expects the template to tell
  5750. # which title to use:
  5751. #
  5752. # <html>
  5753. # <head><title><%= yield :title %></title></head>
  5754. # <body><%= yield %></body>
  5755. # </html>
  5756. #
  5757. # You would use +content_for+ in your template to specify the title:
  5758. #
  5759. # <%= content_for :title, "Main" %>
  5760. # Hello
  5761. #
  5762. # And the final result would be:
  5763. #
  5764. # <html>
  5765. # <head><title>Main</title></head>
  5766. # <body>Hello</body>
  5767. # </html>
  5768. #
  5769. # However, if +content_for+ is called several times, the final result
  5770. # would have all calls concatenated. For instance, if we have the following
  5771. # template:
  5772. #
  5773. # <%= content_for :title, "Main" %>
  5774. # Hello
  5775. # <%= content_for :title, " page" %>
  5776. #
  5777. # The final result would be:
  5778. #
  5779. # <html>
  5780. # <head><title>Main page</title></head>
  5781. # <body>Hello</body>
  5782. # </html>
  5783. #
  5784. # This means that, if you have <code>yield :title</code> in your layout
  5785. # and you want to use streaming, you would have to render the whole template
  5786. # (and eventually trigger all queries) before streaming the title and all
  5787. # assets, which kills the purpose of streaming. For this reason Rails 3.1
  5788. # introduces a new helper called +provide+ that does the same as +content_for+
  5789. # but tells the layout to stop searching for other entries and continue rendering.
  5790. #
  5791. # For instance, the template above using +provide+ would be:
  5792. #
  5793. # <%= provide :title, "Main" %>
  5794. # Hello
  5795. # <%= content_for :title, " page" %>
  5796. #
  5797. # Giving:
  5798. #
  5799. # <html>
  5800. # <head><title>Main</title></head>
  5801. # <body>Hello</body>
  5802. # </html>
  5803. #
  5804. # That said, when streaming, you need to properly check your templates
  5805. # and choose when to use +provide+ and +content_for+.
  5806. #
  5807. # == Headers, cookies, session and flash
  5808. #
  5809. # When streaming, the HTTP headers are sent to the client right before
  5810. # it renders the first line. This means that, modifying headers, cookies,
  5811. # session or flash after the template starts rendering will not propagate
  5812. # to the client.
  5813. #
  5814. # == Middlewares
  5815. #
  5816. # Middlewares that need to manipulate the body won't work with streaming.
  5817. # You should disable those middlewares whenever streaming in development
  5818. # or production. For instance, <tt>Rack::Bug</tt> won't work when streaming as it
  5819. # needs to inject contents in the HTML body.
  5820. #
  5821. # Also <tt>Rack::Cache</tt> won't work with streaming as it does not support
  5822. # streaming bodies yet. Whenever streaming Cache-Control is automatically
  5823. # set to "no-cache".
  5824. #
  5825. # == Errors
  5826. #
  5827. # When it comes to streaming, exceptions get a bit more complicated. This
  5828. # happens because part of the template was already rendered and streamed to
  5829. # the client, making it impossible to render a whole exception page.
  5830. #
  5831. # Currently, when an exception happens in development or production, Rails
  5832. # will automatically stream to the client:
  5833. #
  5834. # "><script>window.location = "/500.html"</script></html>
  5835. #
  5836. # The first two characters (">) are required in case the exception happens
  5837. # while rendering attributes for a given tag. You can check the real cause
  5838. # for the exception in your logger.
  5839. #
  5840. # == Web server support
  5841. #
  5842. # Not all web servers support streaming out-of-the-box. You need to check
  5843. # the instructions for each of them.
  5844. #
  5845. # ==== Unicorn
  5846. #
  5847. # Unicorn supports streaming but it needs to be configured. For this, you
  5848. # need to create a config file as follow:
  5849. #
  5850. # # unicorn.config.rb
  5851. # listen 3000, tcp_nopush: false
  5852. #
  5853. # And use it on initialization:
  5854. #
  5855. # unicorn_rails --config-file unicorn.config.rb
  5856. #
  5857. # You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
  5858. # Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
  5859. #
  5860. # If you are using Unicorn with Nginx, you may need to tweak Nginx.
  5861. # Streaming should work out of the box on Rainbows.
  5862. #
  5863. # ==== Passenger
  5864. #
  5865. # To be described.
  5866. #
  5867. module Streaming
  5868. extend ActiveSupport::Concern
  5869. include AbstractController::Rendering
  5870. protected
  5871. # Set proper cache control and transfer encoding when streaming
  5872. def _process_options(options) #:nodoc:
  5873. super
  5874. if options[:stream]
  5875. if env["HTTP_VERSION"] == "HTTP/1.0"
  5876. options.delete(:stream)
  5877. else
  5878. headers["Cache-Control"] ||= "no-cache"
  5879. headers["Transfer-Encoding"] = "chunked"
  5880. headers.delete("Content-Length")
  5881. end
  5882. end
  5883. end
  5884. # Call render_body if we are streaming instead of usual +render+.
  5885. def _render_template(options) #:nodoc:
  5886. if options.delete(:stream)
  5887. Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
  5888. else
  5889. super
  5890. end
  5891. end
  5892. end
  5893. end
  5894. require 'active_support/core_ext/hash/indifferent_access'
  5895. require 'active_support/core_ext/array/wrap'
  5896. require 'active_support/rescuable'
  5897. require 'action_dispatch/http/upload'
  5898. module ActionController
  5899. # Raised when a required parameter is missing.
  5900. #
  5901. # params = ActionController::Parameters.new(a: {})
  5902. # params.fetch(:b)
  5903. # # => ActionController::ParameterMissing: param not found: b
  5904. # params.require(:a)
  5905. # # => ActionController::ParameterMissing: param not found: a
  5906. class ParameterMissing < KeyError
  5907. attr_reader :param # :nodoc:
  5908. def initialize(param) # :nodoc:
  5909. @param = param
  5910. super("param not found: #{param}")
  5911. end
  5912. end
  5913. # Raised when a supplied parameter is not expected.
  5914. #
  5915. # params = ActionController::Parameters.new(a: "123", b: "456")
  5916. # params.permit(:c)
  5917. # # => ActionController::UnpermittedParameters: found unexpected keys: a, b
  5918. class UnpermittedParameters < IndexError
  5919. attr_reader :params # :nodoc:
  5920. def initialize(params) # :nodoc:
  5921. @params = params
  5922. super("found unpermitted parameters: #{params.join(", ")}")
  5923. end
  5924. end
  5925. # == Action Controller \Parameters
  5926. #
  5927. # Allows to choose which attributes should be whitelisted for mass updating
  5928. # and thus prevent accidentally exposing that which shouldn’t be exposed.
  5929. # Provides two methods for this purpose: #require and #permit. The former is
  5930. # used to mark parameters as required. The latter is used to set the parameter
  5931. # as permitted and limit which attributes should be allowed for mass updating.
  5932. #
  5933. # params = ActionController::Parameters.new({
  5934. # person: {
  5935. # name: 'Francesco',
  5936. # age: 22,
  5937. # role: 'admin'
  5938. # }
  5939. # })
  5940. #
  5941. # permitted = params.require(:person).permit(:name, :age)
  5942. # permitted # => {"name"=>"Francesco", "age"=>22}
  5943. # permitted.class # => ActionController::Parameters
  5944. # permitted.permitted? # => true
  5945. #
  5946. # Person.first.update!(permitted)
  5947. # # => #<Person id: 1, name: "Francesco", age: 22, role: "user">
  5948. #
  5949. # It provides two options that controls the top-level behavior of new instances:
  5950. #
  5951. # * +permit_all_parameters+ - If it's +true+, all the parameters will be
  5952. # permitted by default. The default is +false+.
  5953. # * +action_on_unpermitted_parameters+ - Allow to control the behavior when parameters
  5954. # that are not explicitly permitted are found. The values can be <tt>:log</tt> to
  5955. # write a message on the logger or <tt>:raise</tt> to raise
  5956. # ActionController::UnpermittedParameters exception. The default value is <tt>:log</tt>
  5957. # in test and development environments, +false+ otherwise.
  5958. #
  5959. # params = ActionController::Parameters.new
  5960. # params.permitted? # => false
  5961. #
  5962. # ActionController::Parameters.permit_all_parameters = true
  5963. #
  5964. # params = ActionController::Parameters.new
  5965. # params.permitted? # => true
  5966. #
  5967. # params = ActionController::Parameters.new(a: "123", b: "456")
  5968. # params.permit(:c)
  5969. # # => {}
  5970. #
  5971. # ActionController::Parameters.action_on_unpermitted_parameters = :raise
  5972. #
  5973. # params = ActionController::Parameters.new(a: "123", b: "456")
  5974. # params.permit(:c)
  5975. # # => ActionController::UnpermittedParameters: found unpermitted keys: a, b
  5976. #
  5977. # <tt>ActionController::Parameters</tt> is inherited from
  5978. # <tt>ActiveSupport::HashWithIndifferentAccess</tt>, this means
  5979. # that you can fetch values using either <tt>:key</tt> or <tt>"key"</tt>.
  5980. #
  5981. # params = ActionController::Parameters.new(key: 'value')
  5982. # params[:key] # => "value"
  5983. # params["key"] # => "value"
  5984. class Parameters < ActiveSupport::HashWithIndifferentAccess
  5985. cattr_accessor :permit_all_parameters, instance_accessor: false
  5986. cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
  5987. # Never raise an UnpermittedParameters exception because of these params
  5988. # are present. They are added by Rails and it's of no concern.
  5989. NEVER_UNPERMITTED_PARAMS = %w( controller action )
  5990. # Returns a new instance of <tt>ActionController::Parameters</tt>.
  5991. # Also, sets the +permitted+ attribute to the default value of
  5992. # <tt>ActionController::Parameters.permit_all_parameters</tt>.
  5993. #
  5994. # class Person < ActiveRecord::Base
  5995. # end
  5996. #
  5997. # params = ActionController::Parameters.new(name: 'Francesco')
  5998. # params.permitted? # => false
  5999. # Person.new(params) # => ActiveModel::ForbiddenAttributesError
  6000. #
  6001. # ActionController::Parameters.permit_all_parameters = true
  6002. #
  6003. # params = ActionController::Parameters.new(name: 'Francesco')
  6004. # params.permitted? # => true
  6005. # Person.new(params) # => #<Person id: nil, name: "Francesco">
  6006. def initialize(attributes = nil)
  6007. super(attributes)
  6008. @permitted = self.class.permit_all_parameters
  6009. end
  6010. # Returns +true+ if the parameter is permitted, +false+ otherwise.
  6011. #
  6012. # params = ActionController::Parameters.new
  6013. # params.permitted? # => false
  6014. # params.permit!
  6015. # params.permitted? # => true
  6016. def permitted?
  6017. @permitted
  6018. end
  6019. # Sets the +permitted+ attribute to +true+. This can be used to pass
  6020. # mass assignment. Returns +self+.
  6021. #
  6022. # class Person < ActiveRecord::Base
  6023. # end
  6024. #
  6025. # params = ActionController::Parameters.new(name: 'Francesco')
  6026. # params.permitted? # => false
  6027. # Person.new(params) # => ActiveModel::ForbiddenAttributesError
  6028. # params.permit!
  6029. # params.permitted? # => true
  6030. # Person.new(params) # => #<Person id: nil, name: "Francesco">
  6031. def permit!
  6032. each_pair do |key, value|
  6033. convert_hashes_to_parameters(key, value)
  6034. self[key].permit! if self[key].respond_to? :permit!
  6035. end
  6036. @permitted = true
  6037. self
  6038. end
  6039. # Ensures that a parameter is present. If it's present, returns
  6040. # the parameter at the given +key+, otherwise raises an
  6041. # <tt>ActionController::ParameterMissing</tt> error.
  6042. #
  6043. # ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person)
  6044. # # => {"name"=>"Francesco"}
  6045. #
  6046. # ActionController::Parameters.new(person: nil).require(:person)
  6047. # # => ActionController::ParameterMissing: param not found: person
  6048. #
  6049. # ActionController::Parameters.new(person: {}).require(:person)
  6050. # # => ActionController::ParameterMissing: param not found: person
  6051. def require(key)
  6052. self[key].presence || raise(ParameterMissing.new(key))
  6053. end
  6054. # Alias of #require.
  6055. alias :required :require
  6056. # Returns a new <tt>ActionController::Parameters</tt> instance that
  6057. # includes only the given +filters+ and sets the +permitted+ attribute
  6058. # for the object to +true+. This is useful for limiting which attributes
  6059. # should be allowed for mass updating.
  6060. #
  6061. # params = ActionController::Parameters.new(user: { name: 'Francesco', age: 22, role: 'admin' })
  6062. # permitted = params.require(:user).permit(:name, :age)
  6063. # permitted.permitted? # => true
  6064. # permitted.has_key?(:name) # => true
  6065. # permitted.has_key?(:age) # => true
  6066. # permitted.has_key?(:role) # => false
  6067. #
  6068. # Only permitted scalars pass the filter. For example, given
  6069. #
  6070. # params.permit(:name)
  6071. #
  6072. # +:name+ passes it is a key of +params+ whose associated value is of type
  6073. # +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+,
  6074. # +Date+, +Time+, +DateTime+, +StringIO+, +IO+, or
  6075. # +ActionDispatch::Http::UploadedFile+. Otherwise, the key +:name+ is
  6076. # filtered out.
  6077. #
  6078. # You may declare that the parameter should be an array of permitted scalars
  6079. # by mapping it to an empty array:
  6080. #
  6081. # params.permit(tags: [])
  6082. #
  6083. # You can also use +permit+ on nested parameters, like:
  6084. #
  6085. # params = ActionController::Parameters.new({
  6086. # person: {
  6087. # name: 'Francesco',
  6088. # age: 22,
  6089. # pets: [{
  6090. # name: 'Purplish',
  6091. # category: 'dogs'
  6092. # }]
  6093. # }
  6094. # })
  6095. #
  6096. # permitted = params.permit(person: [ :name, { pets: :name } ])
  6097. # permitted.permitted? # => true
  6098. # permitted[:person][:name] # => "Francesco"
  6099. # permitted[:person][:age] # => nil
  6100. # permitted[:person][:pets][0][:name] # => "Purplish"
  6101. # permitted[:person][:pets][0][:category] # => nil
  6102. #
  6103. # Note that if you use +permit+ in a key that points to a hash,
  6104. # it won't allow all the hash. You also need to specify which
  6105. # attributes inside the hash should be whitelisted.
  6106. #
  6107. # params = ActionController::Parameters.new({
  6108. # person: {
  6109. # contact: {
  6110. # email: 'none@test.com'
  6111. # phone: '555-1234'
  6112. # }
  6113. # }
  6114. # })
  6115. #
  6116. # params.require(:person).permit(:contact)
  6117. # # => {}
  6118. #
  6119. # params.require(:person).permit(contact: :phone)
  6120. # # => {"contact"=>{"phone"=>"555-1234"}}
  6121. #
  6122. # params.require(:person).permit(contact: [ :email, :phone ])
  6123. # # => {"contact"=>{"email"=>"none@test.com", "phone"=>"555-1234"}}
  6124. def permit(*filters)
  6125. params = self.class.new
  6126. filters.flatten.each do |filter|
  6127. case filter
  6128. when Symbol, String
  6129. permitted_scalar_filter(params, filter)
  6130. when Hash then
  6131. hash_filter(params, filter)
  6132. end
  6133. end
  6134. unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters
  6135. params.permit!
  6136. end
  6137. # Returns a parameter for the given +key+. If not found,
  6138. # returns +nil+.
  6139. #
  6140. # params = ActionController::Parameters.new(person: { name: 'Francesco' })
  6141. # params[:person] # => {"name"=>"Francesco"}
  6142. # params[:none] # => nil
  6143. def [](key)
  6144. convert_hashes_to_parameters(key, super)
  6145. end
  6146. # Returns a parameter for the given +key+. If the +key+
  6147. # can't be found, there are several options: With no other arguments,
  6148. # it will raise an <tt>ActionController::ParameterMissing</tt> error;
  6149. # if more arguments are given, then that will be returned; if a block
  6150. # is given, then that will be run and its result returned.
  6151. #
  6152. # params = ActionController::Parameters.new(person: { name: 'Francesco' })
  6153. # params.fetch(:person) # => {"name"=>"Francesco"}
  6154. # params.fetch(:none) # => ActionController::ParameterMissing: param not found: none
  6155. # params.fetch(:none, 'Francesco') # => "Francesco"
  6156. # params.fetch(:none) { 'Francesco' } # => "Francesco"
  6157. def fetch(key, *args)
  6158. convert_hashes_to_parameters(key, super)
  6159. rescue KeyError
  6160. raise ActionController::ParameterMissing.new(key)
  6161. end
  6162. # Returns a new <tt>ActionController::Parameters</tt> instance that
  6163. # includes only the given +keys+. If the given +keys+
  6164. # don't exist, returns an empty hash.
  6165. #
  6166. # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
  6167. # params.slice(:a, :b) # => {"a"=>1, "b"=>2}
  6168. # params.slice(:d) # => {}
  6169. def slice(*keys)
  6170. self.class.new(super).tap do |new_instance|
  6171. new_instance.instance_variable_set :@permitted, @permitted
  6172. end
  6173. end
  6174. # Returns an exact copy of the <tt>ActionController::Parameters</tt>
  6175. # instance. +permitted+ state is kept on the duped object.
  6176. #
  6177. # params = ActionController::Parameters.new(a: 1)
  6178. # params.permit!
  6179. # params.permitted? # => true
  6180. # copy_params = params.dup # => {"a"=>1}
  6181. # copy_params.permitted? # => true
  6182. def dup
  6183. super.tap do |duplicate|
  6184. duplicate.instance_variable_set :@permitted, @permitted
  6185. end
  6186. end
  6187. private
  6188. def convert_hashes_to_parameters(key, value)
  6189. if value.is_a?(Parameters) || !value.is_a?(Hash)
  6190. value
  6191. else
  6192. # Convert to Parameters on first access
  6193. self[key] = self.class.new(value)
  6194. end
  6195. end
  6196. def each_element(object)
  6197. if object.is_a?(Array)
  6198. object.map { |el| yield el }.compact
  6199. elsif object.is_a?(Hash) && object.keys.all? { |k| k =~ /\A-?\d+\z/ }
  6200. hash = object.class.new
  6201. object.each { |k,v| hash[k] = yield v }
  6202. hash
  6203. else
  6204. yield object
  6205. end
  6206. end
  6207. def unpermitted_parameters!(params)
  6208. unpermitted_keys = unpermitted_keys(params)
  6209. if unpermitted_keys.any?
  6210. case self.class.action_on_unpermitted_parameters
  6211. when :log
  6212. ActionController::Base.logger.debug "Unpermitted parameters: #{unpermitted_keys.join(", ")}"
  6213. when :raise
  6214. raise ActionController::UnpermittedParameters.new(unpermitted_keys)
  6215. end
  6216. end
  6217. end
  6218. def unpermitted_keys(params)
  6219. self.keys - params.keys - NEVER_UNPERMITTED_PARAMS
  6220. end
  6221. #
  6222. # --- Filtering ----------------------------------------------------------
  6223. #
  6224. # This is a white list of permitted scalar types that includes the ones
  6225. # supported in XML and JSON requests.
  6226. #
  6227. # This list is in particular used to filter ordinary requests, String goes
  6228. # as first element to quickly short-circuit the common case.
  6229. #
  6230. # If you modify this collection please update the API of +permit+ above.
  6231. PERMITTED_SCALAR_TYPES = [
  6232. String,
  6233. Symbol,
  6234. NilClass,
  6235. Numeric,
  6236. TrueClass,
  6237. FalseClass,
  6238. Date,
  6239. Time,
  6240. # DateTimes are Dates, we document the type but avoid the redundant check.
  6241. StringIO,
  6242. IO,
  6243. ActionDispatch::Http::UploadedFile,
  6244. ]
  6245. def permitted_scalar?(value)
  6246. PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)}
  6247. end
  6248. def permitted_scalar_filter(params, key)
  6249. if has_key?(key) && permitted_scalar?(self[key])
  6250. params[key] = self[key]
  6251. end
  6252. keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k|
  6253. if permitted_scalar?(self[k])
  6254. params[k] = self[k]
  6255. end
  6256. end
  6257. end
  6258. def array_of_permitted_scalars?(value)
  6259. if value.is_a?(Array)
  6260. value.all? {|element| permitted_scalar?(element)}
  6261. end
  6262. end
  6263. def array_of_permitted_scalars_filter(params, key)
  6264. if has_key?(key) && array_of_permitted_scalars?(self[key])
  6265. params[key] = self[key]
  6266. end
  6267. end
  6268. EMPTY_ARRAY = []
  6269. def hash_filter(params, filter)
  6270. filter = filter.with_indifferent_access
  6271. # Slicing filters out non-declared keys.
  6272. slice(*filter.keys).each do |key, value|
  6273. return unless value
  6274. if filter[key] == EMPTY_ARRAY
  6275. # Declaration { comment_ids: [] }.
  6276. array_of_permitted_scalars_filter(params, key)
  6277. else
  6278. # Declaration { user: :name } or { user: [:name, :age, { adress: ... }] }.
  6279. params[key] = each_element(value) do |element|
  6280. if element.is_a?(Hash)
  6281. element = self.class.new(element) unless element.respond_to?(:permit)
  6282. element.permit(*Array.wrap(filter[key]))
  6283. end
  6284. end
  6285. end
  6286. end
  6287. end
  6288. end
  6289. # == Strong \Parameters
  6290. #
  6291. # It provides an interface for protecting attributes from end-user
  6292. # assignment. This makes Action Controller parameters forbidden
  6293. # to be used in Active Model mass assignment until they have been
  6294. # whitelisted.
  6295. #
  6296. # In addition, parameters can be marked as required and flow through a
  6297. # predefined raise/rescue flow to end up as a 400 Bad Request with no
  6298. # effort.
  6299. #
  6300. # class PeopleController < ActionController::Base
  6301. # # Using "Person.create(params[:person])" would raise an
  6302. # # ActiveModel::ForbiddenAttributes exception because it'd
  6303. # # be using mass assignment without an explicit permit step.
  6304. # # This is the recommended form:
  6305. # def create
  6306. # Person.create(person_params)
  6307. # end
  6308. #
  6309. # # This will pass with flying colors as long as there's a person key in the
  6310. # # parameters, otherwise it'll raise an ActionController::MissingParameter
  6311. # # exception, which will get caught by ActionController::Base and turned
  6312. # # into a 400 Bad Request reply.
  6313. # def update
  6314. # redirect_to current_account.people.find(params[:id]).tap { |person|
  6315. # person.update!(person_params)
  6316. # }
  6317. # end
  6318. #
  6319. # private
  6320. # # Using a private method to encapsulate the permissible parameters is
  6321. # # just a good pattern since you'll be able to reuse the same permit
  6322. # # list between create and update. Also, you can specialize this method
  6323. # # with per-user checking of permissible attributes.
  6324. # def person_params
  6325. # params.require(:person).permit(:name, :age)
  6326. # end
  6327. # end
  6328. #
  6329. # In order to use <tt>accepts_nested_attribute_for</tt> with Strong \Parameters, you
  6330. # will need to specify which nested attributes should be whitelisted.
  6331. #
  6332. # class Person
  6333. # has_many :pets
  6334. # accepts_nested_attributes_for :pets
  6335. # end
  6336. #
  6337. # class PeopleController < ActionController::Base
  6338. # def create
  6339. # Person.create(person_params)
  6340. # end
  6341. #
  6342. # ...
  6343. #
  6344. # private
  6345. #
  6346. # def person_params
  6347. # # It's mandatory to specify the nested attributes that should be whitelisted.
  6348. # # If you use `permit` with just the key that points to the nested attributes hash,
  6349. # # it will return an empty hash.
  6350. # params.require(:person).permit(:name, :age, pets_attributes: [ :name, :category ])
  6351. # end
  6352. # end
  6353. #
  6354. # See ActionController::Parameters.require and ActionController::Parameters.permit
  6355. # for more information.
  6356. module StrongParameters
  6357. extend ActiveSupport::Concern
  6358. include ActiveSupport::Rescuable
  6359. # Returns a new ActionController::Parameters object that
  6360. # has been instantiated with the <tt>request.parameters</tt>.
  6361. def params
  6362. @_params ||= Parameters.new(request.parameters)
  6363. end
  6364. # Assigns the given +value+ to the +params+ hash. If +value+
  6365. # is a Hash, this will create an ActionController::Parameters
  6366. # object that has been instantiated with the given +value+ hash.
  6367. def params=(value)
  6368. @_params = value.is_a?(Hash) ? Parameters.new(value) : value
  6369. end
  6370. end
  6371. end
  6372. module ActionController
  6373. module Testing
  6374. extend ActiveSupport::Concern
  6375. include RackDelegation
  6376. # TODO : Rewrite tests using controller.headers= to use Rack env
  6377. def headers=(new_headers)
  6378. @_response ||= ActionDispatch::Response.new
  6379. @_response.headers.replace(new_headers)
  6380. end
  6381. # Behavior specific to functional tests
  6382. module Functional # :nodoc:
  6383. def set_response!(request)
  6384. end
  6385. def recycle!
  6386. @_url_options = nil
  6387. self.response_body = nil
  6388. self.formats = nil
  6389. self.params = nil
  6390. end
  6391. end
  6392. module ClassMethods
  6393. def before_filters
  6394. _process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name}
  6395. end
  6396. end
  6397. end
  6398. end
  6399. module ActionController
  6400. # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
  6401. # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
  6402. #
  6403. # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
  6404. # url options like the +host+. In order to do so, this module requires the host class
  6405. # to implement +env+ and +request+, which need to be a Rack-compatible.
  6406. #
  6407. # class RootUrl
  6408. # include ActionController::UrlFor
  6409. # include Rails.application.routes.url_helpers
  6410. #
  6411. # delegate :env, :request, to: :controller
  6412. #
  6413. # def initialize(controller)
  6414. # @controller = controller
  6415. # @url = root_path # named route from the application.
  6416. # end
  6417. # end
  6418. module UrlFor
  6419. extend ActiveSupport::Concern
  6420. include AbstractController::UrlFor
  6421. def url_options
  6422. @_url_options ||= super.reverse_merge(
  6423. :host => request.host,
  6424. :port => request.optional_port,
  6425. :protocol => request.protocol,
  6426. :_recall => request.symbolized_path_parameters
  6427. ).freeze
  6428. if (same_origin = _routes.equal?(env["action_dispatch.routes"])) ||
  6429. (script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
  6430. (original_script_name = env['SCRIPT_NAME'])
  6431. @_url_options.dup.tap do |options|
  6432. if original_script_name
  6433. options[:original_script_name] = original_script_name
  6434. else
  6435. options[:script_name] = same_origin ? request.script_name.dup : script_name
  6436. end
  6437. options.freeze
  6438. end
  6439. else
  6440. @_url_options
  6441. end
  6442. end
  6443. end
  6444. end
  6445. require 'active_support/core_ext/array/extract_options'
  6446. require 'action_dispatch/middleware/stack'
  6447. module ActionController
  6448. # Extend ActionDispatch middleware stack to make it aware of options
  6449. # allowing the following syntax in controllers:
  6450. #
  6451. # class PostsController < ApplicationController
  6452. # use AuthenticationMiddleware, except: [:index, :show]
  6453. # end
  6454. #
  6455. class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
  6456. class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
  6457. def initialize(klass, *args, &block)
  6458. options = args.extract_options!
  6459. @only = Array(options.delete(:only)).map(&:to_s)
  6460. @except = Array(options.delete(:except)).map(&:to_s)
  6461. args << options unless options.empty?
  6462. super
  6463. end
  6464. def valid?(action)
  6465. if @only.present?
  6466. @only.include?(action)
  6467. elsif @except.present?
  6468. !@except.include?(action)
  6469. else
  6470. true
  6471. end
  6472. end
  6473. end
  6474. def build(action, app=nil, &block)
  6475. app ||= block
  6476. action = action.to_s
  6477. raise "MiddlewareStack#build requires an app" unless app
  6478. middlewares.reverse.inject(app) do |a, middleware|
  6479. middleware.valid?(action) ?
  6480. middleware.build(a) : a
  6481. end
  6482. end
  6483. end
  6484. # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
  6485. # valid Rack interface without the additional niceties provided by
  6486. # <tt>ActionController::Base</tt>.
  6487. #
  6488. # A sample metal controller might look like this:
  6489. #
  6490. # class HelloController < ActionController::Metal
  6491. # def index
  6492. # self.response_body = "Hello World!"
  6493. # end
  6494. # end
  6495. #
  6496. # And then to route requests to your metal controller, you would add
  6497. # something like this to <tt>config/routes.rb</tt>:
  6498. #
  6499. # match 'hello', to: HelloController.action(:index)
  6500. #
  6501. # The +action+ method returns a valid Rack application for the \Rails
  6502. # router to dispatch to.
  6503. #
  6504. # == Rendering Helpers
  6505. #
  6506. # <tt>ActionController::Metal</tt> by default provides no utilities for rendering
  6507. # views, partials, or other responses aside from explicitly calling of
  6508. # <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
  6509. # add the render helpers you're used to having in a normal controller, you
  6510. # can do the following:
  6511. #
  6512. # class HelloController < ActionController::Metal
  6513. # include ActionController::Rendering
  6514. # append_view_path "#{Rails.root}/app/views"
  6515. #
  6516. # def index
  6517. # render "hello/index"
  6518. # end
  6519. # end
  6520. #
  6521. # == Redirection Helpers
  6522. #
  6523. # To add redirection helpers to your metal controller, do the following:
  6524. #
  6525. # class HelloController < ActionController::Metal
  6526. # include ActionController::Redirecting
  6527. # include Rails.application.routes.url_helpers
  6528. #
  6529. # def index
  6530. # redirect_to root_url
  6531. # end
  6532. # end
  6533. #
  6534. # == Other Helpers
  6535. #
  6536. # You can refer to the modules included in <tt>ActionController::Base</tt> to see
  6537. # other features you can bring into your metal controller.
  6538. #
  6539. class Metal < AbstractController::Base
  6540. abstract!
  6541. attr_internal_writer :env
  6542. def env
  6543. @_env ||= {}
  6544. end
  6545. # Returns the last part of the controller's name, underscored, without the ending
  6546. # <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
  6547. # Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
  6548. #
  6549. # ==== Returns
  6550. # * <tt>string</tt>
  6551. def self.controller_name
  6552. @controller_name ||= name.demodulize.sub(/Controller$/, '').underscore
  6553. end
  6554. # Delegates to the class' <tt>controller_name</tt>
  6555. def controller_name
  6556. self.class.controller_name
  6557. end
  6558. # The details below can be overridden to support a specific
  6559. # Request and Response object. The default ActionController::Base
  6560. # implementation includes RackDelegation, which makes a request
  6561. # and response object available. You might wish to control the
  6562. # environment and response manually for performance reasons.
  6563. attr_internal :headers, :response, :request
  6564. delegate :session, :to => "@_request"
  6565. def initialize
  6566. @_headers = {"Content-Type" => "text/html"}
  6567. @_status = 200
  6568. @_request = nil
  6569. @_response = nil
  6570. @_routes = nil
  6571. super
  6572. end
  6573. def params
  6574. @_params ||= request.parameters
  6575. end
  6576. def params=(val)
  6577. @_params = val
  6578. end
  6579. # Basic implementations for content_type=, location=, and headers are
  6580. # provided to reduce the dependency on the RackDelegation module
  6581. # in Renderer and Redirector.
  6582. def content_type=(type)
  6583. headers["Content-Type"] = type.to_s
  6584. end
  6585. def content_type
  6586. headers["Content-Type"]
  6587. end
  6588. def location
  6589. headers["Location"]
  6590. end
  6591. def location=(url)
  6592. headers["Location"] = url
  6593. end
  6594. # basic url_for that can be overridden for more robust functionality
  6595. def url_for(string)
  6596. string
  6597. end
  6598. def status
  6599. @_status
  6600. end
  6601. def status=(status)
  6602. @_status = Rack::Utils.status_code(status)
  6603. end
  6604. def response_body=(body)
  6605. body = [body] unless body.nil? || body.respond_to?(:each)
  6606. super
  6607. end
  6608. def performed?
  6609. response_body || (response && response.committed?)
  6610. end
  6611. def dispatch(name, request) #:nodoc:
  6612. @_request = request
  6613. @_env = request.env
  6614. @_env['action_controller.instance'] = self
  6615. process(name)
  6616. to_a
  6617. end
  6618. def to_a #:nodoc:
  6619. response ? response.to_a : [status, headers, response_body]
  6620. end
  6621. class_attribute :middleware_stack
  6622. self.middleware_stack = ActionController::MiddlewareStack.new
  6623. def self.inherited(base) # :nodoc:
  6624. base.middleware_stack = middleware_stack.dup
  6625. super
  6626. end
  6627. # Pushes the given Rack middleware and its arguments to the bottom of the
  6628. # middleware stack.
  6629. def self.use(*args, &block)
  6630. middleware_stack.use(*args, &block)
  6631. end
  6632. # Alias for +middleware_stack+.
  6633. def self.middleware
  6634. middleware_stack
  6635. end
  6636. # Makes the controller a Rack endpoint that runs the action in the given
  6637. # +env+'s +action_dispatch.request.path_parameters+ key.
  6638. def self.call(env)
  6639. action(env['action_dispatch.request.path_parameters'][:action]).call(env)
  6640. end
  6641. # Returns a Rack endpoint for the given action name.
  6642. def self.action(name, klass = ActionDispatch::Request)
  6643. middleware_stack.build(name.to_s) do |env|
  6644. new.dispatch(name, klass.new(env))
  6645. end
  6646. end
  6647. end
  6648. end
  6649. module ActionController
  6650. class Middleware < Metal
  6651. class ActionMiddleware
  6652. def initialize(controller, app)
  6653. @controller, @app = controller, app
  6654. end
  6655. def call(env)
  6656. request = ActionDispatch::Request.new(env)
  6657. @controller.build(@app).dispatch(:index, request)
  6658. end
  6659. end
  6660. class << self
  6661. alias build new
  6662. def new(app)
  6663. ActionMiddleware.new(self, app)
  6664. end
  6665. end
  6666. attr_internal :app
  6667. def process(action)
  6668. response = super
  6669. self.status, self.headers, self.response_body = response if response.is_a?(Array)
  6670. response
  6671. end
  6672. def initialize(app)
  6673. super()
  6674. @_app = app
  6675. end
  6676. def index
  6677. call(env)
  6678. end
  6679. end
  6680. endmodule ActionController
  6681. module ModelNaming
  6682. # Converts the given object to an ActiveModel compliant one.
  6683. def convert_to_model(object)
  6684. object.respond_to?(:to_model) ? object.to_model : object
  6685. end
  6686. def model_name_from_record_or_class(record_or_class)
  6687. (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
  6688. end
  6689. end
  6690. end
  6691. require "rails"
  6692. require "action_controller"
  6693. require "action_dispatch/railtie"
  6694. require "action_view/railtie"
  6695. require "abstract_controller/railties/routes_helpers"
  6696. require "action_controller/railties/helpers"
  6697. module ActionController
  6698. class Railtie < Rails::Railtie #:nodoc:
  6699. config.action_controller = ActiveSupport::OrderedOptions.new
  6700. config.eager_load_namespaces << ActionController
  6701. initializer "action_controller.assets_config", :group => :all do |app|
  6702. app.config.action_controller.assets_dir ||= app.config.paths["public"].first
  6703. end
  6704. initializer "action_controller.set_helpers_path" do |app|
  6705. ActionController::Helpers.helpers_path = app.helpers_paths
  6706. end
  6707. initializer "action_controller.parameters_config" do |app|
  6708. options = app.config.action_controller
  6709. ActionController::Parameters.permit_all_parameters = options.delete(:permit_all_parameters) { false }
  6710. ActionController::Parameters.action_on_unpermitted_parameters = options.delete(:action_on_unpermitted_parameters) do
  6711. (Rails.env.test? || Rails.env.development?) ? :log : false
  6712. end
  6713. end
  6714. initializer "action_controller.set_configs" do |app|
  6715. paths = app.config.paths
  6716. options = app.config.action_controller
  6717. options.logger ||= Rails.logger
  6718. options.cache_store ||= Rails.cache
  6719. options.javascripts_dir ||= paths["public/javascripts"].first
  6720. options.stylesheets_dir ||= paths["public/stylesheets"].first
  6721. # Ensure readers methods get compiled
  6722. options.asset_host ||= app.config.asset_host
  6723. options.relative_url_root ||= app.config.relative_url_root
  6724. ActiveSupport.on_load(:action_controller) do
  6725. include app.routes.mounted_helpers
  6726. extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
  6727. extend ::ActionController::Railties::Helpers
  6728. options.each do |k,v|
  6729. k = "#{k}="
  6730. if respond_to?(k)
  6731. send(k, v)
  6732. elsif !Base.respond_to?(k)
  6733. raise "Invalid option key: #{k}"
  6734. end
  6735. end
  6736. end
  6737. end
  6738. initializer "action_controller.compile_config_methods" do
  6739. ActiveSupport.on_load(:action_controller) do
  6740. config.compile_methods! if config.respond_to?(:compile_methods!)
  6741. end
  6742. end
  6743. end
  6744. end
  6745. module ActionController
  6746. module Railties
  6747. module Helpers
  6748. def inherited(klass)
  6749. super
  6750. return unless klass.respond_to?(:helpers_path=)
  6751. if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
  6752. paths = namespace.railtie_helpers_paths
  6753. else
  6754. paths = ActionController::Helpers.helpers_path
  6755. end
  6756. klass.helpers_path = paths
  6757. if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
  6758. klass.helper :all
  6759. end
  6760. end
  6761. end
  6762. end
  6763. end
  6764. require 'action_view/record_identifier'
  6765. module ActionController
  6766. module RecordIdentifier
  6767. MODULE_MESSAGE = 'Calling ActionController::RecordIdentifier.%s is deprecated and ' \
  6768. 'will be removed in Rails 4.1, please call using ActionView::RecordIdentifier instead.'
  6769. INSTANCE_MESSAGE = '%s method will no longer be included by default in controllers ' \
  6770. 'since Rails 4.1. If you would like to use it in controllers, please include ' \
  6771. 'ActionView::RecordIdentifier module.'
  6772. def dom_id(record, prefix = nil)
  6773. ActiveSupport::Deprecation.warn(INSTANCE_MESSAGE % 'dom_id')
  6774. ActionView::RecordIdentifier.dom_id(record, prefix)
  6775. end
  6776. def dom_class(record, prefix = nil)
  6777. ActiveSupport::Deprecation.warn(INSTANCE_MESSAGE % 'dom_class')
  6778. ActionView::RecordIdentifier.dom_class(record, prefix)
  6779. end
  6780. def self.dom_id(record, prefix = nil)
  6781. ActiveSupport::Deprecation.warn(MODULE_MESSAGE % 'dom_id')
  6782. ActionView::RecordIdentifier.dom_id(record, prefix)
  6783. end
  6784. def self.dom_class(record, prefix = nil)
  6785. ActiveSupport::Deprecation.warn(MODULE_MESSAGE % 'dom_class')
  6786. ActionView::RecordIdentifier.dom_class(record, prefix)
  6787. end
  6788. end
  6789. end
  6790. require 'rack/session/abstract/id'
  6791. require 'active_support/core_ext/object/to_query'
  6792. require 'active_support/core_ext/module/anonymous'
  6793. require 'active_support/core_ext/hash/keys'
  6794. module ActionController
  6795. module TemplateAssertions
  6796. extend ActiveSupport::Concern
  6797. included do
  6798. setup :setup_subscriptions
  6799. teardown :teardown_subscriptions
  6800. end
  6801. def setup_subscriptions
  6802. @_partials = Hash.new(0)
  6803. @_templates = Hash.new(0)
  6804. @_layouts = Hash.new(0)
  6805. ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
  6806. path = payload[:layout]
  6807. if path
  6808. @_layouts[path] += 1
  6809. if path =~ /^layouts\/(.*)/
  6810. @_layouts[$1] += 1
  6811. end
  6812. end
  6813. end
  6814. ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload|
  6815. path = payload[:virtual_path]
  6816. next unless path
  6817. partial = path =~ /^.*\/_[^\/]*$/
  6818. if partial
  6819. @_partials[path] += 1
  6820. @_partials[path.split("/").last] += 1
  6821. end
  6822. @_templates[path] += 1
  6823. end
  6824. end
  6825. def teardown_subscriptions
  6826. ActiveSupport::Notifications.unsubscribe("render_template.action_view")
  6827. ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
  6828. end
  6829. def process(*args)
  6830. @_partials = Hash.new(0)
  6831. @_templates = Hash.new(0)
  6832. @_layouts = Hash.new(0)
  6833. super
  6834. end
  6835. # Asserts that the request was rendered with the appropriate template file or partials.
  6836. #
  6837. # # assert that the "new" view template was rendered
  6838. # assert_template "new"
  6839. #
  6840. # # assert that the exact template "admin/posts/new" was rendered
  6841. # assert_template %r{\Aadmin/posts/new\Z}
  6842. #
  6843. # # assert that the layout 'admin' was rendered
  6844. # assert_template layout: 'admin'
  6845. # assert_template layout: 'layouts/admin'
  6846. # assert_template layout: :admin
  6847. #
  6848. # # assert that no layout was rendered
  6849. # assert_template layout: nil
  6850. # assert_template layout: false
  6851. #
  6852. # # assert that the "_customer" partial was rendered twice
  6853. # assert_template partial: '_customer', count: 2
  6854. #
  6855. # # assert that no partials were rendered
  6856. # assert_template partial: false
  6857. #
  6858. # In a view test case, you can also assert that specific locals are passed
  6859. # to partials:
  6860. #
  6861. # # assert that the "_customer" partial was rendered with a specific object
  6862. # assert_template partial: '_customer', locals: { customer: @customer }
  6863. def assert_template(options = {}, message = nil)
  6864. # Force body to be read in case the template is being streamed.
  6865. response.body
  6866. case options
  6867. when NilClass, Regexp, String, Symbol
  6868. options = options.to_s if Symbol === options
  6869. rendered = @_templates
  6870. msg = message || sprintf("expecting <%s> but rendering with <%s>",
  6871. options.inspect, rendered.keys)
  6872. matches_template =
  6873. case options
  6874. when String
  6875. !options.empty? && rendered.any? do |t, num|
  6876. options_splited = options.split(File::SEPARATOR)
  6877. t_splited = t.split(File::SEPARATOR)
  6878. t_splited.last(options_splited.size) == options_splited
  6879. end
  6880. when Regexp
  6881. rendered.any? { |t,num| t.match(options) }
  6882. when NilClass
  6883. rendered.blank?
  6884. end
  6885. assert matches_template, msg
  6886. when Hash
  6887. options.assert_valid_keys(:layout, :partial, :locals, :count)
  6888. if options.key?(:layout)
  6889. expected_layout = options[:layout]
  6890. msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
  6891. expected_layout, @_layouts.keys)
  6892. case expected_layout
  6893. when String, Symbol
  6894. assert_includes @_layouts.keys, expected_layout.to_s, msg
  6895. when Regexp
  6896. assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
  6897. when nil, false
  6898. assert(@_layouts.empty?, msg)
  6899. end
  6900. end
  6901. if expected_partial = options[:partial]
  6902. if expected_locals = options[:locals]
  6903. if defined?(@_rendered_views)
  6904. view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/')
  6905. partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view
  6906. assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg
  6907. msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial,
  6908. expected_locals,
  6909. @_rendered_views.locals_for(view)]
  6910. assert(@_rendered_views.view_rendered?(view, options[:locals]), msg)
  6911. else
  6912. warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
  6913. end
  6914. elsif expected_count = options[:count]
  6915. actual_count = @_partials[expected_partial]
  6916. msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
  6917. expected_partial, expected_count, actual_count)
  6918. assert(actual_count == expected_count.to_i, msg)
  6919. else
  6920. msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
  6921. options[:partial], @_partials.keys)
  6922. assert_includes @_partials, expected_partial, msg
  6923. end
  6924. elsif options.key?(:partial)
  6925. assert @_partials.empty?,
  6926. "Expected no partials to be rendered"
  6927. end
  6928. else
  6929. raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
  6930. end
  6931. end
  6932. end
  6933. class TestRequest < ActionDispatch::TestRequest #:nodoc:
  6934. DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
  6935. DEFAULT_ENV.delete 'PATH_INFO'
  6936. def initialize(env = {})
  6937. super
  6938. self.session = TestSession.new
  6939. self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
  6940. end
  6941. def assign_parameters(routes, controller_path, action, parameters = {})
  6942. parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
  6943. extra_keys = routes.extra_keys(parameters)
  6944. non_path_parameters = get? ? query_parameters : request_parameters
  6945. parameters.each do |key, value|
  6946. if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
  6947. value = value.map{ |v| v.duplicable? ? v.dup : v }
  6948. elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
  6949. value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
  6950. elsif value.frozen? && value.duplicable?
  6951. value = value.dup
  6952. end
  6953. if extra_keys.include?(key.to_sym)
  6954. non_path_parameters[key] = value
  6955. else
  6956. if value.is_a?(Array)
  6957. value = value.map(&:to_param)
  6958. else
  6959. value = value.to_param
  6960. end
  6961. path_parameters[key.to_s] = value
  6962. end
  6963. end
  6964. # Clear the combined params hash in case it was already referenced.
  6965. @env.delete("action_dispatch.request.parameters")
  6966. params = self.request_parameters.dup
  6967. %w(controller action only_path).each do |k|
  6968. params.delete(k)
  6969. params.delete(k.to_sym)
  6970. end
  6971. data = params.to_query
  6972. @env['CONTENT_LENGTH'] = data.length.to_s
  6973. @env['rack.input'] = StringIO.new(data)
  6974. end
  6975. def recycle!
  6976. @formats = nil
  6977. @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
  6978. @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
  6979. @symbolized_path_params = nil
  6980. @method = @request_method = nil
  6981. @fullpath = @ip = @remote_ip = @protocol = nil
  6982. @env['action_dispatch.request.query_parameters'] = {}
  6983. @set_cookies ||= {}
  6984. @set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }])
  6985. deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies")
  6986. @set_cookies.reject!{ |k,v| deleted_cookies.include?(k) }
  6987. cookie_jar.update(rack_cookies)
  6988. cookie_jar.update(cookies)
  6989. cookie_jar.update(@set_cookies)
  6990. cookie_jar.recycle!
  6991. end
  6992. private
  6993. def default_env
  6994. DEFAULT_ENV
  6995. end
  6996. end
  6997. class TestResponse < ActionDispatch::TestResponse
  6998. def recycle!
  6999. initialize
  7000. end
  7001. end
  7002. # Methods #destroy and #load! are overridden to avoid calling methods on the
  7003. # @store object, which does not exist for the TestSession class.
  7004. class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
  7005. DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
  7006. def initialize(session = {})
  7007. super(nil, nil)
  7008. @id = SecureRandom.hex(16)
  7009. @data = stringify_keys(session)
  7010. @loaded = true
  7011. end
  7012. def exists?
  7013. true
  7014. end
  7015. def keys
  7016. @data.keys
  7017. end
  7018. def values
  7019. @data.values
  7020. end
  7021. def destroy
  7022. clear
  7023. end
  7024. private
  7025. def load!
  7026. @id
  7027. end
  7028. end
  7029. # Superclass for ActionController functional tests. Functional tests allow you to
  7030. # test a single controller action per test method. This should not be confused with
  7031. # integration tests (see ActionDispatch::IntegrationTest), which are more like
  7032. # "stories" that can involve multiple controllers and multiple actions (i.e. multiple
  7033. # different HTTP requests).
  7034. #
  7035. # == Basic example
  7036. #
  7037. # Functional tests are written as follows:
  7038. # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
  7039. # an HTTP request.
  7040. # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
  7041. # the controller's HTTP response, the database contents, etc.
  7042. #
  7043. # For example:
  7044. #
  7045. # class BooksControllerTest < ActionController::TestCase
  7046. # def test_create
  7047. # # Simulate a POST response with the given HTTP parameters.
  7048. # post(:create, book: { title: "Love Hina" })
  7049. #
  7050. # # Assert that the controller tried to redirect us to
  7051. # # the created book's URI.
  7052. # assert_response :found
  7053. #
  7054. # # Assert that the controller really put the book in the database.
  7055. # assert_not_nil Book.find_by_title("Love Hina")
  7056. # end
  7057. # end
  7058. #
  7059. # You can also send a real document in the simulated HTTP request.
  7060. #
  7061. # def test_create
  7062. # json = {book: { title: "Love Hina" }}.to_json
  7063. # post :create, json
  7064. # end
  7065. #
  7066. # == Special instance variables
  7067. #
  7068. # ActionController::TestCase will also automatically provide the following instance
  7069. # variables for use in the tests:
  7070. #
  7071. # <b>@controller</b>::
  7072. # The controller instance that will be tested.
  7073. # <b>@request</b>::
  7074. # An ActionController::TestRequest, representing the current HTTP
  7075. # request. You can modify this object before sending the HTTP request. For example,
  7076. # you might want to set some session properties before sending a GET request.
  7077. # <b>@response</b>::
  7078. # An ActionController::TestResponse object, representing the response
  7079. # of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
  7080. # after calling +post+. If the various assert methods are not sufficient, then you
  7081. # may use this object to inspect the HTTP response in detail.
  7082. #
  7083. # (Earlier versions of \Rails required each functional test to subclass
  7084. # Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
  7085. #
  7086. # == Controller is automatically inferred
  7087. #
  7088. # ActionController::TestCase will automatically infer the controller under test
  7089. # from the test class name. If the controller cannot be inferred from the test
  7090. # class name, you can explicitly set it with +tests+.
  7091. #
  7092. # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
  7093. # tests WidgetController
  7094. # end
  7095. #
  7096. # == \Testing controller internals
  7097. #
  7098. # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
  7099. # can be used against. These collections are:
  7100. #
  7101. # * assigns: Instance variables assigned in the action that are available for the view.
  7102. # * session: Objects being saved in the session.
  7103. # * flash: The flash objects currently in the session.
  7104. # * cookies: \Cookies being sent to the user on this request.
  7105. #
  7106. # These collections can be used just like any other hash:
  7107. #
  7108. # assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
  7109. # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
  7110. # assert flash.empty? # makes sure that there's nothing in the flash
  7111. #
  7112. # For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To
  7113. # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
  7114. # So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
  7115. #
  7116. # On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
  7117. #
  7118. # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
  7119. # action call which can then be asserted against.
  7120. #
  7121. # == Manipulating session and cookie variables
  7122. #
  7123. # Sometimes you need to set up the session and cookie variables for a test.
  7124. # To do this just assign a value to the session or cookie collection:
  7125. #
  7126. # session[:key] = "value"
  7127. # cookies[:key] = "value"
  7128. #
  7129. # To clear the cookies for a test just clear the cookie collection:
  7130. #
  7131. # cookies.clear
  7132. #
  7133. # == \Testing named routes
  7134. #
  7135. # If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
  7136. #
  7137. # assert_redirected_to page_url(title: 'foo')
  7138. class TestCase < ActiveSupport::TestCase
  7139. module Behavior
  7140. extend ActiveSupport::Concern
  7141. include ActionDispatch::TestProcess
  7142. include ActiveSupport::Testing::ConstantLookup
  7143. attr_reader :response, :request
  7144. module ClassMethods
  7145. # Sets the controller class name. Useful if the name can't be inferred from test class.
  7146. # Normalizes +controller_class+ before using.
  7147. #
  7148. # tests WidgetController
  7149. # tests :widget
  7150. # tests 'widget'
  7151. def tests(controller_class)
  7152. case controller_class
  7153. when String, Symbol
  7154. self.controller_class = "#{controller_class.to_s.camelize}Controller".constantize
  7155. when Class
  7156. self.controller_class = controller_class
  7157. else
  7158. raise ArgumentError, "controller class must be a String, Symbol, or Class"
  7159. end
  7160. end
  7161. def controller_class=(new_class)
  7162. prepare_controller_class(new_class) if new_class
  7163. self._controller_class = new_class
  7164. end
  7165. def controller_class
  7166. if current_controller_class = self._controller_class
  7167. current_controller_class
  7168. else
  7169. self.controller_class = determine_default_controller_class(name)
  7170. end
  7171. end
  7172. def determine_default_controller_class(name)
  7173. determine_constant_from_test_name(name) do |constant|
  7174. Class === constant && constant < ActionController::Metal
  7175. end
  7176. end
  7177. def prepare_controller_class(new_class)
  7178. new_class.send :include, ActionController::TestCase::RaiseActionExceptions
  7179. end
  7180. end
  7181. # Executes a request simulating GET HTTP method and set/volley the response
  7182. def get(action, *args)
  7183. process(action, "GET", *args)
  7184. end
  7185. # Executes a request simulating POST HTTP method and set/volley the response
  7186. def post(action, *args)
  7187. process(action, "POST", *args)
  7188. end
  7189. # Executes a request simulating PATCH HTTP method and set/volley the response
  7190. def patch(action, *args)
  7191. process(action, "PATCH", *args)
  7192. end
  7193. # Executes a request simulating PUT HTTP method and set/volley the response
  7194. def put(action, *args)
  7195. process(action, "PUT", *args)
  7196. end
  7197. # Executes a request simulating DELETE HTTP method and set/volley the response
  7198. def delete(action, *args)
  7199. process(action, "DELETE", *args)
  7200. end
  7201. # Executes a request simulating HEAD HTTP method and set/volley the response
  7202. def head(action, *args)
  7203. process(action, "HEAD", *args)
  7204. end
  7205. # Executes a request simulating OPTIONS HTTP method and set/volley the response
  7206. def options(action, *args)
  7207. process(action, "OPTIONS", *args)
  7208. end
  7209. def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
  7210. @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
  7211. @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
  7212. __send__(request_method, action, parameters, session, flash).tap do
  7213. @request.env.delete 'HTTP_X_REQUESTED_WITH'
  7214. @request.env.delete 'HTTP_ACCEPT'
  7215. end
  7216. end
  7217. alias xhr :xml_http_request
  7218. def paramify_values(hash_or_array_or_value)
  7219. case hash_or_array_or_value
  7220. when Hash
  7221. Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
  7222. when Array
  7223. hash_or_array_or_value.map {|i| paramify_values(i)}
  7224. when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
  7225. hash_or_array_or_value
  7226. else
  7227. hash_or_array_or_value.to_param
  7228. end
  7229. end
  7230. def process(action, http_method = 'GET', *args)
  7231. check_required_ivars
  7232. http_method, args = handle_old_process_api(http_method, args, caller)
  7233. if args.first.is_a?(String) && http_method != 'HEAD'
  7234. @request.env['RAW_POST_DATA'] = args.shift
  7235. end
  7236. parameters, session, flash = args
  7237. # Ensure that numbers and symbols passed as params are converted to
  7238. # proper params, as is the case when engaging rack.
  7239. parameters = paramify_values(parameters) if html_format?(parameters)
  7240. @html_document = nil
  7241. unless @controller.respond_to?(:recycle!)
  7242. @controller.extend(Testing::Functional)
  7243. @controller.class.class_eval { include Testing }
  7244. end
  7245. @request.recycle!
  7246. @response.recycle!
  7247. @controller.recycle!
  7248. @request.env['REQUEST_METHOD'] = http_method
  7249. parameters ||= {}
  7250. controller_class_name = @controller.class.anonymous? ?
  7251. "anonymous" :
  7252. @controller.class.name.underscore.sub(/_controller$/, '')
  7253. @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
  7254. @request.session.update(session) if session
  7255. @request.flash.update(flash || {})
  7256. @controller.request = @request
  7257. @controller.response = @response
  7258. build_request_uri(action, parameters)
  7259. name = @request.parameters[:action]
  7260. @controller.process(name)
  7261. if cookies = @request.env['action_dispatch.cookies']
  7262. cookies.write(@response)
  7263. end
  7264. @response.prepare!
  7265. @assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
  7266. @request.session['flash'] = @request.flash.to_session_value
  7267. @request.session.delete('flash') if @request.session['flash'].blank?
  7268. @response
  7269. end
  7270. def setup_controller_request_and_response
  7271. @request = build_request
  7272. @response = build_response
  7273. @response.request = @request
  7274. @controller = nil unless defined? @controller
  7275. if klass = self.class.controller_class
  7276. unless @controller
  7277. begin
  7278. @controller = klass.new
  7279. rescue
  7280. warn "could not construct controller #{klass}" if $VERBOSE
  7281. end
  7282. end
  7283. end
  7284. if @controller
  7285. @controller.request = @request
  7286. @controller.params = {}
  7287. end
  7288. end
  7289. def build_request
  7290. TestRequest.new
  7291. end
  7292. def build_response
  7293. TestResponse.new
  7294. end
  7295. included do
  7296. include ActionController::TemplateAssertions
  7297. include ActionDispatch::Assertions
  7298. class_attribute :_controller_class
  7299. setup :setup_controller_request_and_response
  7300. end
  7301. private
  7302. def check_required_ivars
  7303. # Sanity check for required instance variables so we can give an
  7304. # understandable error message.
  7305. [:@routes, :@controller, :@request, :@response].each do |iv_name|
  7306. if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
  7307. raise "#{iv_name} is nil: make sure you set it in your test's setup method."
  7308. end
  7309. end
  7310. end
  7311. def handle_old_process_api(http_method, args, callstack)
  7312. # 4.0: Remove this method.
  7313. if http_method.is_a?(Hash)
  7314. ActiveSupport::Deprecation.warn("TestCase#process now expects the HTTP method as second argument: process(action, http_method, params, session, flash)", callstack)
  7315. args.unshift(http_method)
  7316. http_method = args.last.is_a?(String) ? args.last : "GET"
  7317. end
  7318. [http_method, args]
  7319. end
  7320. def build_request_uri(action, parameters)
  7321. unless @request.env["PATH_INFO"]
  7322. options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
  7323. options.update(
  7324. :only_path => true,
  7325. :action => action,
  7326. :relative_url_root => nil,
  7327. :_recall => @request.symbolized_path_parameters)
  7328. url, query_string = @routes.url_for(options).split("?", 2)
  7329. @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
  7330. @request.env["PATH_INFO"] = url
  7331. @request.env["QUERY_STRING"] = query_string || ""
  7332. end
  7333. end
  7334. def html_format?(parameters)
  7335. return true unless parameters.is_a?(Hash)
  7336. Mime.fetch(parameters[:format]) { Mime['html'] }.html?
  7337. end
  7338. end
  7339. # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
  7340. # (skipping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
  7341. # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
  7342. # than 0.0.0.0.
  7343. #
  7344. # The exception is stored in the exception accessor for further inspection.
  7345. module RaiseActionExceptions
  7346. def self.included(base) #:nodoc:
  7347. unless base.method_defined?(:exception) && base.method_defined?(:exception=)
  7348. base.class_eval do
  7349. attr_accessor :exception
  7350. protected :exception, :exception=
  7351. end
  7352. end
  7353. end
  7354. protected
  7355. def rescue_action_without_handler(e)
  7356. self.exception = e
  7357. if request.remote_addr == "0.0.0.0"
  7358. raise(e)
  7359. else
  7360. super(e)
  7361. end
  7362. end
  7363. end
  7364. include Behavior
  7365. end
  7366. end
  7367. require 'action_view/vendor/html-scanner'
  7368. require 'active_support/deprecation'
  7369. ActiveSupport::Deprecation.warn 'Vendored html-scanner was moved to action_view, please require "action_view/vendor/html-scanner" instead. ' +
  7370. 'This file will be removed in Rails 4.1'
  7371. require 'active_support/rails'
  7372. require 'abstract_controller'
  7373. require 'action_dispatch'
  7374. require 'action_controller/metal/live'
  7375. require 'action_controller/metal/strong_parameters'
  7376. module ActionController
  7377. extend ActiveSupport::Autoload
  7378. autoload :Base
  7379. autoload :Caching
  7380. autoload :Metal
  7381. autoload :Middleware
  7382. autoload_under "metal" do
  7383. autoload :Compatibility
  7384. autoload :ConditionalGet
  7385. autoload :Cookies
  7386. autoload :DataStreaming
  7387. autoload :Flash
  7388. autoload :ForceSSL
  7389. autoload :Head
  7390. autoload :Helpers
  7391. autoload :HideActions
  7392. autoload :HttpAuthentication
  7393. autoload :ImplicitRender
  7394. autoload :Instrumentation
  7395. autoload :MimeResponds
  7396. autoload :ParamsWrapper
  7397. autoload :RackDelegation
  7398. autoload :Redirecting
  7399. autoload :Renderers
  7400. autoload :Rendering
  7401. autoload :RequestForgeryProtection
  7402. autoload :Rescue
  7403. autoload :Responder
  7404. autoload :Streaming
  7405. autoload :StrongParameters
  7406. autoload :Testing
  7407. autoload :UrlFor
  7408. end
  7409. autoload :Integration, 'action_controller/deprecated/integration_test'
  7410. autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
  7411. autoload :Routing, 'action_controller/deprecated'
  7412. autoload :TestCase, 'action_controller/test_case'
  7413. autoload :TemplateAssertions, 'action_controller/test_case'
  7414. eager_autoload do
  7415. autoload :RecordIdentifier
  7416. end
  7417. def self.eager_load!
  7418. super
  7419. ActionController::Caching.eager_load!
  7420. HTML.eager_load!
  7421. end
  7422. end
  7423. # All of these simply register additional autoloads
  7424. require 'action_view'
  7425. require 'action_view/vendor/html-scanner'
  7426. ActiveSupport.on_load(:action_view) do
  7427. ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
  7428. end
  7429. # Common Active Support usage in Action Controller
  7430. require 'active_support/core_ext/class/attribute_accessors'
  7431. require 'active_support/core_ext/load_error'
  7432. require 'active_support/core_ext/module/attr_internal'
  7433. require 'active_support/core_ext/name_error'
  7434. require 'active_support/core_ext/uri'
  7435. require 'active_support/inflector'
  7436. module ActionDispatch
  7437. module Http
  7438. module Cache
  7439. module Request
  7440. HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
  7441. HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
  7442. def if_modified_since
  7443. if since = env[HTTP_IF_MODIFIED_SINCE]
  7444. Time.rfc2822(since) rescue nil
  7445. end
  7446. end
  7447. def if_none_match
  7448. env[HTTP_IF_NONE_MATCH]
  7449. end
  7450. def if_none_match_etags
  7451. (if_none_match ? if_none_match.split(/\s*,\s*/) : []).collect do |etag|
  7452. etag.gsub(/^\"|\"$/, "")
  7453. end
  7454. end
  7455. def not_modified?(modified_at)
  7456. if_modified_since && modified_at && if_modified_since >= modified_at
  7457. end
  7458. def etag_matches?(etag)
  7459. if etag
  7460. etag = etag.gsub(/^\"|\"$/, "")
  7461. if_none_match_etags.include?(etag)
  7462. end
  7463. end
  7464. # Check response freshness (Last-Modified and ETag) against request
  7465. # If-Modified-Since and If-None-Match conditions. If both headers are
  7466. # supplied, both must match, or the request is not considered fresh.
  7467. def fresh?(response)
  7468. last_modified = if_modified_since
  7469. etag = if_none_match
  7470. return false unless last_modified || etag
  7471. success = true
  7472. success &&= not_modified?(response.last_modified) if last_modified
  7473. success &&= etag_matches?(response.etag) if etag
  7474. success
  7475. end
  7476. end
  7477. module Response
  7478. attr_reader :cache_control, :etag
  7479. alias :etag? :etag
  7480. def last_modified
  7481. if last = headers[LAST_MODIFIED]
  7482. Time.httpdate(last)
  7483. end
  7484. end
  7485. def last_modified?
  7486. headers.include?(LAST_MODIFIED)
  7487. end
  7488. def last_modified=(utc_time)
  7489. headers[LAST_MODIFIED] = utc_time.httpdate
  7490. end
  7491. def date
  7492. if date_header = headers['Date']
  7493. Time.httpdate(date_header)
  7494. end
  7495. end
  7496. def date?
  7497. headers.include?('Date')
  7498. end
  7499. def date=(utc_time)
  7500. headers['Date'] = utc_time.httpdate
  7501. end
  7502. def etag=(etag)
  7503. key = ActiveSupport::Cache.expand_cache_key(etag)
  7504. @etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}")
  7505. end
  7506. private
  7507. LAST_MODIFIED = "Last-Modified".freeze
  7508. ETAG = "ETag".freeze
  7509. CACHE_CONTROL = "Cache-Control".freeze
  7510. SPESHUL_KEYS = %w[extras no-cache max-age public must-revalidate]
  7511. def cache_control_segments
  7512. if cache_control = self[CACHE_CONTROL]
  7513. cache_control.delete(' ').split(',')
  7514. else
  7515. []
  7516. end
  7517. end
  7518. def cache_control_headers
  7519. cache_control = {}
  7520. cache_control_segments.each do |segment|
  7521. directive, argument = segment.split('=', 2)
  7522. if SPESHUL_KEYS.include? directive
  7523. key = directive.tr('-', '_')
  7524. cache_control[key.to_sym] = argument || true
  7525. else
  7526. cache_control[:extras] ||= []
  7527. cache_control[:extras] << segment
  7528. end
  7529. end
  7530. cache_control
  7531. end
  7532. def prepare_cache_control!
  7533. @cache_control = cache_control_headers
  7534. @etag = self[ETAG]
  7535. end
  7536. def handle_conditional_get!
  7537. if etag? || last_modified? || !@cache_control.empty?
  7538. set_conditional_cache_control!
  7539. end
  7540. end
  7541. DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
  7542. NO_CACHE = "no-cache".freeze
  7543. PUBLIC = "public".freeze
  7544. PRIVATE = "private".freeze
  7545. MUST_REVALIDATE = "must-revalidate".freeze
  7546. def set_conditional_cache_control!
  7547. control = {}
  7548. cc_headers = cache_control_headers
  7549. if extras = cc_headers.delete(:extras)
  7550. @cache_control[:extras] ||= []
  7551. @cache_control[:extras] += extras
  7552. @cache_control[:extras].uniq!
  7553. end
  7554. control.merge! cc_headers
  7555. control.merge! @cache_control
  7556. if control.empty?
  7557. headers[CACHE_CONTROL] = DEFAULT_CACHE_CONTROL
  7558. elsif control[:no_cache]
  7559. headers[CACHE_CONTROL] = NO_CACHE
  7560. if control[:extras]
  7561. headers[CACHE_CONTROL] += ", #{control[:extras].join(', ')}"
  7562. end
  7563. else
  7564. extras = control[:extras]
  7565. max_age = control[:max_age]
  7566. options = []
  7567. options << "max-age=#{max_age.to_i}" if max_age
  7568. options << (control[:public] ? PUBLIC : PRIVATE)
  7569. options << MUST_REVALIDATE if control[:must_revalidate]
  7570. options.concat(extras) if extras
  7571. headers[CACHE_CONTROL] = options.join(", ")
  7572. end
  7573. end
  7574. end
  7575. end
  7576. end
  7577. end
  7578. require 'active_support/core_ext/hash/keys'
  7579. require 'active_support/core_ext/object/duplicable'
  7580. require 'action_dispatch/http/parameter_filter'
  7581. module ActionDispatch
  7582. module Http
  7583. # Allows you to specify sensitive parameters which will be replaced from
  7584. # the request log by looking in the query string of the request and all
  7585. # subhashes of the params hash to filter. If a block is given, each key and
  7586. # value of the params hash and all subhashes is passed to it, the value
  7587. # or key can be replaced using String#replace or similar method.
  7588. #
  7589. # env["action_dispatch.parameter_filter"] = [:password]
  7590. # => replaces the value to all keys matching /password/i with "[FILTERED]"
  7591. #
  7592. # env["action_dispatch.parameter_filter"] = [:foo, "bar"]
  7593. # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
  7594. #
  7595. # env["action_dispatch.parameter_filter"] = lambda do |k,v|
  7596. # v.reverse! if k =~ /secret/i
  7597. # end
  7598. # => reverses the value to all keys matching /secret/i
  7599. module FilterParameters
  7600. ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
  7601. NULL_PARAM_FILTER = ParameterFilter.new # :nodoc:
  7602. NULL_ENV_FILTER = ParameterFilter.new ENV_MATCH # :nodoc:
  7603. def initialize(env)
  7604. super
  7605. @filtered_parameters = nil
  7606. @filtered_env = nil
  7607. @filtered_path = nil
  7608. end
  7609. # Return a hash of parameters with all sensitive data replaced.
  7610. def filtered_parameters
  7611. @filtered_parameters ||= parameter_filter.filter(parameters)
  7612. end
  7613. # Return a hash of request.env with all sensitive data replaced.
  7614. def filtered_env
  7615. @filtered_env ||= env_filter.filter(@env)
  7616. end
  7617. # Reconstructed a path with all sensitive GET parameters replaced.
  7618. def filtered_path
  7619. @filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}"
  7620. end
  7621. protected
  7622. def parameter_filter
  7623. parameter_filter_for @env.fetch("action_dispatch.parameter_filter") {
  7624. return NULL_PARAM_FILTER
  7625. }
  7626. end
  7627. def env_filter
  7628. user_key = @env.fetch("action_dispatch.parameter_filter") {
  7629. return NULL_ENV_FILTER
  7630. }
  7631. parameter_filter_for(Array(user_key) + ENV_MATCH)
  7632. end
  7633. def parameter_filter_for(filters)
  7634. ParameterFilter.new(filters)
  7635. end
  7636. KV_RE = '[^&;=]+'
  7637. PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
  7638. def filtered_query_string
  7639. query_string.gsub(PAIR_RE) do |_|
  7640. parameter_filter.filter([[$1, $2]]).first.join("=")
  7641. end
  7642. end
  7643. end
  7644. end
  7645. end
  7646. module ActionDispatch
  7647. module Http
  7648. module FilterRedirect
  7649. FILTERED = '[FILTERED]'.freeze # :nodoc:
  7650. def filtered_location
  7651. if !location_filter.empty? && location_filter_match?
  7652. FILTERED
  7653. else
  7654. location
  7655. end
  7656. end
  7657. private
  7658. def location_filter
  7659. if request.present?
  7660. request.env['action_dispatch.redirect_filter'] || []
  7661. else
  7662. []
  7663. end
  7664. end
  7665. def location_filter_match?
  7666. location_filter.any? do |filter|
  7667. if String === filter
  7668. location.include?(filter)
  7669. elsif Regexp === filter
  7670. location.match(filter)
  7671. end
  7672. end
  7673. end
  7674. end
  7675. end
  7676. end
  7677. module ActionDispatch
  7678. module Http
  7679. class Headers
  7680. include Enumerable
  7681. def initialize(env = {})
  7682. @headers = env
  7683. end
  7684. def [](header_name)
  7685. @headers[env_name(header_name)]
  7686. end
  7687. def []=(k,v); @headers[k] = v; end
  7688. def key?(k); @headers.key? k; end
  7689. alias :include? :key?
  7690. def fetch(header_name, *args, &block)
  7691. @headers.fetch env_name(header_name), *args, &block
  7692. end
  7693. def each(&block)
  7694. @headers.each(&block)
  7695. end
  7696. private
  7697. # Converts a HTTP header name to an environment variable name if it is
  7698. # not contained within the headers hash.
  7699. def env_name(header_name)
  7700. @headers.include?(header_name) ? header_name : cgi_name(header_name)
  7701. end
  7702. def cgi_name(k)
  7703. "HTTP_#{k.upcase.gsub(/-/, '_')}"
  7704. end
  7705. end
  7706. end
  7707. end
  7708. require 'active_support/core_ext/module/attribute_accessors'
  7709. module ActionDispatch
  7710. module Http
  7711. module MimeNegotiation
  7712. extend ActiveSupport::Concern
  7713. included do
  7714. mattr_accessor :ignore_accept_header
  7715. self.ignore_accept_header = false
  7716. end
  7717. # The MIME type of the HTTP request, such as Mime::XML.
  7718. #
  7719. # For backward compatibility, the post \format is extracted from the
  7720. # X-Post-Data-Format HTTP header if present.
  7721. def content_mime_type
  7722. @env["action_dispatch.request.content_type"] ||= begin
  7723. if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
  7724. Mime::Type.lookup($1.strip.downcase)
  7725. else
  7726. nil
  7727. end
  7728. end
  7729. end
  7730. def content_type
  7731. content_mime_type && content_mime_type.to_s
  7732. end
  7733. # Returns the accepted MIME type for the request.
  7734. def accepts
  7735. @env["action_dispatch.request.accepts"] ||= begin
  7736. header = @env['HTTP_ACCEPT'].to_s.strip
  7737. if header.empty?
  7738. [content_mime_type]
  7739. else
  7740. Mime::Type.parse(header)
  7741. end
  7742. end
  7743. end
  7744. # Returns the MIME type for the \format used in the request.
  7745. #
  7746. # GET /posts/5.xml | request.format => Mime::XML
  7747. # GET /posts/5.xhtml | request.format => Mime::HTML
  7748. # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
  7749. #
  7750. def format(view_path = [])
  7751. formats.first
  7752. end
  7753. def formats
  7754. @env["action_dispatch.request.formats"] ||=
  7755. if parameters[:format]
  7756. Array(Mime[parameters[:format]])
  7757. elsif use_accept_header && valid_accept_header
  7758. accepts
  7759. elsif xhr?
  7760. [Mime::JS]
  7761. else
  7762. [Mime::HTML]
  7763. end
  7764. end
  7765. # Sets the \format by string extension, which can be used to force custom formats
  7766. # that are not controlled by the extension.
  7767. #
  7768. # class ApplicationController < ActionController::Base
  7769. # before_action :adjust_format_for_iphone
  7770. #
  7771. # private
  7772. # def adjust_format_for_iphone
  7773. # request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
  7774. # end
  7775. # end
  7776. def format=(extension)
  7777. parameters[:format] = extension.to_s
  7778. @env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
  7779. end
  7780. # Sets the \formats by string extensions. This differs from #format= by allowing you
  7781. # to set multiple, ordered formats, which is useful when you want to have a fallback.
  7782. #
  7783. # In this example, the :iphone format will be used if it's available, otherwise it'll fallback
  7784. # to the :html format.
  7785. #
  7786. # class ApplicationController < ActionController::Base
  7787. # before_action :adjust_format_for_iphone_with_html_fallback
  7788. #
  7789. # private
  7790. # def adjust_format_for_iphone_with_html_fallback
  7791. # request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/]
  7792. # end
  7793. # end
  7794. def formats=(extensions)
  7795. parameters[:format] = extensions.first.to_s
  7796. @env["action_dispatch.request.formats"] = extensions.collect do |extension|
  7797. Mime::Type.lookup_by_extension(extension)
  7798. end
  7799. end
  7800. # Receives an array of mimes and return the first user sent mime that
  7801. # matches the order array.
  7802. #
  7803. def negotiate_mime(order)
  7804. formats.each do |priority|
  7805. if priority == Mime::ALL
  7806. return order.first
  7807. elsif order.include?(priority)
  7808. return priority
  7809. end
  7810. end
  7811. order.include?(Mime::ALL) ? formats.first : nil
  7812. end
  7813. protected
  7814. BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
  7815. def valid_accept_header
  7816. (xhr? && (accept || content_mime_type)) ||
  7817. (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS)
  7818. end
  7819. def use_accept_header
  7820. !self.class.ignore_accept_header
  7821. end
  7822. end
  7823. end
  7824. end
  7825. require 'set'
  7826. require 'active_support/core_ext/class/attribute_accessors'
  7827. require 'active_support/core_ext/string/starts_ends_with'
  7828. module Mime
  7829. class Mimes < Array
  7830. def symbols
  7831. @symbols ||= map { |m| m.to_sym }
  7832. end
  7833. %w(<< concat shift unshift push pop []= clear compact! collect!
  7834. delete delete_at delete_if flatten! map! insert reject! reverse!
  7835. replace slice! sort! uniq!).each do |method|
  7836. module_eval <<-CODE, __FILE__, __LINE__ + 1
  7837. def #{method}(*)
  7838. @symbols = nil
  7839. super
  7840. end
  7841. CODE
  7842. end
  7843. end
  7844. SET = Mimes.new
  7845. EXTENSION_LOOKUP = {}
  7846. LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
  7847. class << self
  7848. def [](type)
  7849. return type if type.is_a?(Type)
  7850. Type.lookup_by_extension(type) || NullType.new
  7851. end
  7852. def fetch(type)
  7853. return type if type.is_a?(Type)
  7854. EXTENSION_LOOKUP.fetch(type.to_s) { |k| yield k }
  7855. end
  7856. end
  7857. # Encapsulates the notion of a mime type. Can be used at render time, for example, with:
  7858. #
  7859. # class PostsController < ActionController::Base
  7860. # def show
  7861. # @post = Post.find(params[:id])
  7862. #
  7863. # respond_to do |format|
  7864. # format.html
  7865. # format.ics { render text: post.to_ics, mime_type: Mime::Type["text/calendar"] }
  7866. # format.xml { render xml: @people }
  7867. # end
  7868. # end
  7869. # end
  7870. class Type
  7871. @@html_types = Set.new [:html, :all]
  7872. cattr_reader :html_types
  7873. # These are the content types which browsers can generate without using ajax, flash, etc
  7874. # i.e. following a link, getting an image or posting a form. CSRF protection
  7875. # only needs to protect against these types.
  7876. @@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
  7877. attr_reader :symbol
  7878. @register_callbacks = []
  7879. # A simple helper class used in parsing the accept header
  7880. class AcceptItem #:nodoc:
  7881. attr_accessor :index, :name, :q
  7882. alias :to_s :name
  7883. def initialize(index, name, q = nil)
  7884. @index = index
  7885. @name = name
  7886. q ||= 0.0 if @name == Mime::ALL.to_s # default wildcard match to end of list
  7887. @q = ((q || 1.0).to_f * 100).to_i
  7888. end
  7889. def <=>(item)
  7890. result = item.q <=> @q
  7891. result = @index <=> item.index if result == 0
  7892. result
  7893. end
  7894. def ==(item)
  7895. @name == item.to_s
  7896. end
  7897. end
  7898. class AcceptList < Array #:nodoc:
  7899. def assort!
  7900. sort!
  7901. # Take care of the broken text/xml entry by renaming or deleting it
  7902. if text_xml_idx && app_xml_idx
  7903. app_xml.q = [text_xml.q, app_xml.q].max # set the q value to the max of the two
  7904. exchange_xml_items if app_xml_idx > text_xml_idx # make sure app_xml is ahead of text_xml in the list
  7905. delete_at(text_xml_idx) # delete text_xml from the list
  7906. elsif text_xml_idx
  7907. text_xml.name = Mime::XML.to_s
  7908. end
  7909. # Look for more specific XML-based types and sort them ahead of app/xml
  7910. if app_xml_idx
  7911. idx = app_xml_idx
  7912. while idx < length
  7913. type = self[idx]
  7914. break if type.q < app_xml.q
  7915. if type.name.ends_with? '+xml'
  7916. self[app_xml_idx], self[idx] = self[idx], app_xml
  7917. @app_xml_idx = idx
  7918. end
  7919. idx += 1
  7920. end
  7921. end
  7922. map! { |i| Mime::Type.lookup(i.name) }.uniq!
  7923. to_a
  7924. end
  7925. private
  7926. def text_xml_idx
  7927. @text_xml_idx ||= index('text/xml')
  7928. end
  7929. def app_xml_idx
  7930. @app_xml_idx ||= index(Mime::XML.to_s)
  7931. end
  7932. def text_xml
  7933. self[text_xml_idx]
  7934. end
  7935. def app_xml
  7936. self[app_xml_idx]
  7937. end
  7938. def exchange_xml_items
  7939. self[app_xml_idx], self[text_xml_idx] = text_xml, app_xml
  7940. @app_xml_idx, @text_xml_idx = text_xml_idx, app_xml_idx
  7941. end
  7942. end
  7943. class << self
  7944. TRAILING_STAR_REGEXP = /(text|application)\/\*/
  7945. PARAMETER_SEPARATOR_REGEXP = /;\s*\w+="?\w+"?/
  7946. def register_callback(&block)
  7947. @register_callbacks << block
  7948. end
  7949. def lookup(string)
  7950. LOOKUP[string]
  7951. end
  7952. def lookup_by_extension(extension)
  7953. EXTENSION_LOOKUP[extension.to_s]
  7954. end
  7955. # Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
  7956. # rendering different HTML versions depending on the user agent, like an iPhone.
  7957. def register_alias(string, symbol, extension_synonyms = [])
  7958. register(string, symbol, [], extension_synonyms, true)
  7959. end
  7960. def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
  7961. Mime.const_set(symbol.upcase, Type.new(string, symbol, mime_type_synonyms))
  7962. new_mime = Mime.const_get(symbol.upcase)
  7963. SET << new_mime
  7964. ([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup
  7965. ([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = SET.last }
  7966. @register_callbacks.each do |callback|
  7967. callback.call(new_mime)
  7968. end
  7969. end
  7970. def parse(accept_header)
  7971. if accept_header !~ /,/
  7972. accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
  7973. parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)]
  7974. else
  7975. list, index = AcceptList.new, 0
  7976. accept_header.split(',').each do |header|
  7977. params, q = header.split(PARAMETER_SEPARATOR_REGEXP)
  7978. if params.present?
  7979. params.strip!
  7980. params = parse_trailing_star(params) || [params]
  7981. params.each do |m|
  7982. list << AcceptItem.new(index, m.to_s, q)
  7983. index += 1
  7984. end
  7985. end
  7986. end
  7987. list.assort!
  7988. end
  7989. end
  7990. def parse_trailing_star(accept_header)
  7991. parse_data_with_trailing_star($1) if accept_header =~ TRAILING_STAR_REGEXP
  7992. end
  7993. # For an input of <tt>'text'</tt>, returns <tt>[Mime::JSON, Mime::XML, Mime::ICS,
  7994. # Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]</tt>.
  7995. #
  7996. # For an input of <tt>'application'</tt>, returns <tt>[Mime::HTML, Mime::JS,
  7997. # Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM]</tt>.
  7998. def parse_data_with_trailing_star(input)
  7999. Mime::SET.select { |m| m =~ input }
  8000. end
  8001. # This method is opposite of register method.
  8002. #
  8003. # Usage:
  8004. #
  8005. # Mime::Type.unregister(:mobile)
  8006. def unregister(symbol)
  8007. symbol = symbol.upcase
  8008. mime = Mime.const_get(symbol)
  8009. Mime.instance_eval { remove_const(symbol) }
  8010. SET.delete_if { |v| v.eql?(mime) }
  8011. LOOKUP.delete_if { |k,v| v.eql?(mime) }
  8012. EXTENSION_LOOKUP.delete_if { |k,v| v.eql?(mime) }
  8013. end
  8014. end
  8015. def initialize(string, symbol = nil, synonyms = [])
  8016. @symbol, @synonyms = symbol, synonyms
  8017. @string = string
  8018. end
  8019. def to_s
  8020. @string
  8021. end
  8022. def to_str
  8023. to_s
  8024. end
  8025. def to_sym
  8026. @symbol
  8027. end
  8028. def ref
  8029. to_sym || to_s
  8030. end
  8031. def ===(list)
  8032. if list.is_a?(Array)
  8033. (@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
  8034. else
  8035. super
  8036. end
  8037. end
  8038. def ==(mime_type)
  8039. return false if mime_type.blank?
  8040. (@synonyms + [ self ]).any? do |synonym|
  8041. synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
  8042. end
  8043. end
  8044. def =~(mime_type)
  8045. return false if mime_type.blank?
  8046. regexp = Regexp.new(Regexp.quote(mime_type.to_s))
  8047. (@synonyms + [ self ]).any? do |synonym|
  8048. synonym.to_s =~ regexp
  8049. end
  8050. end
  8051. # Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
  8052. # ActionController::RequestForgeryProtection.
  8053. def verify_request?
  8054. ActiveSupport::Deprecation.warn "Mime::Type#verify_request? is deprecated and will be removed in Rails 4.1"
  8055. @@browser_generated_types.include?(to_sym)
  8056. end
  8057. def self.browser_generated_types
  8058. ActiveSupport::Deprecation.warn "Mime::Type.browser_generated_types is deprecated and will be removed in Rails 4.1"
  8059. @@browser_generated_types
  8060. end
  8061. def html?
  8062. @@html_types.include?(to_sym) || @string =~ /html/
  8063. end
  8064. private
  8065. def to_ary; end
  8066. def to_a; end
  8067. def method_missing(method, *args)
  8068. if method.to_s.ends_with? '?'
  8069. method[0..-2].downcase.to_sym == to_sym
  8070. else
  8071. super
  8072. end
  8073. end
  8074. def respond_to_missing?(method, include_private = false) #:nodoc:
  8075. method.to_s.ends_with? '?'
  8076. end
  8077. end
  8078. class NullType
  8079. def nil?
  8080. true
  8081. end
  8082. private
  8083. def method_missing(method, *args)
  8084. false if method.to_s.ends_with? '?'
  8085. end
  8086. end
  8087. end
  8088. require 'action_dispatch/http/mime_types'
  8089. # Build list of Mime types for HTTP responses
  8090. # http://www.iana.org/assignments/media-types/
  8091. Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
  8092. Mime::Type.register "text/plain", :text, [], %w(txt)
  8093. Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
  8094. Mime::Type.register "text/css", :css
  8095. Mime::Type.register "text/calendar", :ics
  8096. Mime::Type.register "text/csv", :csv
  8097. Mime::Type.register "image/png", :png, [], %w(png)
  8098. Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)
  8099. Mime::Type.register "image/gif", :gif, [], %w(gif)
  8100. Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
  8101. Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
  8102. Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
  8103. Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
  8104. Mime::Type.register "application/rss+xml", :rss
  8105. Mime::Type.register "application/atom+xml", :atom
  8106. Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
  8107. Mime::Type.register "multipart/form-data", :multipart_form
  8108. Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
  8109. # http://www.ietf.org/rfc/rfc4627.txt
  8110. # http://www.json.org/JSONRequest.html
  8111. Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
  8112. Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
  8113. Mime::Type.register "application/zip", :zip, [], %w(zip)
  8114. # Create Mime::ALL but do not add it to the SET.
  8115. Mime::ALL = Mime::Type.new("*/*", :all, [])
  8116. module ActionDispatch
  8117. module Http
  8118. class ParameterFilter
  8119. FILTERED = '[FILTERED]'.freeze # :nodoc:
  8120. def initialize(filters = [])
  8121. @filters = filters
  8122. end
  8123. def filter(params)
  8124. compiled_filter.call(params)
  8125. end
  8126. private
  8127. def compiled_filter
  8128. @compiled_filter ||= CompiledFilter.compile(@filters)
  8129. end
  8130. class CompiledFilter # :nodoc:
  8131. def self.compile(filters)
  8132. return lambda { |params| params.dup } if filters.empty?
  8133. strings, regexps, blocks = [], [], []
  8134. filters.each do |item|
  8135. case item
  8136. when Proc
  8137. blocks << item
  8138. when Regexp
  8139. regexps << item
  8140. else
  8141. strings << item.to_s
  8142. end
  8143. end
  8144. regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
  8145. new regexps, blocks
  8146. end
  8147. attr_reader :regexps, :blocks
  8148. def initialize(regexps, blocks)
  8149. @regexps = regexps
  8150. @blocks = blocks
  8151. end
  8152. def call(original_params)
  8153. filtered_params = {}
  8154. original_params.each do |key, value|
  8155. if regexps.any? { |r| key =~ r }
  8156. value = FILTERED
  8157. elsif value.is_a?(Hash)
  8158. value = call(value)
  8159. elsif value.is_a?(Array)
  8160. value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
  8161. elsif blocks.any?
  8162. key = key.dup
  8163. value = value.dup if value.duplicable?
  8164. blocks.each { |b| b.call(key, value) }
  8165. end
  8166. filtered_params[key] = value
  8167. end
  8168. filtered_params
  8169. end
  8170. end
  8171. end
  8172. end
  8173. end
  8174. require 'active_support/core_ext/hash/keys'
  8175. require 'active_support/core_ext/hash/indifferent_access'
  8176. module ActionDispatch
  8177. module Http
  8178. module Parameters
  8179. def initialize(env)
  8180. super
  8181. @symbolized_path_params = nil
  8182. end
  8183. # Returns both GET and POST \parameters in a single hash.
  8184. def parameters
  8185. @env["action_dispatch.request.parameters"] ||= begin
  8186. params = begin
  8187. request_parameters.merge(query_parameters)
  8188. rescue EOFError
  8189. query_parameters.dup
  8190. end
  8191. params.merge!(path_parameters)
  8192. encode_params(params).with_indifferent_access
  8193. end
  8194. end
  8195. alias :params :parameters
  8196. def path_parameters=(parameters) #:nodoc:
  8197. @symbolized_path_params = nil
  8198. @env.delete("action_dispatch.request.parameters")
  8199. @env["action_dispatch.request.path_parameters"] = parameters
  8200. end
  8201. # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
  8202. def symbolized_path_parameters
  8203. @symbolized_path_params ||= path_parameters.symbolize_keys
  8204. end
  8205. # Returns a hash with the \parameters used to form the \path of the request.
  8206. # Returned hash keys are strings:
  8207. #
  8208. # {'action' => 'my_action', 'controller' => 'my_controller'}
  8209. #
  8210. # See <tt>symbolized_path_parameters</tt> for symbolized keys.
  8211. def path_parameters
  8212. @env["action_dispatch.request.path_parameters"] ||= {}
  8213. end
  8214. def reset_parameters #:nodoc:
  8215. @env.delete("action_dispatch.request.parameters")
  8216. end
  8217. private
  8218. # TODO: Validate that the characters are UTF-8. If they aren't,
  8219. # you'll get a weird error down the road, but our form handling
  8220. # should really prevent that from happening
  8221. def encode_params(params)
  8222. if params.is_a?(String)
  8223. return params.force_encoding(Encoding::UTF_8).encode!
  8224. elsif !params.is_a?(Hash)
  8225. return params
  8226. end
  8227. params.each do |k, v|
  8228. case v
  8229. when Hash
  8230. encode_params(v)
  8231. when Array
  8232. v.map! {|el| encode_params(el) }
  8233. else
  8234. encode_params(v)
  8235. end
  8236. end
  8237. end
  8238. # Convert nested Hash to ActiveSupport::HashWithIndifferentAccess
  8239. def normalize_parameters(value)
  8240. case value
  8241. when Hash
  8242. h = {}
  8243. value.each { |k, v| h[k] = normalize_parameters(v) }
  8244. h.with_indifferent_access
  8245. when Array
  8246. value.map { |e| normalize_parameters(e) }
  8247. else
  8248. value
  8249. end
  8250. end
  8251. end
  8252. end
  8253. end
  8254. require "rack/cache"
  8255. require "rack/cache/context"
  8256. require "active_support/cache"
  8257. module ActionDispatch
  8258. class RailsMetaStore < Rack::Cache::MetaStore
  8259. def self.resolve(uri)
  8260. new
  8261. end
  8262. def initialize(store = Rails.cache)
  8263. @store = store
  8264. end
  8265. def read(key)
  8266. if data = @store.read(key)
  8267. Marshal.load(data)
  8268. else
  8269. []
  8270. end
  8271. end
  8272. def write(key, value)
  8273. @store.write(key, Marshal.dump(value))
  8274. end
  8275. ::Rack::Cache::MetaStore::RAILS = self
  8276. end
  8277. class RailsEntityStore < Rack::Cache::EntityStore
  8278. def self.resolve(uri)
  8279. new
  8280. end
  8281. def initialize(store = Rails.cache)
  8282. @store = store
  8283. end
  8284. def exist?(key)
  8285. @store.exist?(key)
  8286. end
  8287. def open(key)
  8288. @store.read(key)
  8289. end
  8290. def read(key)
  8291. body = open(key)
  8292. body.join if body
  8293. end
  8294. def write(body)
  8295. buf = []
  8296. key, size = slurp(body) { |part| buf << part }
  8297. @store.write(key, buf)
  8298. [key, size]
  8299. end
  8300. ::Rack::Cache::EntityStore::RAILS = self
  8301. end
  8302. end
  8303. require 'stringio'
  8304. require 'active_support/inflector'
  8305. require 'action_dispatch/http/headers'
  8306. require 'action_controller/metal/exceptions'
  8307. require 'rack/request'
  8308. require 'action_dispatch/http/cache'
  8309. require 'action_dispatch/http/mime_negotiation'
  8310. require 'action_dispatch/http/parameters'
  8311. require 'action_dispatch/http/filter_parameters'
  8312. require 'action_dispatch/http/upload'
  8313. require 'action_dispatch/http/url'
  8314. require 'active_support/core_ext/array/conversions'
  8315. module ActionDispatch
  8316. class Request < Rack::Request
  8317. include ActionDispatch::Http::Cache::Request
  8318. include ActionDispatch::Http::MimeNegotiation
  8319. include ActionDispatch::Http::Parameters
  8320. include ActionDispatch::Http::FilterParameters
  8321. include ActionDispatch::Http::Upload
  8322. include ActionDispatch::Http::URL
  8323. autoload :Session, 'action_dispatch/request/session'
  8324. LOCALHOST = Regexp.union [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/]
  8325. ENV_METHODS = %w[ AUTH_TYPE GATEWAY_INTERFACE
  8326. PATH_TRANSLATED REMOTE_HOST
  8327. REMOTE_IDENT REMOTE_USER REMOTE_ADDR
  8328. SERVER_NAME SERVER_PROTOCOL
  8329. HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
  8330. HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
  8331. HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
  8332. ENV_METHODS.each do |env|
  8333. class_eval <<-METHOD, __FILE__, __LINE__ + 1
  8334. def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
  8335. @env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"]
  8336. end # end
  8337. METHOD
  8338. end
  8339. def initialize(env)
  8340. super
  8341. @method = nil
  8342. @request_method = nil
  8343. @remote_ip = nil
  8344. @original_fullpath = nil
  8345. @fullpath = nil
  8346. @ip = nil
  8347. @uuid = nil
  8348. end
  8349. def key?(key)
  8350. @env.key?(key)
  8351. end
  8352. # List of HTTP request methods from the following RFCs:
  8353. # Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)
  8354. # HTTP Extensions for Distributed Authoring -- WEBDAV (http://www.ietf.org/rfc/rfc2518.txt)
  8355. # Versioning Extensions to WebDAV (http://www.ietf.org/rfc/rfc3253.txt)
  8356. # Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
  8357. # Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
  8358. # Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
  8359. # PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
  8360. RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
  8361. RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
  8362. RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
  8363. RFC3648 = %w(ORDERPATCH)
  8364. RFC3744 = %w(ACL)
  8365. RFC5323 = %w(SEARCH)
  8366. RFC5789 = %w(PATCH)
  8367. HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
  8368. HTTP_METHOD_LOOKUP = {}
  8369. # Populate the HTTP method lookup cache
  8370. HTTP_METHODS.each { |method|
  8371. HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
  8372. }
  8373. # Returns the HTTP \method that the application should see.
  8374. # In the case where the \method was overridden by a middleware
  8375. # (for instance, if a HEAD request was converted to a GET,
  8376. # or if a _method parameter was used to determine the \method
  8377. # the application should use), this \method returns the overridden
  8378. # value, not the original.
  8379. def request_method
  8380. @request_method ||= check_method(env["REQUEST_METHOD"])
  8381. end
  8382. # Returns a symbol form of the #request_method
  8383. def request_method_symbol
  8384. HTTP_METHOD_LOOKUP[request_method]
  8385. end
  8386. # Returns the original value of the environment's REQUEST_METHOD,
  8387. # even if it was overridden by middleware. See #request_method for
  8388. # more information.
  8389. def method
  8390. @method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
  8391. end
  8392. # Returns a symbol form of the #method
  8393. def method_symbol
  8394. HTTP_METHOD_LOOKUP[method]
  8395. end
  8396. # Is this a GET (or HEAD) request?
  8397. # Equivalent to <tt>request.request_method_symbol == :get</tt>.
  8398. def get?
  8399. HTTP_METHOD_LOOKUP[request_method] == :get
  8400. end
  8401. # Is this a POST request?
  8402. # Equivalent to <tt>request.request_method_symbol == :post</tt>.
  8403. def post?
  8404. HTTP_METHOD_LOOKUP[request_method] == :post
  8405. end
  8406. # Is this a PATCH request?
  8407. # Equivalent to <tt>request.request_method == :patch</tt>.
  8408. def patch?
  8409. HTTP_METHOD_LOOKUP[request_method] == :patch
  8410. end
  8411. # Is this a PUT request?
  8412. # Equivalent to <tt>request.request_method_symbol == :put</tt>.
  8413. def put?
  8414. HTTP_METHOD_LOOKUP[request_method] == :put
  8415. end
  8416. # Is this a DELETE request?
  8417. # Equivalent to <tt>request.request_method_symbol == :delete</tt>.
  8418. def delete?
  8419. HTTP_METHOD_LOOKUP[request_method] == :delete
  8420. end
  8421. # Is this a HEAD request?
  8422. # Equivalent to <tt>request.request_method_symbol == :head</tt>.
  8423. def head?
  8424. HTTP_METHOD_LOOKUP[request_method] == :head
  8425. end
  8426. # Provides access to the request's HTTP headers, for example:
  8427. #
  8428. # request.headers["Content-Type"] # => "text/plain"
  8429. def headers
  8430. Http::Headers.new(@env)
  8431. end
  8432. def original_fullpath
  8433. @original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
  8434. end
  8435. def fullpath
  8436. @fullpath ||= super
  8437. end
  8438. def original_url
  8439. base_url + original_fullpath
  8440. end
  8441. def media_type
  8442. content_mime_type.to_s
  8443. end
  8444. # Returns the content length of the request as an integer.
  8445. def content_length
  8446. super.to_i
  8447. end
  8448. # Returns true if the "X-Requested-With" header contains "XMLHttpRequest"
  8449. # (case-insensitive). All major JavaScript libraries send this header with
  8450. # every Ajax request.
  8451. def xml_http_request?
  8452. @env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
  8453. end
  8454. alias :xhr? :xml_http_request?
  8455. def ip
  8456. @ip ||= super
  8457. end
  8458. # Originating IP address, usually set by the RemoteIp middleware.
  8459. def remote_ip
  8460. @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
  8461. end
  8462. # Returns the unique request id, which is based off either the X-Request-Id header that can
  8463. # be generated by a firewall, load balancer, or web server or by the RequestId middleware
  8464. # (which sets the action_dispatch.request_id environment variable).
  8465. #
  8466. # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
  8467. # This relies on the rack variable set by the ActionDispatch::RequestId middleware.
  8468. def uuid
  8469. @uuid ||= env["action_dispatch.request_id"]
  8470. end
  8471. # Returns the lowercase name of the HTTP server software.
  8472. def server_software
  8473. (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
  8474. end
  8475. # Read the request \body. This is useful for web services that need to
  8476. # work with raw requests directly.
  8477. def raw_post
  8478. unless @env.include? 'RAW_POST_DATA'
  8479. raw_post_body = body
  8480. @env['RAW_POST_DATA'] = raw_post_body.read(@env['CONTENT_LENGTH'].to_i)
  8481. raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
  8482. end
  8483. @env['RAW_POST_DATA']
  8484. end
  8485. # The request body is an IO input stream. If the RAW_POST_DATA environment
  8486. # variable is already set, wrap it in a StringIO.
  8487. def body
  8488. if raw_post = @env['RAW_POST_DATA']
  8489. raw_post.force_encoding(Encoding::BINARY)
  8490. StringIO.new(raw_post)
  8491. else
  8492. @env['rack.input']
  8493. end
  8494. end
  8495. def form_data?
  8496. FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s)
  8497. end
  8498. def body_stream #:nodoc:
  8499. @env['rack.input']
  8500. end
  8501. # TODO This should be broken apart into AD::Request::Session and probably
  8502. # be included by the session middleware.
  8503. def reset_session
  8504. if session && session.respond_to?(:destroy)
  8505. session.destroy
  8506. else
  8507. self.session = {}
  8508. end
  8509. @env['action_dispatch.request.flash_hash'] = nil
  8510. end
  8511. def session=(session) #:nodoc:
  8512. Session.set @env, session
  8513. end
  8514. def session_options=(options)
  8515. Session::Options.set @env, options
  8516. end
  8517. # Override Rack's GET method to support indifferent access
  8518. def GET
  8519. @env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {})
  8520. rescue TypeError => e
  8521. raise ActionController::BadRequest.new(:query, e)
  8522. end
  8523. alias :query_parameters :GET
  8524. # Override Rack's POST method to support indifferent access
  8525. def POST
  8526. @env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {})
  8527. rescue TypeError => e
  8528. raise ActionController::BadRequest.new(:request, e)
  8529. end
  8530. alias :request_parameters :POST
  8531. # Returns the authorization header regardless of whether it was specified directly or through one of the
  8532. # proxy alternatives.
  8533. def authorization
  8534. @env['HTTP_AUTHORIZATION'] ||
  8535. @env['X-HTTP_AUTHORIZATION'] ||
  8536. @env['X_HTTP_AUTHORIZATION'] ||
  8537. @env['REDIRECT_X_HTTP_AUTHORIZATION']
  8538. end
  8539. # True if the request came from localhost, 127.0.0.1.
  8540. def local?
  8541. LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
  8542. end
  8543. # Remove nils from the params hash
  8544. def deep_munge(hash)
  8545. hash.each do |k, v|
  8546. case v
  8547. when Array
  8548. v.grep(Hash) { |x| deep_munge(x) }
  8549. v.compact!
  8550. hash[k] = nil if v.empty?
  8551. when Hash
  8552. deep_munge(v)
  8553. end
  8554. end
  8555. hash
  8556. end
  8557. protected
  8558. def parse_query(qs)
  8559. deep_munge(super)
  8560. end
  8561. private
  8562. def check_method(name)
  8563. HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
  8564. name
  8565. end
  8566. end
  8567. end
  8568. require 'active_support/core_ext/class/attribute_accessors'
  8569. require 'monitor'
  8570. module ActionDispatch # :nodoc:
  8571. # Represents an HTTP response generated by a controller action. Use it to
  8572. # retrieve the current state of the response, or customize the response. It can
  8573. # either represent a real HTTP response (i.e. one that is meant to be sent
  8574. # back to the web browser) or a TestResponse (i.e. one that is generated
  8575. # from integration tests).
  8576. #
  8577. # \Response is mostly a Ruby on \Rails framework implementation detail, and
  8578. # should never be used directly in controllers. Controllers should use the
  8579. # methods defined in ActionController::Base instead. For example, if you want
  8580. # to set the HTTP response's content MIME type, then use
  8581. # ActionControllerBase#headers instead of Response#headers.
  8582. #
  8583. # Nevertheless, integration tests may want to inspect controller responses in
  8584. # more detail, and that's when \Response can be useful for application
  8585. # developers. Integration test methods such as
  8586. # ActionDispatch::Integration::Session#get and
  8587. # ActionDispatch::Integration::Session#post return objects of type
  8588. # TestResponse (which are of course also of type \Response).
  8589. #
  8590. # For example, the following demo integration test prints the body of the
  8591. # controller response to the console:
  8592. #
  8593. # class DemoControllerTest < ActionDispatch::IntegrationTest
  8594. # def test_print_root_path_to_console
  8595. # get('/')
  8596. # puts response.body
  8597. # end
  8598. # end
  8599. class Response
  8600. attr_accessor :request, :header
  8601. attr_reader :status
  8602. attr_writer :sending_file
  8603. alias_method :headers=, :header=
  8604. alias_method :headers, :header
  8605. delegate :[], :[]=, :to => :@header
  8606. delegate :each, :to => :@stream
  8607. # Sets the HTTP response's content MIME type. For example, in the controller
  8608. # you could write this:
  8609. #
  8610. # response.content_type = "text/plain"
  8611. #
  8612. # If a character set has been defined for this response (see charset=) then
  8613. # the character set information will also be included in the content type
  8614. # information.
  8615. attr_accessor :charset
  8616. attr_reader :content_type
  8617. CONTENT_TYPE = "Content-Type".freeze
  8618. SET_COOKIE = "Set-Cookie".freeze
  8619. LOCATION = "Location".freeze
  8620. cattr_accessor(:default_charset) { "utf-8" }
  8621. cattr_accessor(:default_headers)
  8622. include Rack::Response::Helpers
  8623. include ActionDispatch::Http::FilterRedirect
  8624. include ActionDispatch::Http::Cache::Response
  8625. include MonitorMixin
  8626. class Buffer # :nodoc:
  8627. def initialize(response, buf)
  8628. @response = response
  8629. @buf = buf
  8630. @closed = false
  8631. end
  8632. def write(string)
  8633. raise IOError, "closed stream" if closed?
  8634. @response.commit!
  8635. @buf.push string
  8636. end
  8637. def each(&block)
  8638. @buf.each(&block)
  8639. end
  8640. def close
  8641. @response.commit!
  8642. @closed = true
  8643. end
  8644. def closed?
  8645. @closed
  8646. end
  8647. end
  8648. attr_reader :stream
  8649. def initialize(status = 200, header = {}, body = [])
  8650. super()
  8651. header = merge_default_headers(header, self.class.default_headers)
  8652. self.body, self.header, self.status = body, header, status
  8653. @sending_file = false
  8654. @blank = false
  8655. @cv = new_cond
  8656. @committed = false
  8657. @content_type = nil
  8658. @charset = nil
  8659. if content_type = self[CONTENT_TYPE]
  8660. type, charset = content_type.split(/;\s*charset=/)
  8661. @content_type = Mime::Type.lookup(type)
  8662. @charset = charset || self.class.default_charset
  8663. end
  8664. prepare_cache_control!
  8665. yield self if block_given?
  8666. end
  8667. def await_commit
  8668. synchronize do
  8669. @cv.wait_until { @committed }
  8670. end
  8671. end
  8672. def commit!
  8673. synchronize do
  8674. @committed = true
  8675. @cv.broadcast
  8676. end
  8677. end
  8678. def committed?
  8679. @committed
  8680. end
  8681. # Sets the HTTP status code.
  8682. def status=(status)
  8683. @status = Rack::Utils.status_code(status)
  8684. end
  8685. def content_type=(content_type)
  8686. @content_type = content_type.to_s
  8687. end
  8688. # The response code of the request.
  8689. def response_code
  8690. @status
  8691. end
  8692. # Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>.
  8693. def code
  8694. @status.to_s
  8695. end
  8696. # Returns the corresponding message for the current HTTP status code:
  8697. #
  8698. # response.status = 200
  8699. # response.message # => "OK"
  8700. #
  8701. # response.status = 404
  8702. # response.message # => "Not Found"
  8703. #
  8704. def message
  8705. Rack::Utils::HTTP_STATUS_CODES[@status]
  8706. end
  8707. alias_method :status_message, :message
  8708. def respond_to?(method)
  8709. if method.to_s == 'to_path'
  8710. stream.respond_to?(:to_path)
  8711. else
  8712. super
  8713. end
  8714. end
  8715. def to_path
  8716. stream.to_path
  8717. end
  8718. # Returns the content of the response as a string. This contains the contents
  8719. # of any calls to <tt>render</tt>.
  8720. def body
  8721. strings = []
  8722. each { |part| strings << part.to_s }
  8723. strings.join
  8724. end
  8725. EMPTY = " "
  8726. # Allows you to manually set or override the response body.
  8727. def body=(body)
  8728. @blank = true if body == EMPTY
  8729. if body.respond_to?(:to_path)
  8730. @stream = body
  8731. else
  8732. @stream = build_buffer self, munge_body_object(body)
  8733. end
  8734. end
  8735. def body_parts
  8736. parts = []
  8737. @stream.each { |x| parts << x }
  8738. parts
  8739. end
  8740. def set_cookie(key, value)
  8741. ::Rack::Utils.set_cookie_header!(header, key, value)
  8742. end
  8743. def delete_cookie(key, value={})
  8744. ::Rack::Utils.delete_cookie_header!(header, key, value)
  8745. end
  8746. def location
  8747. headers[LOCATION]
  8748. end
  8749. alias_method :redirect_url, :location
  8750. def location=(url)
  8751. headers[LOCATION] = url
  8752. end
  8753. def close
  8754. stream.close if stream.respond_to?(:close)
  8755. end
  8756. def to_a
  8757. rack_response @status, @header.to_hash
  8758. end
  8759. alias prepare! to_a
  8760. alias to_ary to_a # For implicit splat on 1.9.2
  8761. # Returns the response cookies, converted to a Hash of (name => value) pairs
  8762. #
  8763. # assert_equal 'AuthorOfNewPage', r.cookies['author']
  8764. def cookies
  8765. cookies = {}
  8766. if header = self[SET_COOKIE]
  8767. header = header.split("\n") if header.respond_to?(:to_str)
  8768. header.each do |cookie|
  8769. if pair = cookie.split(';').first
  8770. key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
  8771. cookies[key] = value
  8772. end
  8773. end
  8774. end
  8775. cookies
  8776. end
  8777. private
  8778. def merge_default_headers(original, default)
  8779. return original unless default.respond_to?(:merge)
  8780. default.merge(original)
  8781. end
  8782. def build_buffer(response, body)
  8783. Buffer.new response, body
  8784. end
  8785. def munge_body_object(body)
  8786. body.respond_to?(:each) ? body : [body]
  8787. end
  8788. def assign_default_content_type_and_charset!(headers)
  8789. return if headers[CONTENT_TYPE].present?
  8790. @content_type ||= Mime::HTML
  8791. @charset ||= self.class.default_charset unless @charset == false
  8792. type = @content_type.to_s.dup
  8793. type << "; charset=#{@charset}" if append_charset?
  8794. headers[CONTENT_TYPE] = type
  8795. end
  8796. def append_charset?
  8797. !@sending_file && @charset != false
  8798. end
  8799. def rack_response(status, header)
  8800. assign_default_content_type_and_charset!(header)
  8801. handle_conditional_get!
  8802. header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join)
  8803. if [204, 304].include?(@status)
  8804. header.delete CONTENT_TYPE
  8805. [status, header, []]
  8806. else
  8807. [status, header, self]
  8808. end
  8809. end
  8810. end
  8811. end
  8812. module ActionDispatch
  8813. module Http
  8814. # Models uploaded files.
  8815. #
  8816. # The actual file is accessible via the +tempfile+ accessor, though some
  8817. # of its interface is available directly for convenience.
  8818. #
  8819. # Uploaded files are temporary files whose lifespan is one request. When
  8820. # the object is finalized Ruby unlinks the file, so there is not need to
  8821. # clean them with a separate maintenance task.
  8822. class UploadedFile
  8823. # The basename of the file in the client.
  8824. attr_accessor :original_filename
  8825. # A string with the MIME type of the file.
  8826. attr_accessor :content_type
  8827. # A +Tempfile+ object with the actual uploaded file. Note that some of
  8828. # its interface is available directly.
  8829. attr_accessor :tempfile
  8830. # A string with the headers of the multipart request.
  8831. attr_accessor :headers
  8832. def initialize(hash) # :nodoc:
  8833. @tempfile = hash[:tempfile]
  8834. raise(ArgumentError, ':tempfile is required') unless @tempfile
  8835. @original_filename = encode_filename(hash[:filename])
  8836. @content_type = hash[:type]
  8837. @headers = hash[:head]
  8838. end
  8839. # Shortcut for +tempfile.read+.
  8840. def read(length=nil, buffer=nil)
  8841. @tempfile.read(length, buffer)
  8842. end
  8843. # Shortcut for +tempfile.open+.
  8844. def open
  8845. @tempfile.open
  8846. end
  8847. # Shortcut for +tempfile.close+.
  8848. def close(unlink_now=false)
  8849. @tempfile.close(unlink_now)
  8850. end
  8851. # Shortcut for +tempfile.path+.
  8852. def path
  8853. @tempfile.path
  8854. end
  8855. # Shortcut for +tempfile.rewind+.
  8856. def rewind
  8857. @tempfile.rewind
  8858. end
  8859. # Shortcut for +tempfile.size+.
  8860. def size
  8861. @tempfile.size
  8862. end
  8863. # Shortcut for +tempfile.eof?+.
  8864. def eof?
  8865. @tempfile.eof?
  8866. end
  8867. private
  8868. def encode_filename(filename)
  8869. # Encode the filename in the utf8 encoding, unless it is nil
  8870. filename.force_encoding(Encoding::UTF_8).encode! if filename
  8871. end
  8872. end
  8873. module Upload # :nodoc:
  8874. # Convert nested Hash to ActiveSupport::HashWithIndifferentAccess and replace
  8875. # file upload hash with UploadedFile objects
  8876. def normalize_parameters(value)
  8877. if Hash === value && value.has_key?(:tempfile)
  8878. UploadedFile.new(value)
  8879. else
  8880. super
  8881. end
  8882. end
  8883. private :normalize_parameters
  8884. end
  8885. end
  8886. end
  8887. module ActionDispatch
  8888. module Http
  8889. module URL
  8890. IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
  8891. mattr_accessor :tld_length
  8892. self.tld_length = 1
  8893. class << self
  8894. def extract_domain(host, tld_length = @@tld_length)
  8895. host.split('.').last(1 + tld_length).join('.') if named_host?(host)
  8896. end
  8897. def extract_subdomains(host, tld_length = @@tld_length)
  8898. if named_host?(host)
  8899. parts = host.split('.')
  8900. parts[0..-(tld_length + 2)]
  8901. else
  8902. []
  8903. end
  8904. end
  8905. def extract_subdomain(host, tld_length = @@tld_length)
  8906. extract_subdomains(host, tld_length).join('.')
  8907. end
  8908. def url_for(options = {})
  8909. path = options.delete(:script_name).to_s.chomp("/")
  8910. path << options.delete(:path).to_s
  8911. params = options[:params].is_a?(Hash) ? options[:params] : options.slice(:params)
  8912. params.reject! { |_,v| v.to_param.nil? }
  8913. result = build_host_url(options)
  8914. if options[:trailing_slash]
  8915. if path.include?('?')
  8916. result << path.sub(/\?/, '/\&')
  8917. else
  8918. result << path.sub(/[^\/]\z|\A\z/, '\&/')
  8919. end
  8920. else
  8921. result << path
  8922. end
  8923. result << "?#{params.to_query}" unless params.empty?
  8924. result << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
  8925. result
  8926. end
  8927. private
  8928. def build_host_url(options)
  8929. if options[:host].blank? && options[:only_path].blank?
  8930. raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
  8931. end
  8932. result = ""
  8933. unless options[:only_path]
  8934. unless options[:protocol] == false
  8935. result << (options[:protocol] || "http")
  8936. result << ":" unless result.match(%r{:|//})
  8937. end
  8938. result << "//" unless result.match("//")
  8939. result << rewrite_authentication(options)
  8940. result << host_or_subdomain_and_domain(options)
  8941. result << ":#{options.delete(:port)}" if options[:port]
  8942. end
  8943. result
  8944. end
  8945. def named_host?(host)
  8946. host && IP_HOST_REGEXP !~ host
  8947. end
  8948. def rewrite_authentication(options)
  8949. if options[:user] && options[:password]
  8950. "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
  8951. else
  8952. ""
  8953. end
  8954. end
  8955. def host_or_subdomain_and_domain(options)
  8956. return options[:host] if !named_host?(options[:host]) || (options[:subdomain].nil? && options[:domain].nil?)
  8957. tld_length = options[:tld_length] || @@tld_length
  8958. host = ""
  8959. unless options[:subdomain] == false
  8960. host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)).to_param
  8961. host << "."
  8962. end
  8963. host << (options[:domain] || extract_domain(options[:host], tld_length))
  8964. host
  8965. end
  8966. end
  8967. def initialize(env)
  8968. super
  8969. @protocol = nil
  8970. @port = nil
  8971. end
  8972. # Returns the complete URL used for this request.
  8973. def url
  8974. protocol + host_with_port + fullpath
  8975. end
  8976. # Returns 'https://' if this is an SSL request and 'http://' otherwise.
  8977. def protocol
  8978. @protocol ||= ssl? ? 'https://' : 'http://'
  8979. end
  8980. # Returns the \host for this request, such as "example.com".
  8981. def raw_host_with_port
  8982. if forwarded = env["HTTP_X_FORWARDED_HOST"]
  8983. forwarded.split(/,\s?/).last
  8984. else
  8985. env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
  8986. end
  8987. end
  8988. # Returns the host for this request, such as example.com.
  8989. def host
  8990. raw_host_with_port.sub(/:\d+$/, '')
  8991. end
  8992. # Returns a \host:\port string for this request, such as "example.com" or
  8993. # "example.com:8080".
  8994. def host_with_port
  8995. "#{host}#{port_string}"
  8996. end
  8997. # Returns the port number of this request as an integer.
  8998. def port
  8999. @port ||= begin
  9000. if raw_host_with_port =~ /:(\d+)$/
  9001. $1.to_i
  9002. else
  9003. standard_port
  9004. end
  9005. end
  9006. end
  9007. # Returns the standard \port number for this request's protocol.
  9008. def standard_port
  9009. case protocol
  9010. when 'https://' then 443
  9011. else 80
  9012. end
  9013. end
  9014. # Returns whether this request is using the standard port
  9015. def standard_port?
  9016. port == standard_port
  9017. end
  9018. # Returns a number \port suffix like 8080 if the \port number of this request
  9019. # is not the default HTTP \port 80 or HTTPS \port 443.
  9020. def optional_port
  9021. standard_port? ? nil : port
  9022. end
  9023. # Returns a string \port suffix, including colon, like ":8080" if the \port
  9024. # number of this request is not the default HTTP \port 80 or HTTPS \port 443.
  9025. def port_string
  9026. standard_port? ? '' : ":#{port}"
  9027. end
  9028. def server_port
  9029. @env['SERVER_PORT'].to_i
  9030. end
  9031. # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
  9032. # a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
  9033. def domain(tld_length = @@tld_length)
  9034. ActionDispatch::Http::URL.extract_domain(host, tld_length)
  9035. end
  9036. # Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
  9037. # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
  9038. # such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
  9039. # in "www.rubyonrails.co.uk".
  9040. def subdomains(tld_length = @@tld_length)
  9041. ActionDispatch::Http::URL.extract_subdomains(host, tld_length)
  9042. end
  9043. # Returns all the \subdomains as a string, so <tt>"dev.www"</tt> would be
  9044. # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
  9045. # such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt>
  9046. # in "www.rubyonrails.co.uk".
  9047. def subdomain(tld_length = @@tld_length)
  9048. ActionDispatch::Http::URL.extract_subdomain(host, tld_length)
  9049. end
  9050. end
  9051. end
  9052. end
  9053. module Rack # :nodoc:
  9054. Mount = ActionDispatch::Journey::Router
  9055. Mount::RouteSet = ActionDispatch::Journey::Router
  9056. Mount::RegexpWithNamedGroups = ActionDispatch::Journey::Path::Pattern
  9057. end
  9058. require 'action_controller/metal/exceptions'
  9059. module ActionDispatch
  9060. module Journey
  9061. # The Formatter class is used for formatting URLs. For example, parameters
  9062. # passed to +url_for+ in rails will eventually call Formatter#generate.
  9063. class Formatter # :nodoc:
  9064. attr_reader :routes
  9065. def initialize(routes)
  9066. @routes = routes
  9067. @cache = nil
  9068. end
  9069. def generate(type, name, options, recall = {}, parameterize = nil)
  9070. constraints = recall.merge(options)
  9071. missing_keys = []
  9072. match_route(name, constraints) do |route|
  9073. parameterized_parts = extract_parameterized_parts(route, options, recall, parameterize)
  9074. next if !name && route.requirements.empty? && route.parts.empty?
  9075. missing_keys = missing_keys(route, parameterized_parts)
  9076. next unless missing_keys.empty?
  9077. params = options.dup.delete_if do |key, _|
  9078. parameterized_parts.key?(key) || route.defaults.key?(key)
  9079. end
  9080. return [route.format(parameterized_parts), params]
  9081. end
  9082. message = "No route matches #{constraints.inspect}"
  9083. message << " missing required keys: #{missing_keys.inspect}" if name
  9084. raise ActionController::UrlGenerationError, message
  9085. end
  9086. def clear
  9087. @cache = nil
  9088. end
  9089. private
  9090. def extract_parameterized_parts(route, options, recall, parameterize = nil)
  9091. parameterized_parts = recall.merge(options)
  9092. keys_to_keep = route.parts.reverse.drop_while { |part|
  9093. !options.key?(part) || (options[part] || recall[part]).nil?
  9094. } | route.required_parts
  9095. (parameterized_parts.keys - keys_to_keep).each do |bad_key|
  9096. parameterized_parts.delete(bad_key)
  9097. end
  9098. if parameterize
  9099. parameterized_parts.each do |k, v|
  9100. parameterized_parts[k] = parameterize.call(k, v)
  9101. end
  9102. end
  9103. parameterized_parts.keep_if { |_, v| v }
  9104. parameterized_parts
  9105. end
  9106. def named_routes
  9107. routes.named_routes
  9108. end
  9109. def match_route(name, options)
  9110. if named_routes.key?(name)
  9111. yield named_routes[name]
  9112. else
  9113. routes = non_recursive(cache, options.to_a)
  9114. hash = routes.group_by { |_, r| r.score(options) }
  9115. hash.keys.sort.reverse_each do |score|
  9116. next if score < 0
  9117. hash[score].sort_by { |i, _| i }.each do |_, route|
  9118. yield route
  9119. end
  9120. end
  9121. end
  9122. end
  9123. def non_recursive(cache, options)
  9124. routes = []
  9125. stack = [cache]
  9126. while stack.any?
  9127. c = stack.shift
  9128. routes.concat(c[:___routes]) if c.key?(:___routes)
  9129. options.each do |pair|
  9130. stack << c[pair] if c.key?(pair)
  9131. end
  9132. end
  9133. routes
  9134. end
  9135. # Returns an array populated with missing keys if any are present.
  9136. def missing_keys(route, parts)
  9137. missing_keys = []
  9138. tests = route.path.requirements
  9139. route.required_parts.each { |key|
  9140. if tests.key?(key)
  9141. missing_keys << key unless /\A#{tests[key]}\Z/ === parts[key]
  9142. else
  9143. missing_keys << key unless parts[key]
  9144. end
  9145. }
  9146. missing_keys
  9147. end
  9148. def possibles(cache, options, depth = 0)
  9149. cache.fetch(:___routes) { [] } + options.find_all { |pair|
  9150. cache.key?(pair)
  9151. }.map { |pair|
  9152. possibles(cache[pair], options, depth + 1)
  9153. }.flatten(1)
  9154. end
  9155. # Returns +true+ if no missing keys are present, otherwise +false+.
  9156. def verify_required_parts!(route, parts)
  9157. missing_keys(route, parts).empty?
  9158. end
  9159. def build_cache
  9160. root = { ___routes: [] }
  9161. routes.each_with_index do |route, i|
  9162. leaf = route.required_defaults.inject(root) do |h, tuple|
  9163. h[tuple] ||= {}
  9164. end
  9165. (leaf[:___routes] ||= []) << [i, route]
  9166. end
  9167. root
  9168. end
  9169. def cache
  9170. @cache ||= build_cache
  9171. end
  9172. end
  9173. end
  9174. end
  9175. require 'action_dispatch/journey/gtg/transition_table'
  9176. module ActionDispatch
  9177. module Journey # :nodoc:
  9178. module GTG # :nodoc:
  9179. class Builder # :nodoc:
  9180. DUMMY = Nodes::Dummy.new
  9181. attr_reader :root, :ast, :endpoints
  9182. def initialize(root)
  9183. @root = root
  9184. @ast = Nodes::Cat.new root, DUMMY
  9185. @followpos = nil
  9186. end
  9187. def transition_table
  9188. dtrans = TransitionTable.new
  9189. marked = {}
  9190. state_id = Hash.new { |h,k| h[k] = h.length }
  9191. start = firstpos(root)
  9192. dstates = [start]
  9193. until dstates.empty?
  9194. s = dstates.shift
  9195. next if marked[s]
  9196. marked[s] = true # mark s
  9197. s.group_by { |state| symbol(state) }.each do |sym, ps|
  9198. u = ps.map { |l| followpos(l) }.flatten
  9199. next if u.empty?
  9200. if u.uniq == [DUMMY]
  9201. from = state_id[s]
  9202. to = state_id[Object.new]
  9203. dtrans[from, to] = sym
  9204. dtrans.add_accepting(to)
  9205. ps.each { |state| dtrans.add_memo(to, state.memo) }
  9206. else
  9207. dtrans[state_id[s], state_id[u]] = sym
  9208. if u.include?(DUMMY)
  9209. to = state_id[u]
  9210. accepting = ps.find_all { |l| followpos(l).include?(DUMMY) }
  9211. accepting.each { |accepting_state|
  9212. dtrans.add_memo(to, accepting_state.memo)
  9213. }
  9214. dtrans.add_accepting(state_id[u])
  9215. end
  9216. end
  9217. dstates << u
  9218. end
  9219. end
  9220. dtrans
  9221. end
  9222. def nullable?(node)
  9223. case node
  9224. when Nodes::Group
  9225. true
  9226. when Nodes::Star
  9227. true
  9228. when Nodes::Or
  9229. node.children.any? { |c| nullable?(c) }
  9230. when Nodes::Cat
  9231. nullable?(node.left) && nullable?(node.right)
  9232. when Nodes::Terminal
  9233. !node.left
  9234. when Nodes::Unary
  9235. nullable?(node.left)
  9236. else
  9237. raise ArgumentError, 'unknown nullable: %s' % node.class.name
  9238. end
  9239. end
  9240. def firstpos(node)
  9241. case node
  9242. when Nodes::Star
  9243. firstpos(node.left)
  9244. when Nodes::Cat
  9245. if nullable?(node.left)
  9246. firstpos(node.left) | firstpos(node.right)
  9247. else
  9248. firstpos(node.left)
  9249. end
  9250. when Nodes::Or
  9251. node.children.map { |c| firstpos(c) }.flatten.uniq
  9252. when Nodes::Unary
  9253. firstpos(node.left)
  9254. when Nodes::Terminal
  9255. nullable?(node) ? [] : [node]
  9256. else
  9257. raise ArgumentError, 'unknown firstpos: %s' % node.class.name
  9258. end
  9259. end
  9260. def lastpos(node)
  9261. case node
  9262. when Nodes::Star
  9263. firstpos(node.left)
  9264. when Nodes::Or
  9265. node.children.map { |c| lastpos(c) }.flatten.uniq
  9266. when Nodes::Cat
  9267. if nullable?(node.right)
  9268. lastpos(node.left) | lastpos(node.right)
  9269. else
  9270. lastpos(node.right)
  9271. end
  9272. when Nodes::Terminal
  9273. nullable?(node) ? [] : [node]
  9274. when Nodes::Unary
  9275. lastpos(node.left)
  9276. else
  9277. raise ArgumentError, 'unknown lastpos: %s' % node.class.name
  9278. end
  9279. end
  9280. def followpos(node)
  9281. followpos_table[node]
  9282. end
  9283. private
  9284. def followpos_table
  9285. @followpos ||= build_followpos
  9286. end
  9287. def build_followpos
  9288. table = Hash.new { |h, k| h[k] = [] }
  9289. @ast.each do |n|
  9290. case n
  9291. when Nodes::Cat
  9292. lastpos(n.left).each do |i|
  9293. table[i] += firstpos(n.right)
  9294. end
  9295. when Nodes::Star
  9296. lastpos(n).each do |i|
  9297. table[i] += firstpos(n)
  9298. end
  9299. end
  9300. end
  9301. table
  9302. end
  9303. def symbol(edge)
  9304. case edge
  9305. when Journey::Nodes::Symbol
  9306. edge.regexp
  9307. else
  9308. edge.left
  9309. end
  9310. end
  9311. end
  9312. end
  9313. end
  9314. end
  9315. require 'strscan'
  9316. module ActionDispatch
  9317. module Journey # :nodoc:
  9318. module GTG # :nodoc:
  9319. class MatchData # :nodoc:
  9320. attr_reader :memos
  9321. def initialize(memos)
  9322. @memos = memos
  9323. end
  9324. end
  9325. class Simulator # :nodoc:
  9326. attr_reader :tt
  9327. def initialize(transition_table)
  9328. @tt = transition_table
  9329. end
  9330. def simulate(string)
  9331. input = StringScanner.new(string)
  9332. state = [0]
  9333. while sym = input.scan(%r([/.?]|[^/.?]+))
  9334. state = tt.move(state, sym)
  9335. end
  9336. acceptance_states = state.find_all { |s|
  9337. tt.accepting? s
  9338. }
  9339. return if acceptance_states.empty?
  9340. memos = acceptance_states.map { |x| tt.memo(x) }.flatten.compact
  9341. MatchData.new(memos)
  9342. end
  9343. alias :=~ :simulate
  9344. alias :match :simulate
  9345. end
  9346. end
  9347. end
  9348. end
  9349. require 'action_dispatch/journey/nfa/dot'
  9350. module ActionDispatch
  9351. module Journey # :nodoc:
  9352. module GTG # :nodoc:
  9353. class TransitionTable # :nodoc:
  9354. include Journey::NFA::Dot
  9355. attr_reader :memos
  9356. def initialize
  9357. @regexp_states = Hash.new { |h,k| h[k] = {} }
  9358. @string_states = Hash.new { |h,k| h[k] = {} }
  9359. @accepting = {}
  9360. @memos = Hash.new { |h,k| h[k] = [] }
  9361. end
  9362. def add_accepting(state)
  9363. @accepting[state] = true
  9364. end
  9365. def accepting_states
  9366. @accepting.keys
  9367. end
  9368. def accepting?(state)
  9369. @accepting[state]
  9370. end
  9371. def add_memo(idx, memo)
  9372. @memos[idx] << memo
  9373. end
  9374. def memo(idx)
  9375. @memos[idx]
  9376. end
  9377. def eclosure(t)
  9378. Array(t)
  9379. end
  9380. def move(t, a)
  9381. move_string(t, a).concat(move_regexp(t, a))
  9382. end
  9383. def to_json
  9384. require 'json'
  9385. simple_regexp = Hash.new { |h,k| h[k] = {} }
  9386. @regexp_states.each do |from, hash|
  9387. hash.each do |re, to|
  9388. simple_regexp[from][re.source] = to
  9389. end
  9390. end
  9391. JSON.dump({
  9392. regexp_states: simple_regexp,
  9393. string_states: @string_states,
  9394. accepting: @accepting
  9395. })
  9396. end
  9397. def to_svg
  9398. svg = IO.popen('dot -Tsvg', 'w+') { |f|
  9399. f.write(to_dot)
  9400. f.close_write
  9401. f.readlines
  9402. }
  9403. 3.times { svg.shift }
  9404. svg.join.sub(/width="[^"]*"/, '').sub(/height="[^"]*"/, '')
  9405. end
  9406. def visualizer(paths, title = 'FSM')
  9407. viz_dir = File.join File.dirname(__FILE__), '..', 'visualizer'
  9408. fsm_js = File.read File.join(viz_dir, 'fsm.js')
  9409. fsm_css = File.read File.join(viz_dir, 'fsm.css')
  9410. erb = File.read File.join(viz_dir, 'index.html.erb')
  9411. states = "function tt() { return #{to_json}; }"
  9412. fun_routes = paths.shuffle.first(3).map do |ast|
  9413. ast.map { |n|
  9414. case n
  9415. when Nodes::Symbol
  9416. case n.left
  9417. when ':id' then rand(100).to_s
  9418. when ':format' then %w{ xml json }.shuffle.first
  9419. else
  9420. 'omg'
  9421. end
  9422. when Nodes::Terminal then n.symbol
  9423. else
  9424. nil
  9425. end
  9426. }.compact.join
  9427. end
  9428. stylesheets = [fsm_css]
  9429. svg = to_svg
  9430. javascripts = [states, fsm_js]
  9431. # Annoying hack for 1.9 warnings
  9432. fun_routes = fun_routes
  9433. stylesheets = stylesheets
  9434. svg = svg
  9435. javascripts = javascripts
  9436. require 'erb'
  9437. template = ERB.new erb
  9438. template.result(binding)
  9439. end
  9440. def []=(from, to, sym)
  9441. case sym
  9442. when String
  9443. @string_states[from][sym] = to
  9444. when Regexp
  9445. @regexp_states[from][sym] = to
  9446. else
  9447. raise ArgumentError, 'unknown symbol: %s' % sym.class
  9448. end
  9449. end
  9450. def states
  9451. ss = @string_states.keys + @string_states.values.map(&:values).flatten
  9452. rs = @regexp_states.keys + @regexp_states.values.map(&:values).flatten
  9453. (ss + rs).uniq
  9454. end
  9455. def transitions
  9456. @string_states.map { |from, hash|
  9457. hash.map { |s, to| [from, s, to] }
  9458. }.flatten(1) + @regexp_states.map { |from, hash|
  9459. hash.map { |s, to| [from, s, to] }
  9460. }.flatten(1)
  9461. end
  9462. private
  9463. def move_regexp(t, a)
  9464. return [] if t.empty?
  9465. t.map { |s|
  9466. @regexp_states[s].map { |re, v| re === a ? v : nil }
  9467. }.flatten.compact.uniq
  9468. end
  9469. def move_string(t, a)
  9470. return [] if t.empty?
  9471. t.map { |s| @string_states[s][a] }.compact
  9472. end
  9473. end
  9474. end
  9475. end
  9476. end
  9477. require 'action_dispatch/journey/nfa/transition_table'
  9478. require 'action_dispatch/journey/gtg/transition_table'
  9479. module ActionDispatch
  9480. module Journey # :nodoc:
  9481. module NFA # :nodoc:
  9482. class Visitor < Visitors::Visitor # :nodoc:
  9483. def initialize(tt)
  9484. @tt = tt
  9485. @i = -1
  9486. end
  9487. def visit_CAT(node)
  9488. left = visit(node.left)
  9489. right = visit(node.right)
  9490. @tt.merge(left.last, right.first)
  9491. [left.first, right.last]
  9492. end
  9493. def visit_GROUP(node)
  9494. from = @i += 1
  9495. left = visit(node.left)
  9496. to = @i += 1
  9497. @tt.accepting = to
  9498. @tt[from, left.first] = nil
  9499. @tt[left.last, to] = nil
  9500. @tt[from, to] = nil
  9501. [from, to]
  9502. end
  9503. def visit_OR(node)
  9504. from = @i += 1
  9505. children = node.children.map { |c| visit(c) }
  9506. to = @i += 1
  9507. children.each do |child|
  9508. @tt[from, child.first] = nil
  9509. @tt[child.last, to] = nil
  9510. end
  9511. @tt.accepting = to
  9512. [from, to]
  9513. end
  9514. def terminal(node)
  9515. from_i = @i += 1 # new state
  9516. to_i = @i += 1 # new state
  9517. @tt[from_i, to_i] = node
  9518. @tt.accepting = to_i
  9519. @tt.add_memo(to_i, node.memo)
  9520. [from_i, to_i]
  9521. end
  9522. end
  9523. class Builder # :nodoc:
  9524. def initialize(ast)
  9525. @ast = ast
  9526. end
  9527. def transition_table
  9528. tt = TransitionTable.new
  9529. Visitor.new(tt).accept(@ast)
  9530. tt
  9531. end
  9532. end
  9533. end
  9534. end
  9535. end
  9536. # encoding: utf-8
  9537. module ActionDispatch
  9538. module Journey # :nodoc:
  9539. module NFA # :nodoc:
  9540. module Dot # :nodoc:
  9541. def to_dot
  9542. edges = transitions.map { |from, sym, to|
  9543. " #{from} -> #{to} [label=\"#{sym || 'ε'}\"];"
  9544. }
  9545. #memo_nodes = memos.values.flatten.map { |n|
  9546. # label = n
  9547. # if Journey::Route === n
  9548. # label = "#{n.verb.source} #{n.path.spec}"
  9549. # end
  9550. # " #{n.object_id} [label=\"#{label}\", shape=box];"
  9551. #}
  9552. #memo_edges = memos.map { |k, memos|
  9553. # (memos || []).map { |v| " #{k} -> #{v.object_id};" }
  9554. #}.flatten.uniq
  9555. <<-eodot
  9556. digraph nfa {
  9557. rankdir=LR;
  9558. node [shape = doublecircle];
  9559. #{accepting_states.join ' '};
  9560. node [shape = circle];
  9561. #{edges.join "\n"}
  9562. }
  9563. eodot
  9564. end
  9565. end
  9566. end
  9567. end
  9568. end
  9569. require 'strscan'
  9570. module ActionDispatch
  9571. module Journey # :nodoc:
  9572. module NFA # :nodoc:
  9573. class MatchData # :nodoc:
  9574. attr_reader :memos
  9575. def initialize(memos)
  9576. @memos = memos
  9577. end
  9578. end
  9579. class Simulator # :nodoc:
  9580. attr_reader :tt
  9581. def initialize(transition_table)
  9582. @tt = transition_table
  9583. end
  9584. def simulate(string)
  9585. input = StringScanner.new(string)
  9586. state = tt.eclosure(0)
  9587. until input.eos?
  9588. sym = input.scan(%r([/.?]|[^/.?]+))
  9589. # FIXME: tt.eclosure is not needed for the GTG
  9590. state = tt.eclosure(tt.move(state, sym))
  9591. end
  9592. acceptance_states = state.find_all { |s|
  9593. tt.accepting?(tt.eclosure(s).sort.last)
  9594. }
  9595. return if acceptance_states.empty?
  9596. memos = acceptance_states.map { |x| tt.memo(x) }.flatten.compact
  9597. MatchData.new(memos)
  9598. end
  9599. alias :=~ :simulate
  9600. alias :match :simulate
  9601. end
  9602. end
  9603. end
  9604. end
  9605. require 'action_dispatch/journey/nfa/dot'
  9606. module ActionDispatch
  9607. module Journey # :nodoc:
  9608. module NFA # :nodoc:
  9609. class TransitionTable # :nodoc:
  9610. include Journey::NFA::Dot
  9611. attr_accessor :accepting
  9612. attr_reader :memos
  9613. def initialize
  9614. @table = Hash.new { |h,f| h[f] = {} }
  9615. @memos = {}
  9616. @accepting = nil
  9617. @inverted = nil
  9618. end
  9619. def accepting?(state)
  9620. accepting == state
  9621. end
  9622. def accepting_states
  9623. [accepting]
  9624. end
  9625. def add_memo(idx, memo)
  9626. @memos[idx] = memo
  9627. end
  9628. def memo(idx)
  9629. @memos[idx]
  9630. end
  9631. def []=(i, f, s)
  9632. @table[f][i] = s
  9633. end
  9634. def merge(left, right)
  9635. @memos[right] = @memos.delete(left)
  9636. @table[right] = @table.delete(left)
  9637. end
  9638. def states
  9639. (@table.keys + @table.values.map(&:keys).flatten).uniq
  9640. end
  9641. # Returns a generalized transition graph with reduced states. The states
  9642. # are reduced like a DFA, but the table must be simulated like an NFA.
  9643. #
  9644. # Edges of the GTG are regular expressions.
  9645. def generalized_table
  9646. gt = GTG::TransitionTable.new
  9647. marked = {}
  9648. state_id = Hash.new { |h,k| h[k] = h.length }
  9649. alphabet = self.alphabet
  9650. stack = [eclosure(0)]
  9651. until stack.empty?
  9652. state = stack.pop
  9653. next if marked[state] || state.empty?
  9654. marked[state] = true
  9655. alphabet.each do |alpha|
  9656. next_state = eclosure(following_states(state, alpha))
  9657. next if next_state.empty?
  9658. gt[state_id[state], state_id[next_state]] = alpha
  9659. stack << next_state
  9660. end
  9661. end
  9662. final_groups = state_id.keys.find_all { |s|
  9663. s.sort.last == accepting
  9664. }
  9665. final_groups.each do |states|
  9666. id = state_id[states]
  9667. gt.add_accepting(id)
  9668. save = states.find { |s|
  9669. @memos.key?(s) && eclosure(s).sort.last == accepting
  9670. }
  9671. gt.add_memo(id, memo(save))
  9672. end
  9673. gt
  9674. end
  9675. # Returns set of NFA states to which there is a transition on ast symbol
  9676. # +a+ from some state +s+ in +t+.
  9677. def following_states(t, a)
  9678. Array(t).map { |s| inverted[s][a] }.flatten.uniq
  9679. end
  9680. # Returns set of NFA states to which there is a transition on ast symbol
  9681. # +a+ from some state +s+ in +t+.
  9682. def move(t, a)
  9683. Array(t).map { |s|
  9684. inverted[s].keys.compact.find_all { |sym|
  9685. sym === a
  9686. }.map { |sym| inverted[s][sym] }
  9687. }.flatten.uniq
  9688. end
  9689. def alphabet
  9690. inverted.values.map(&:keys).flatten.compact.uniq.sort_by { |x| x.to_s }
  9691. end
  9692. # Returns a set of NFA states reachable from some NFA state +s+ in set
  9693. # +t+ on nil-transitions alone.
  9694. def eclosure(t)
  9695. stack = Array(t)
  9696. seen = {}
  9697. children = []
  9698. until stack.empty?
  9699. s = stack.pop
  9700. next if seen[s]
  9701. seen[s] = true
  9702. children << s
  9703. stack.concat(inverted[s][nil])
  9704. end
  9705. children.uniq
  9706. end
  9707. def transitions
  9708. @table.map { |to, hash|
  9709. hash.map { |from, sym| [from, sym, to] }
  9710. }.flatten(1)
  9711. end
  9712. private
  9713. def inverted
  9714. return @inverted if @inverted
  9715. @inverted = Hash.new { |h, from|
  9716. h[from] = Hash.new { |j, s| j[s] = [] }
  9717. }
  9718. @table.each { |to, hash|
  9719. hash.each { |from, sym|
  9720. if sym
  9721. sym = Nodes::Symbol === sym ? sym.regexp : sym.left
  9722. end
  9723. @inverted[from][sym] << to
  9724. }
  9725. }
  9726. @inverted
  9727. end
  9728. end
  9729. end
  9730. end
  9731. end
  9732. require 'action_dispatch/journey/visitors'
  9733. module ActionDispatch
  9734. module Journey # :nodoc:
  9735. module Nodes # :nodoc:
  9736. class Node # :nodoc:
  9737. include Enumerable
  9738. attr_accessor :left, :memo
  9739. def initialize(left)
  9740. @left = left
  9741. @memo = nil
  9742. end
  9743. def each(&block)
  9744. Visitors::Each.new(block).accept(self)
  9745. end
  9746. def to_s
  9747. Visitors::String.new.accept(self)
  9748. end
  9749. def to_dot
  9750. Visitors::Dot.new.accept(self)
  9751. end
  9752. def to_sym
  9753. name.to_sym
  9754. end
  9755. def name
  9756. left.tr '*:', ''
  9757. end
  9758. def type
  9759. raise NotImplementedError
  9760. end
  9761. def symbol?; false; end
  9762. def literal?; false; end
  9763. end
  9764. class Terminal < Node # :nodoc:
  9765. alias :symbol :left
  9766. end
  9767. class Literal < Terminal # :nodoc:
  9768. def literal?; true; end
  9769. def type; :LITERAL; end
  9770. end
  9771. class Dummy < Literal # :nodoc:
  9772. def initialize(x = Object.new)
  9773. super
  9774. end
  9775. def literal?; false; end
  9776. end
  9777. %w{ Symbol Slash Dot }.each do |t|
  9778. class_eval <<-eoruby, __FILE__, __LINE__ + 1
  9779. class #{t} < Terminal;
  9780. def type; :#{t.upcase}; end
  9781. end
  9782. eoruby
  9783. end
  9784. class Symbol < Terminal # :nodoc:
  9785. attr_accessor :regexp
  9786. alias :symbol :regexp
  9787. DEFAULT_EXP = /[^\.\/\?]+/
  9788. def initialize(left)
  9789. super
  9790. @regexp = DEFAULT_EXP
  9791. end
  9792. def default_regexp?
  9793. regexp == DEFAULT_EXP
  9794. end
  9795. def symbol?; true; end
  9796. end
  9797. class Unary < Node # :nodoc:
  9798. def children; [left] end
  9799. end
  9800. class Group < Unary # :nodoc:
  9801. def type; :GROUP; end
  9802. end
  9803. class Star < Unary # :nodoc:
  9804. def type; :STAR; end
  9805. end
  9806. class Binary < Node # :nodoc:
  9807. attr_accessor :right
  9808. def initialize(left, right)
  9809. super(left)
  9810. @right = right
  9811. end
  9812. def children; [left, right] end
  9813. end
  9814. class Cat < Binary # :nodoc:
  9815. def type; :CAT; end
  9816. end
  9817. class Or < Node # :nodoc:
  9818. attr_reader :children
  9819. def initialize(children)
  9820. @children = children
  9821. end
  9822. def type; :OR; end
  9823. end
  9824. end
  9825. end
  9826. end
  9827. #
  9828. # DO NOT MODIFY!!!!
  9829. # This file is automatically generated by Racc 1.4.9
  9830. # from Racc grammer file "".
  9831. #
  9832. require 'racc/parser.rb'
  9833. require 'action_dispatch/journey/parser_extras'
  9834. module ActionDispatch
  9835. module Journey # :nodoc:
  9836. class Parser < Racc::Parser # :nodoc:
  9837. ##### State transition tables begin ###
  9838. racc_action_table = [
  9839. 17, 21, 13, 15, 14, 7, nil, 16, 8, 19,
  9840. 13, 15, 14, 7, 23, 16, 8, 19, 13, 15,
  9841. 14, 7, nil, 16, 8, 13, 15, 14, 7, nil,
  9842. 16, 8, 13, 15, 14, 7, nil, 16, 8 ]
  9843. racc_action_check = [
  9844. 1, 17, 1, 1, 1, 1, nil, 1, 1, 1,
  9845. 20, 20, 20, 20, 20, 20, 20, 20, 7, 7,
  9846. 7, 7, nil, 7, 7, 19, 19, 19, 19, nil,
  9847. 19, 19, 0, 0, 0, 0, nil, 0, 0 ]
  9848. racc_action_pointer = [
  9849. 30, 0, nil, nil, nil, nil, nil, 16, nil, nil,
  9850. nil, nil, nil, nil, nil, nil, nil, 1, nil, 23,
  9851. 8, nil, nil, nil ]
  9852. racc_action_default = [
  9853. -18, -18, -2, -3, -4, -5, -6, -18, -9, -10,
  9854. -11, -12, -13, -14, -15, -16, -17, -18, -1, -18,
  9855. -18, 24, -8, -7 ]
  9856. racc_goto_table = [
  9857. 18, 1, nil, nil, nil, nil, nil, nil, 20, nil,
  9858. nil, nil, nil, nil, nil, nil, nil, nil, 22, 18 ]
  9859. racc_goto_check = [
  9860. 2, 1, nil, nil, nil, nil, nil, nil, 1, nil,
  9861. nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ]
  9862. racc_goto_pointer = [
  9863. nil, 1, -1, nil, nil, nil, nil, nil, nil, nil,
  9864. nil ]
  9865. racc_goto_default = [
  9866. nil, nil, 2, 3, 4, 5, 6, 9, 10, 11,
  9867. 12 ]
  9868. racc_reduce_table = [
  9869. 0, 0, :racc_error,
  9870. 2, 11, :_reduce_1,
  9871. 1, 11, :_reduce_2,
  9872. 1, 11, :_reduce_none,
  9873. 1, 12, :_reduce_none,
  9874. 1, 12, :_reduce_none,
  9875. 1, 12, :_reduce_none,
  9876. 3, 15, :_reduce_7,
  9877. 3, 13, :_reduce_8,
  9878. 1, 16, :_reduce_9,
  9879. 1, 14, :_reduce_none,
  9880. 1, 14, :_reduce_none,
  9881. 1, 14, :_reduce_none,
  9882. 1, 14, :_reduce_none,
  9883. 1, 19, :_reduce_14,
  9884. 1, 17, :_reduce_15,
  9885. 1, 18, :_reduce_16,
  9886. 1, 20, :_reduce_17 ]
  9887. racc_reduce_n = 18
  9888. racc_shift_n = 24
  9889. racc_token_table = {
  9890. false => 0,
  9891. :error => 1,
  9892. :SLASH => 2,
  9893. :LITERAL => 3,
  9894. :SYMBOL => 4,
  9895. :LPAREN => 5,
  9896. :RPAREN => 6,
  9897. :DOT => 7,
  9898. :STAR => 8,
  9899. :OR => 9 }
  9900. racc_nt_base = 10
  9901. racc_use_result_var = true
  9902. Racc_arg = [
  9903. racc_action_table,
  9904. racc_action_check,
  9905. racc_action_default,
  9906. racc_action_pointer,
  9907. racc_goto_table,
  9908. racc_goto_check,
  9909. racc_goto_default,
  9910. racc_goto_pointer,
  9911. racc_nt_base,
  9912. racc_reduce_table,
  9913. racc_token_table,
  9914. racc_shift_n,
  9915. racc_reduce_n,
  9916. racc_use_result_var ]
  9917. Racc_token_to_s_table = [
  9918. "$end",
  9919. "error",
  9920. "SLASH",
  9921. "LITERAL",
  9922. "SYMBOL",
  9923. "LPAREN",
  9924. "RPAREN",
  9925. "DOT",
  9926. "STAR",
  9927. "OR",
  9928. "$start",
  9929. "expressions",
  9930. "expression",
  9931. "or",
  9932. "terminal",
  9933. "group",
  9934. "star",
  9935. "symbol",
  9936. "literal",
  9937. "slash",
  9938. "dot" ]
  9939. Racc_debug_parser = false
  9940. ##### State transition tables end #####
  9941. # reduce 0 omitted
  9942. def _reduce_1(val, _values, result)
  9943. result = Cat.new(val.first, val.last)
  9944. result
  9945. end
  9946. def _reduce_2(val, _values, result)
  9947. result = val.first
  9948. result
  9949. end
  9950. # reduce 3 omitted
  9951. # reduce 4 omitted
  9952. # reduce 5 omitted
  9953. # reduce 6 omitted
  9954. def _reduce_7(val, _values, result)
  9955. result = Group.new(val[1])
  9956. result
  9957. end
  9958. def _reduce_8(val, _values, result)
  9959. result = Or.new([val.first, val.last])
  9960. result
  9961. end
  9962. def _reduce_9(val, _values, result)
  9963. result = Star.new(Symbol.new(val.last))
  9964. result
  9965. end
  9966. # reduce 10 omitted
  9967. # reduce 11 omitted
  9968. # reduce 12 omitted
  9969. # reduce 13 omitted
  9970. def _reduce_14(val, _values, result)
  9971. result = Slash.new('/')
  9972. result
  9973. end
  9974. def _reduce_15(val, _values, result)
  9975. result = Symbol.new(val.first)
  9976. result
  9977. end
  9978. def _reduce_16(val, _values, result)
  9979. result = Literal.new(val.first)
  9980. result
  9981. end
  9982. def _reduce_17(val, _values, result)
  9983. result = Dot.new(val.first)
  9984. result
  9985. end
  9986. def _reduce_none(val, _values, result)
  9987. val[0]
  9988. end
  9989. end # class Parser
  9990. end # module Journey
  9991. end # module ActionDispatch
  9992. require 'action_dispatch/journey/scanner'
  9993. require 'action_dispatch/journey/nodes/node'
  9994. module ActionDispatch
  9995. module Journey # :nodoc:
  9996. class Parser < Racc::Parser # :nodoc:
  9997. include Journey::Nodes
  9998. def initialize
  9999. @scanner = Scanner.new
  10000. end
  10001. def parse(string)
  10002. @scanner.scan_setup(string)
  10003. do_parse
  10004. end
  10005. def next_token
  10006. @scanner.next_token
  10007. end
  10008. end
  10009. end
  10010. end
  10011. module ActionDispatch
  10012. module Journey # :nodoc:
  10013. module Path # :nodoc:
  10014. class Pattern # :nodoc:
  10015. attr_reader :spec, :requirements, :anchored
  10016. def initialize(strexp)
  10017. parser = Journey::Parser.new
  10018. @anchored = true
  10019. case strexp
  10020. when String
  10021. @spec = parser.parse(strexp)
  10022. @requirements = {}
  10023. @separators = "/.?"
  10024. when Router::Strexp
  10025. @spec = parser.parse(strexp.path)
  10026. @requirements = strexp.requirements
  10027. @separators = strexp.separators.join
  10028. @anchored = strexp.anchor
  10029. else
  10030. raise "wtf bro: #{strexp}"
  10031. end
  10032. @names = nil
  10033. @optional_names = nil
  10034. @required_names = nil
  10035. @re = nil
  10036. @offsets = nil
  10037. end
  10038. def ast
  10039. @spec.grep(Nodes::Symbol).each do |node|
  10040. re = @requirements[node.to_sym]
  10041. node.regexp = re if re
  10042. end
  10043. @spec.grep(Nodes::Star).each do |node|
  10044. node = node.left
  10045. node.regexp = @requirements[node.to_sym] || /(.+)/
  10046. end
  10047. @spec
  10048. end
  10049. def names
  10050. @names ||= spec.grep(Nodes::Symbol).map { |n| n.name }
  10051. end
  10052. def required_names
  10053. @required_names ||= names - optional_names
  10054. end
  10055. def optional_names
  10056. @optional_names ||= spec.grep(Nodes::Group).map { |group|
  10057. group.grep(Nodes::Symbol)
  10058. }.flatten.map { |n| n.name }.uniq
  10059. end
  10060. class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
  10061. attr_reader :offsets
  10062. def initialize(matchers)
  10063. @matchers = matchers
  10064. @capture_count = [0]
  10065. end
  10066. def visit(node)
  10067. super
  10068. @capture_count
  10069. end
  10070. def visit_SYMBOL(node)
  10071. node = node.to_sym
  10072. if @matchers.key?(node)
  10073. re = /#{@matchers[node]}|/
  10074. @capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
  10075. else
  10076. @capture_count << (@capture_count.last || 0)
  10077. end
  10078. end
  10079. end
  10080. class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
  10081. def initialize(separator, matchers)
  10082. @separator = separator
  10083. @matchers = matchers
  10084. @separator_re = "([^#{separator}]+)"
  10085. super()
  10086. end
  10087. def accept(node)
  10088. %r{\A#{visit node}\Z}
  10089. end
  10090. def visit_CAT(node)
  10091. [visit(node.left), visit(node.right)].join
  10092. end
  10093. def visit_SYMBOL(node)
  10094. node = node.to_sym
  10095. return @separator_re unless @matchers.key?(node)
  10096. re = @matchers[node]
  10097. "(#{re})"
  10098. end
  10099. def visit_GROUP(node)
  10100. "(?:#{visit node.left})?"
  10101. end
  10102. def visit_LITERAL(node)
  10103. Regexp.escape(node.left)
  10104. end
  10105. alias :visit_DOT :visit_LITERAL
  10106. def visit_SLASH(node)
  10107. node.left
  10108. end
  10109. def visit_STAR(node)
  10110. re = @matchers[node.left.to_sym] || '.+'
  10111. "(#{re})"
  10112. end
  10113. end
  10114. class UnanchoredRegexp < AnchoredRegexp # :nodoc:
  10115. def accept(node)
  10116. %r{\A#{visit node}}
  10117. end
  10118. end
  10119. class MatchData # :nodoc:
  10120. attr_reader :names
  10121. def initialize(names, offsets, match)
  10122. @names = names
  10123. @offsets = offsets
  10124. @match = match
  10125. end
  10126. def captures
  10127. (length - 1).times.map { |i| self[i + 1] }
  10128. end
  10129. def [](x)
  10130. idx = @offsets[x - 1] + x
  10131. @match[idx]
  10132. end
  10133. def length
  10134. @offsets.length
  10135. end
  10136. def post_match
  10137. @match.post_match
  10138. end
  10139. def to_s
  10140. @match.to_s
  10141. end
  10142. end
  10143. def match(other)
  10144. return unless match = to_regexp.match(other)
  10145. MatchData.new(names, offsets, match)
  10146. end
  10147. alias :=~ :match
  10148. def source
  10149. to_regexp.source
  10150. end
  10151. def to_regexp
  10152. @re ||= regexp_visitor.new(@separators, @requirements).accept spec
  10153. end
  10154. private
  10155. def regexp_visitor
  10156. @anchored ? AnchoredRegexp : UnanchoredRegexp
  10157. end
  10158. def offsets
  10159. return @offsets if @offsets
  10160. viz = RegexpOffsets.new(@requirements)
  10161. @offsets = viz.accept(spec)
  10162. end
  10163. end
  10164. end
  10165. end
  10166. end
  10167. module ActionDispatch
  10168. module Journey # :nodoc:
  10169. class Route # :nodoc:
  10170. attr_reader :app, :path, :defaults, :name
  10171. attr_reader :constraints
  10172. alias :conditions :constraints
  10173. attr_accessor :precedence
  10174. ##
  10175. # +path+ is a path constraint.
  10176. # +constraints+ is a hash of constraints to be applied to this route.
  10177. def initialize(name, app, path, constraints, defaults = {})
  10178. @name = name
  10179. @app = app
  10180. @path = path
  10181. @constraints = constraints
  10182. @defaults = defaults
  10183. @required_defaults = nil
  10184. @required_parts = nil
  10185. @parts = nil
  10186. @decorated_ast = nil
  10187. @precedence = 0
  10188. end
  10189. def ast
  10190. @decorated_ast ||= begin
  10191. decorated_ast = path.ast
  10192. decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
  10193. decorated_ast
  10194. end
  10195. end
  10196. def requirements # :nodoc:
  10197. # needed for rails `rake routes`
  10198. path.requirements.merge(@defaults).delete_if { |_,v|
  10199. /.+?/ == v
  10200. }
  10201. end
  10202. def segments
  10203. path.names
  10204. end
  10205. def required_keys
  10206. required_parts + required_defaults.keys
  10207. end
  10208. def score(constraints)
  10209. required_keys = path.required_names
  10210. supplied_keys = constraints.map { |k,v| v && k.to_s }.compact
  10211. return -1 unless (required_keys - supplied_keys).empty?
  10212. score = (supplied_keys & path.names).length
  10213. score + (required_defaults.length * 2)
  10214. end
  10215. def parts
  10216. @parts ||= segments.map { |n| n.to_sym }
  10217. end
  10218. alias :segment_keys :parts
  10219. def format(path_options)
  10220. path_options.delete_if do |key, value|
  10221. value.to_s == defaults[key].to_s && !required_parts.include?(key)
  10222. end
  10223. Visitors::Formatter.new(path_options).accept(path.spec)
  10224. end
  10225. def optional_parts
  10226. path.optional_names.map { |n| n.to_sym }
  10227. end
  10228. def required_parts
  10229. @required_parts ||= path.required_names.map { |n| n.to_sym }
  10230. end
  10231. def required_default?(key)
  10232. (constraints[:required_defaults] || []).include?(key)
  10233. end
  10234. def required_defaults
  10235. @required_defaults ||= @defaults.dup.delete_if do |k,_|
  10236. parts.include?(k) || !required_default?(k)
  10237. end
  10238. end
  10239. def matches?(request)
  10240. constraints.all? do |method, value|
  10241. next true unless request.respond_to?(method)
  10242. case value
  10243. when Regexp, String
  10244. value === request.send(method).to_s
  10245. when Array
  10246. value.include?(request.send(method))
  10247. else
  10248. value === request.send(method)
  10249. end
  10250. end
  10251. end
  10252. def ip
  10253. constraints[:ip] || //
  10254. end
  10255. def verb
  10256. constraints[:request_method] || //
  10257. end
  10258. end
  10259. end
  10260. end
  10261. module ActionDispatch
  10262. module Journey # :nodoc:
  10263. class Router # :nodoc:
  10264. class Strexp # :nodoc:
  10265. class << self
  10266. alias :compile :new
  10267. end
  10268. attr_reader :path, :requirements, :separators, :anchor
  10269. def initialize(path, requirements, separators, anchor = true)
  10270. @path = path
  10271. @requirements = requirements
  10272. @separators = separators
  10273. @anchor = anchor
  10274. end
  10275. def names
  10276. @path.scan(/:\w+/).map { |s| s.tr(':', '') }
  10277. end
  10278. end
  10279. end
  10280. end
  10281. end
  10282. require 'uri'
  10283. module ActionDispatch
  10284. module Journey # :nodoc:
  10285. class Router # :nodoc:
  10286. class Utils # :nodoc:
  10287. # Normalizes URI path.
  10288. #
  10289. # Strips off trailing slash and ensures there is a leading slash.
  10290. #
  10291. # normalize_path("/foo") # => "/foo"
  10292. # normalize_path("/foo/") # => "/foo"
  10293. # normalize_path("foo") # => "/foo"
  10294. # normalize_path("") # => "/"
  10295. def self.normalize_path(path)
  10296. path = "/#{path}"
  10297. path.squeeze!('/')
  10298. path.sub!(%r{/+\Z}, '')
  10299. path = '/' if path == ''
  10300. path
  10301. end
  10302. # URI path and fragment escaping
  10303. # http://tools.ietf.org/html/rfc3986
  10304. module UriEscape # :nodoc:
  10305. # Symbol captures can generate multiple path segments, so include /.
  10306. reserved_segment = '/'
  10307. reserved_fragment = '/?'
  10308. reserved_pchar = ':@&=+$,;%'
  10309. safe_pchar = "#{URI::REGEXP::PATTERN::UNRESERVED}#{reserved_pchar}"
  10310. safe_segment = "#{safe_pchar}#{reserved_segment}"
  10311. safe_fragment = "#{safe_pchar}#{reserved_fragment}"
  10312. UNSAFE_SEGMENT = Regexp.new("[^#{safe_segment}]", false).freeze
  10313. UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false).freeze
  10314. end
  10315. Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
  10316. def self.escape_path(path)
  10317. Parser.escape(path.to_s, UriEscape::UNSAFE_SEGMENT)
  10318. end
  10319. def self.escape_fragment(fragment)
  10320. Parser.escape(fragment.to_s, UriEscape::UNSAFE_FRAGMENT)
  10321. end
  10322. def self.unescape_uri(uri)
  10323. Parser.unescape(uri)
  10324. end
  10325. end
  10326. end
  10327. end
  10328. end
  10329. require 'action_dispatch/journey/router/utils'
  10330. require 'action_dispatch/journey/router/strexp'
  10331. require 'action_dispatch/journey/routes'
  10332. require 'action_dispatch/journey/formatter'
  10333. before = $-w
  10334. $-w = false
  10335. require 'action_dispatch/journey/parser'
  10336. $-w = before
  10337. require 'action_dispatch/journey/route'
  10338. require 'action_dispatch/journey/path/pattern'
  10339. module ActionDispatch
  10340. module Journey # :nodoc:
  10341. class Router # :nodoc:
  10342. class RoutingError < ::StandardError # :nodoc:
  10343. end
  10344. # :nodoc:
  10345. VERSION = '2.0.0'
  10346. class NullReq # :nodoc:
  10347. attr_reader :env
  10348. def initialize(env)
  10349. @env = env
  10350. end
  10351. def request_method
  10352. env['REQUEST_METHOD']
  10353. end
  10354. def path_info
  10355. env['PATH_INFO']
  10356. end
  10357. def ip
  10358. env['REMOTE_ADDR']
  10359. end
  10360. def [](k); env[k]; end
  10361. end
  10362. attr_reader :request_class, :formatter
  10363. attr_accessor :routes
  10364. def initialize(routes, options)
  10365. @options = options
  10366. @params_key = options[:parameters_key]
  10367. @request_class = options[:request_class] || NullReq
  10368. @routes = routes
  10369. end
  10370. def call(env)
  10371. env['PATH_INFO'] = Utils.normalize_path(env['PATH_INFO'])
  10372. find_routes(env).each do |match, parameters, route|
  10373. script_name, path_info, set_params = env.values_at('SCRIPT_NAME',
  10374. 'PATH_INFO',
  10375. @params_key)
  10376. unless route.path.anchored
  10377. env['SCRIPT_NAME'] = (script_name.to_s + match.to_s).chomp('/')
  10378. env['PATH_INFO'] = match.post_match
  10379. end
  10380. env[@params_key] = (set_params || {}).merge parameters
  10381. status, headers, body = route.app.call(env)
  10382. if 'pass' == headers['X-Cascade']
  10383. env['SCRIPT_NAME'] = script_name
  10384. env['PATH_INFO'] = path_info
  10385. env[@params_key] = set_params
  10386. next
  10387. end
  10388. return [status, headers, body]
  10389. end
  10390. return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
  10391. end
  10392. def recognize(req)
  10393. find_routes(req.env).each do |match, parameters, route|
  10394. unless route.path.anchored
  10395. req.env['SCRIPT_NAME'] = match.to_s
  10396. req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
  10397. end
  10398. yield(route, nil, parameters)
  10399. end
  10400. end
  10401. def visualizer
  10402. tt = GTG::Builder.new(ast).transition_table
  10403. groups = partitioned_routes.first.map(&:ast).group_by { |a| a.to_s }
  10404. asts = groups.values.map { |v| v.first }
  10405. tt.visualizer(asts)
  10406. end
  10407. private
  10408. def partitioned_routes
  10409. routes.partitioned_routes
  10410. end
  10411. def ast
  10412. routes.ast
  10413. end
  10414. def simulator
  10415. routes.simulator
  10416. end
  10417. def custom_routes
  10418. partitioned_routes.last
  10419. end
  10420. def filter_routes(path)
  10421. return [] unless ast
  10422. data = simulator.match(path)
  10423. data ? data.memos : []
  10424. end
  10425. def find_routes env
  10426. req = request_class.new(env)
  10427. routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
  10428. r.path.match(req.path_info)
  10429. }
  10430. routes.concat get_routes_as_head(routes)
  10431. routes.sort_by!(&:precedence).select! { |r| r.matches?(req) }
  10432. routes.map! { |r|
  10433. match_data = r.path.match(req.path_info)
  10434. match_names = match_data.names.map { |n| n.to_sym }
  10435. match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) }
  10436. info = Hash[match_names.zip(match_values).find_all { |_, y| y }]
  10437. [match_data, r.defaults.merge(info), r]
  10438. }
  10439. end
  10440. def get_routes_as_head(routes)
  10441. precedence = (routes.map(&:precedence).max || 0) + 1
  10442. routes = routes.select { |r|
  10443. r.verb === "GET" && !(r.verb === "HEAD")
  10444. }.map! { |r|
  10445. Route.new(r.name,
  10446. r.app,
  10447. r.path,
  10448. r.conditions.merge(request_method: "HEAD"),
  10449. r.defaults).tap do |route|
  10450. route.precedence = r.precedence + precedence
  10451. end
  10452. }
  10453. routes.flatten!
  10454. routes
  10455. end
  10456. end
  10457. end
  10458. end
  10459. module ActionDispatch
  10460. module Journey # :nodoc:
  10461. # The Routing table. Contains all routes for a system. Routes can be
  10462. # added to the table by calling Routes#add_route.
  10463. class Routes # :nodoc:
  10464. include Enumerable
  10465. attr_reader :routes, :named_routes
  10466. def initialize
  10467. @routes = []
  10468. @named_routes = {}
  10469. @ast = nil
  10470. @partitioned_routes = nil
  10471. @simulator = nil
  10472. end
  10473. def length
  10474. routes.length
  10475. end
  10476. alias :size :length
  10477. def last
  10478. routes.last
  10479. end
  10480. def each(&block)
  10481. routes.each(&block)
  10482. end
  10483. def clear
  10484. routes.clear
  10485. end
  10486. def partitioned_routes
  10487. @partitioned_routes ||= routes.partition do |r|
  10488. r.path.anchored && r.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
  10489. end
  10490. end
  10491. def ast
  10492. @ast ||= begin
  10493. asts = partitioned_routes.first.map(&:ast)
  10494. Nodes::Or.new(asts) unless asts.empty?
  10495. end
  10496. end
  10497. def simulator
  10498. @simulator ||= begin
  10499. gtg = GTG::Builder.new(ast).transition_table
  10500. GTG::Simulator.new(gtg)
  10501. end
  10502. end
  10503. # Add a route to the routing table.
  10504. def add_route(app, path, conditions, defaults, name = nil)
  10505. route = Route.new(name, app, path, conditions, defaults)
  10506. route.precedence = routes.length
  10507. routes << route
  10508. named_routes[name] = route if name && !named_routes[name]
  10509. clear_cache!
  10510. route
  10511. end
  10512. private
  10513. def clear_cache!
  10514. @ast = nil
  10515. @partitioned_routes = nil
  10516. @simulator = nil
  10517. end
  10518. end
  10519. end
  10520. end
  10521. require 'strscan'
  10522. module ActionDispatch
  10523. module Journey # :nodoc:
  10524. class Scanner # :nodoc:
  10525. def initialize
  10526. @ss = nil
  10527. end
  10528. def scan_setup(str)
  10529. @ss = StringScanner.new(str)
  10530. end
  10531. def eos?
  10532. @ss.eos?
  10533. end
  10534. def pos
  10535. @ss.pos
  10536. end
  10537. def pre_match
  10538. @ss.pre_match
  10539. end
  10540. def next_token
  10541. return if @ss.eos?
  10542. until token = scan || @ss.eos?; end
  10543. token
  10544. end
  10545. private
  10546. def scan
  10547. case
  10548. # /
  10549. when text = @ss.scan(/\//)
  10550. [:SLASH, text]
  10551. when text = @ss.scan(/\*\w+/)
  10552. [:STAR, text]
  10553. when text = @ss.scan(/\(/)
  10554. [:LPAREN, text]
  10555. when text = @ss.scan(/\)/)
  10556. [:RPAREN, text]
  10557. when text = @ss.scan(/\|/)
  10558. [:OR, text]
  10559. when text = @ss.scan(/\./)
  10560. [:DOT, text]
  10561. when text = @ss.scan(/:\w+/)
  10562. [:SYMBOL, text]
  10563. when text = @ss.scan(/[\w%\-~]+/)
  10564. [:LITERAL, text]
  10565. # any char
  10566. when text = @ss.scan(/./)
  10567. [:LITERAL, text]
  10568. end
  10569. end
  10570. end
  10571. end
  10572. end
  10573. # encoding: utf-8
  10574. module ActionDispatch
  10575. module Journey # :nodoc:
  10576. module Visitors # :nodoc:
  10577. class Visitor # :nodoc:
  10578. DISPATCH_CACHE = Hash.new { |h,k|
  10579. h[k] = "visit_#{k}"
  10580. }
  10581. def accept(node)
  10582. visit(node)
  10583. end
  10584. private
  10585. def visit node
  10586. send(DISPATCH_CACHE[node.type], node)
  10587. end
  10588. def binary(node)
  10589. visit(node.left)
  10590. visit(node.right)
  10591. end
  10592. def visit_CAT(n); binary(n); end
  10593. def nary(node)
  10594. node.children.each { |c| visit(c) }
  10595. end
  10596. def visit_OR(n); nary(n); end
  10597. def unary(node)
  10598. visit(node.left)
  10599. end
  10600. def visit_GROUP(n); unary(n); end
  10601. def visit_STAR(n); unary(n); end
  10602. def terminal(node); end
  10603. %w{ LITERAL SYMBOL SLASH DOT }.each do |t|
  10604. class_eval %{ def visit_#{t}(n); terminal(n); end }, __FILE__, __LINE__
  10605. end
  10606. end
  10607. # Loop through the requirements AST
  10608. class Each < Visitor # :nodoc:
  10609. attr_reader :block
  10610. def initialize(block)
  10611. @block = block
  10612. end
  10613. def visit(node)
  10614. super
  10615. block.call(node)
  10616. end
  10617. end
  10618. class String < Visitor # :nodoc:
  10619. private
  10620. def binary(node)
  10621. [visit(node.left), visit(node.right)].join
  10622. end
  10623. def nary(node)
  10624. node.children.map { |c| visit(c) }.join '|'
  10625. end
  10626. def terminal(node)
  10627. node.left
  10628. end
  10629. def visit_GROUP(node)
  10630. "(#{visit(node.left)})"
  10631. end
  10632. end
  10633. # Used for formatting urls (url_for)
  10634. class Formatter < Visitor # :nodoc:
  10635. attr_reader :options, :consumed
  10636. def initialize(options)
  10637. @options = options
  10638. @consumed = {}
  10639. end
  10640. private
  10641. def visit_GROUP(node)
  10642. if consumed == options
  10643. nil
  10644. else
  10645. route = visit(node.left)
  10646. route.include?("\0") ? nil : route
  10647. end
  10648. end
  10649. def terminal(node)
  10650. node.left
  10651. end
  10652. def binary(node)
  10653. [visit(node.left), visit(node.right)].join
  10654. end
  10655. def nary(node)
  10656. node.children.map { |c| visit(c) }.join
  10657. end
  10658. def visit_SYMBOL(node)
  10659. key = node.to_sym
  10660. if value = options[key]
  10661. consumed[key] = value
  10662. Router::Utils.escape_path(value)
  10663. else
  10664. "\0"
  10665. end
  10666. end
  10667. end
  10668. class Dot < Visitor # :nodoc:
  10669. def initialize
  10670. @nodes = []
  10671. @edges = []
  10672. end
  10673. def accept(node)
  10674. super
  10675. <<-eodot
  10676. digraph parse_tree {
  10677. size="8,5"
  10678. node [shape = none];
  10679. edge [dir = none];
  10680. #{@nodes.join "\n"}
  10681. #{@edges.join("\n")}
  10682. }
  10683. eodot
  10684. end
  10685. private
  10686. def binary(node)
  10687. node.children.each do |c|
  10688. @edges << "#{node.object_id} -> #{c.object_id};"
  10689. end
  10690. super
  10691. end
  10692. def nary(node)
  10693. node.children.each do |c|
  10694. @edges << "#{node.object_id} -> #{c.object_id};"
  10695. end
  10696. super
  10697. end
  10698. def unary(node)
  10699. @edges << "#{node.object_id} -> #{node.left.object_id};"
  10700. super
  10701. end
  10702. def visit_GROUP(node)
  10703. @nodes << "#{node.object_id} [label=\"()\"];"
  10704. super
  10705. end
  10706. def visit_CAT(node)
  10707. @nodes << "#{node.object_id} [label=\"â—‹\"];"
  10708. super
  10709. end
  10710. def visit_STAR(node)
  10711. @nodes << "#{node.object_id} [label=\"*\"];"
  10712. super
  10713. end
  10714. def visit_OR(node)
  10715. @nodes << "#{node.object_id} [label=\"|\"];"
  10716. super
  10717. end
  10718. def terminal(node)
  10719. value = node.left
  10720. @nodes << "#{node.object_id} [label=\"#{value}\"];"
  10721. end
  10722. end
  10723. end
  10724. end
  10725. end
  10726. require 'action_dispatch/journey/router'
  10727. require 'action_dispatch/journey/gtg/builder'
  10728. require 'action_dispatch/journey/gtg/simulator'
  10729. require 'action_dispatch/journey/nfa/builder'
  10730. require 'action_dispatch/journey/nfa/simulator'
  10731. module ActionDispatch
  10732. # Provide callbacks to be executed before and after the request dispatch.
  10733. class Callbacks
  10734. include ActiveSupport::Callbacks
  10735. define_callbacks :call
  10736. class << self
  10737. delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
  10738. end
  10739. def self.before(*args, &block)
  10740. set_callback(:call, :before, *args, &block)
  10741. end
  10742. def self.after(*args, &block)
  10743. set_callback(:call, :after, *args, &block)
  10744. end
  10745. def initialize(app)
  10746. @app = app
  10747. end
  10748. def call(env)
  10749. error = nil
  10750. result = run_callbacks :call do
  10751. begin
  10752. @app.call(env)
  10753. rescue => error
  10754. end
  10755. end
  10756. raise error if error
  10757. result
  10758. end
  10759. end
  10760. end
  10761. require 'active_support/core_ext/hash/keys'
  10762. require 'active_support/core_ext/module/attribute_accessors'
  10763. require 'active_support/key_generator'
  10764. require 'active_support/message_verifier'
  10765. module ActionDispatch
  10766. class Request < Rack::Request
  10767. def cookie_jar
  10768. env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self)
  10769. end
  10770. end
  10771. # \Cookies are read and written through ActionController#cookies.
  10772. #
  10773. # The cookies being read are the ones received along with the request, the cookies
  10774. # being written will be sent out with the response. Reading a cookie does not get
  10775. # the cookie object itself back, just the value it holds.
  10776. #
  10777. # Examples of writing:
  10778. #
  10779. # # Sets a simple session cookie.
  10780. # # This cookie will be deleted when the user's browser is closed.
  10781. # cookies[:user_name] = "david"
  10782. #
  10783. # # Assign an array of values to a cookie.
  10784. # cookies[:lat_lon] = [47.68, -122.37]
  10785. #
  10786. # # Sets a cookie that expires in 1 hour.
  10787. # cookies[:login] = { value: "XJ-122", expires: 1.hour.from_now }
  10788. #
  10789. # # Sets a signed cookie, which prevents users from tampering with its value.
  10790. # # The cookie is signed by your app's <tt>config.secret_key_base</tt> value.
  10791. # # It can be read using the signed method <tt>cookies.signed[:key]</tt>
  10792. # cookies.signed[:user_id] = current_user.id
  10793. #
  10794. # # Sets a "permanent" cookie (which expires in 20 years from now).
  10795. # cookies.permanent[:login] = "XJ-122"
  10796. #
  10797. # # You can also chain these methods:
  10798. # cookies.permanent.signed[:login] = "XJ-122"
  10799. #
  10800. # Examples of reading:
  10801. #
  10802. # cookies[:user_name] # => "david"
  10803. # cookies.size # => 2
  10804. # cookies[:lat_lon] # => [47.68, -122.37]
  10805. # cookies.signed[:login] # => "XJ-122"
  10806. #
  10807. # Example for deleting:
  10808. #
  10809. # cookies.delete :user_name
  10810. #
  10811. # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
  10812. #
  10813. # cookies[:key] = {
  10814. # value: 'a yummy cookie',
  10815. # expires: 1.year.from_now,
  10816. # domain: 'domain.com'
  10817. # }
  10818. #
  10819. # cookies.delete(:key, domain: 'domain.com')
  10820. #
  10821. # The option symbols for setting cookies are:
  10822. #
  10823. # * <tt>:value</tt> - The cookie's value or list of values (as an array).
  10824. # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
  10825. # of the application.
  10826. # * <tt>:domain</tt> - The domain for which this cookie applies so you can
  10827. # restrict to the domain level. If you use a schema like www.example.com
  10828. # and want to share session with user.example.com set <tt>:domain</tt>
  10829. # to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
  10830. # <tt>:all</tt> again when deleting keys.
  10831. #
  10832. # domain: nil # Does not sets cookie domain. (default)
  10833. # domain: :all # Allow the cookie for the top most level
  10834. # domain and subdomains.
  10835. #
  10836. # * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
  10837. # * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
  10838. # Default is +false+.
  10839. # * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
  10840. # only HTTP. Defaults to +false+.
  10841. class Cookies
  10842. HTTP_HEADER = "Set-Cookie".freeze
  10843. GENERATOR_KEY = "action_dispatch.key_generator".freeze
  10844. SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt".freeze
  10845. ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt".freeze
  10846. ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze
  10847. TOKEN_KEY = "action_dispatch.secret_token".freeze
  10848. # Cookies can typically store 4096 bytes.
  10849. MAX_COOKIE_SIZE = 4096
  10850. # Raised when storing more than 4K of session data.
  10851. CookieOverflow = Class.new StandardError
  10852. class CookieJar #:nodoc:
  10853. include Enumerable
  10854. # This regular expression is used to split the levels of a domain.
  10855. # The top level domain can be any string without a period or
  10856. # **.**, ***.** style TLDs like co.uk or com.au
  10857. #
  10858. # www.example.co.uk gives:
  10859. # $& => example.co.uk
  10860. #
  10861. # example.com gives:
  10862. # $& => example.com
  10863. #
  10864. # lots.of.subdomains.example.local gives:
  10865. # $& => example.local
  10866. DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
  10867. def self.options_for_env(env) #:nodoc:
  10868. { signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '',
  10869. encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '',
  10870. encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '',
  10871. token_key: env[TOKEN_KEY] }
  10872. end
  10873. def self.build(request)
  10874. env = request.env
  10875. key_generator = env[GENERATOR_KEY]
  10876. options = options_for_env env
  10877. host = request.host
  10878. secure = request.ssl?
  10879. new(key_generator, host, secure, options).tap do |hash|
  10880. hash.update(request.cookies)
  10881. end
  10882. end
  10883. def initialize(key_generator, host = nil, secure = false, options = {})
  10884. @key_generator = key_generator
  10885. @set_cookies = {}
  10886. @delete_cookies = {}
  10887. @host = host
  10888. @secure = secure
  10889. @options = options
  10890. @cookies = {}
  10891. end
  10892. def each(&block)
  10893. @cookies.each(&block)
  10894. end
  10895. # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
  10896. def [](name)
  10897. @cookies[name.to_s]
  10898. end
  10899. def fetch(name, *args, &block)
  10900. @cookies.fetch(name.to_s, *args, &block)
  10901. end
  10902. def key?(name)
  10903. @cookies.key?(name.to_s)
  10904. end
  10905. alias :has_key? :key?
  10906. def update(other_hash)
  10907. @cookies.update other_hash.stringify_keys
  10908. self
  10909. end
  10910. def handle_options(options) #:nodoc:
  10911. options[:path] ||= "/"
  10912. if options[:domain] == :all
  10913. # if there is a provided tld length then we use it otherwise default domain regexp
  10914. domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
  10915. # if host is not ip and matches domain regexp
  10916. # (ip confirms to domain regexp so we explicitly check for ip)
  10917. options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ domain_regexp)
  10918. ".#{$&}"
  10919. end
  10920. elsif options[:domain].is_a? Array
  10921. # if host matches one of the supplied domains without a dot in front of it
  10922. options[:domain] = options[:domain].find {|domain| @host.include? domain.sub(/^\./, '') }
  10923. end
  10924. end
  10925. # Sets the cookie named +name+. The second argument may be the very cookie
  10926. # value, or a hash of options as documented above.
  10927. def []=(key, options)
  10928. if options.is_a?(Hash)
  10929. options.symbolize_keys!
  10930. value = options[:value]
  10931. else
  10932. value = options
  10933. options = { :value => value }
  10934. end
  10935. handle_options(options)
  10936. if @cookies[key.to_s] != value or options[:expires]
  10937. @cookies[key.to_s] = value
  10938. @set_cookies[key.to_s] = options
  10939. @delete_cookies.delete(key.to_s)
  10940. end
  10941. value
  10942. end
  10943. # Removes the cookie on the client machine by setting the value to an empty string
  10944. # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
  10945. # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
  10946. def delete(key, options = {})
  10947. return unless @cookies.has_key? key.to_s
  10948. options.symbolize_keys!
  10949. handle_options(options)
  10950. value = @cookies.delete(key.to_s)
  10951. @delete_cookies[key.to_s] = options
  10952. value
  10953. end
  10954. # Whether the given cookie is to be deleted by this CookieJar.
  10955. # Like <tt>[]=</tt>, you can pass in an options hash to test if a
  10956. # deletion applies to a specific <tt>:path</tt>, <tt>:domain</tt> etc.
  10957. def deleted?(key, options = {})
  10958. options.symbolize_keys!
  10959. handle_options(options)
  10960. @delete_cookies[key.to_s] == options
  10961. end
  10962. # Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie
  10963. def clear(options = {})
  10964. @cookies.each_key{ |k| delete(k, options) }
  10965. end
  10966. # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
  10967. #
  10968. # cookies.permanent[:prefers_open_id] = true
  10969. # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
  10970. #
  10971. # This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
  10972. #
  10973. # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
  10974. #
  10975. # cookies.permanent.signed[:remember_me] = current_user.id
  10976. # # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
  10977. def permanent
  10978. @permanent ||= PermanentCookieJar.new(self, @key_generator, @options)
  10979. end
  10980. # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
  10981. # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
  10982. # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
  10983. # be raised.
  10984. #
  10985. # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+.
  10986. #
  10987. # Example:
  10988. #
  10989. # cookies.signed[:discount] = 45
  10990. # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
  10991. #
  10992. # cookies.signed[:discount] # => 45
  10993. def signed
  10994. @signed ||= SignedCookieJar.new(self, @key_generator, @options)
  10995. end
  10996. # Only needed for supporting the +UpgradeSignatureToEncryptionCookieStore+, users and plugin authors should not use this
  10997. def signed_using_old_secret #:nodoc:
  10998. @signed_using_old_secret ||= SignedCookieJar.new(self, ActiveSupport::DummyKeyGenerator.new(@options[:token_key]), @options)
  10999. end
  11000. # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
  11001. # If the cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception
  11002. # will be raised.
  11003. #
  11004. # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+.
  11005. #
  11006. # Example:
  11007. #
  11008. # cookies.encrypted[:discount] = 45
  11009. # # => Set-Cookie: discount=ZS9ZZ1R4cG1pcUJ1bm80anhQang3dz09LS1mbDZDSU5scGdOT3ltQ2dTdlhSdWpRPT0%3D--ab54663c9f4e3bc340c790d6d2b71e92f5b60315; path=/
  11010. #
  11011. # cookies.encrypted[:discount] # => 45
  11012. def encrypted
  11013. @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options)
  11014. end
  11015. def write(headers)
  11016. @set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) }
  11017. @delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
  11018. end
  11019. def recycle! #:nodoc:
  11020. @set_cookies.clear
  11021. @delete_cookies.clear
  11022. end
  11023. mattr_accessor :always_write_cookie
  11024. self.always_write_cookie = false
  11025. private
  11026. def write_cookie?(cookie)
  11027. @secure || !cookie[:secure] || always_write_cookie
  11028. end
  11029. end
  11030. class PermanentCookieJar #:nodoc:
  11031. def initialize(parent_jar, key_generator, options = {})
  11032. @parent_jar = parent_jar
  11033. @key_generator = key_generator
  11034. @options = options
  11035. end
  11036. def [](key)
  11037. @parent_jar[name.to_s]
  11038. end
  11039. def []=(key, options)
  11040. if options.is_a?(Hash)
  11041. options.symbolize_keys!
  11042. else
  11043. options = { :value => options }
  11044. end
  11045. options[:expires] = 20.years.from_now
  11046. @parent_jar[key] = options
  11047. end
  11048. def permanent
  11049. @permanent ||= PermanentCookieJar.new(self, @key_generator, @options)
  11050. end
  11051. def signed
  11052. @signed ||= SignedCookieJar.new(self, @key_generator, @options)
  11053. end
  11054. def encrypted
  11055. @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options)
  11056. end
  11057. def method_missing(method, *arguments, &block)
  11058. ActiveSupport::Deprecation.warn "#{method} is deprecated with no replacement. " +
  11059. "You probably want to try this method over the parent CookieJar."
  11060. end
  11061. end
  11062. class SignedCookieJar #:nodoc:
  11063. def initialize(parent_jar, key_generator, options = {})
  11064. @parent_jar = parent_jar
  11065. @options = options
  11066. secret = key_generator.generate_key(@options[:signed_cookie_salt])
  11067. @verifier = ActiveSupport::MessageVerifier.new(secret)
  11068. end
  11069. def [](name)
  11070. if signed_message = @parent_jar[name]
  11071. @verifier.verify(signed_message)
  11072. end
  11073. rescue ActiveSupport::MessageVerifier::InvalidSignature
  11074. nil
  11075. end
  11076. def []=(key, options)
  11077. if options.is_a?(Hash)
  11078. options.symbolize_keys!
  11079. options[:value] = @verifier.generate(options[:value])
  11080. else
  11081. options = { :value => @verifier.generate(options) }
  11082. end
  11083. raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
  11084. @parent_jar[key] = options
  11085. end
  11086. def permanent
  11087. @permanent ||= PermanentCookieJar.new(self, @key_generator, @options)
  11088. end
  11089. def signed
  11090. @signed ||= SignedCookieJar.new(self, @key_generator, @options)
  11091. end
  11092. def encrypted
  11093. @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options)
  11094. end
  11095. def method_missing(method, *arguments, &block)
  11096. ActiveSupport::Deprecation.warn "#{method} is deprecated with no replacement. " +
  11097. "You probably want to try this method over the parent CookieJar."
  11098. end
  11099. end
  11100. class EncryptedCookieJar #:nodoc:
  11101. def initialize(parent_jar, key_generator, options = {})
  11102. if ActiveSupport::DummyKeyGenerator === key_generator
  11103. raise "Encrypted Cookies must be used in conjunction with config.secret_key_base." +
  11104. "Set config.secret_key_base in config/initializers/secret_token.rb"
  11105. end
  11106. @parent_jar = parent_jar
  11107. @options = options
  11108. secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
  11109. sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
  11110. @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
  11111. end
  11112. def [](key)
  11113. if encrypted_message = @parent_jar[key]
  11114. @encryptor.decrypt_and_verify(encrypted_message)
  11115. end
  11116. rescue ActiveSupport::MessageVerifier::InvalidSignature,
  11117. ActiveSupport::MessageVerifier::InvalidMessage
  11118. nil
  11119. end
  11120. def []=(key, options)
  11121. if options.is_a?(Hash)
  11122. options.symbolize_keys!
  11123. else
  11124. options = { :value => options }
  11125. end
  11126. options[:value] = @encryptor.encrypt_and_sign(options[:value])
  11127. raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
  11128. @parent_jar[key] = options
  11129. end
  11130. def permanent
  11131. @permanent ||= PermanentCookieJar.new(self, @key_generator, @options)
  11132. end
  11133. def signed
  11134. @signed ||= SignedCookieJar.new(self, @key_generator, @options)
  11135. end
  11136. def encrypted
  11137. @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options)
  11138. end
  11139. def method_missing(method, *arguments, &block)
  11140. ActiveSupport::Deprecation.warn "#{method} is deprecated with no replacement. " +
  11141. "You probably want to try this method over the parent CookieJar."
  11142. end
  11143. end
  11144. def initialize(app)
  11145. @app = app
  11146. end
  11147. def call(env)
  11148. status, headers, body = @app.call(env)
  11149. if cookie_jar = env['action_dispatch.cookies']
  11150. cookie_jar.write(headers)
  11151. if headers[HTTP_HEADER].respond_to?(:join)
  11152. headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
  11153. end
  11154. end
  11155. [status, headers, body]
  11156. end
  11157. end
  11158. end
  11159. require 'action_dispatch/http/request'
  11160. require 'action_dispatch/middleware/exception_wrapper'
  11161. require 'action_dispatch/routing/inspector'
  11162. module ActionDispatch
  11163. # This middleware is responsible for logging exceptions and
  11164. # showing a debugging page in case the request is local.
  11165. class DebugExceptions
  11166. RESCUES_TEMPLATE_PATH = File.expand_path('../templates', __FILE__)
  11167. def initialize(app, routes_app = nil)
  11168. @app = app
  11169. @routes_app = routes_app
  11170. end
  11171. def call(env)
  11172. _, headers, body = response = @app.call(env)
  11173. if headers['X-Cascade'] == 'pass'
  11174. body.close if body.respond_to?(:close)
  11175. raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
  11176. end
  11177. response
  11178. rescue Exception => exception
  11179. raise exception if env['action_dispatch.show_exceptions'] == false
  11180. render_exception(env, exception)
  11181. end
  11182. private
  11183. def render_exception(env, exception)
  11184. wrapper = ExceptionWrapper.new(env, exception)
  11185. log_error(env, wrapper)
  11186. if env['action_dispatch.show_detailed_exceptions']
  11187. template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
  11188. :request => Request.new(env),
  11189. :exception => wrapper.exception,
  11190. :application_trace => wrapper.application_trace,
  11191. :framework_trace => wrapper.framework_trace,
  11192. :full_trace => wrapper.full_trace,
  11193. :routes_inspector => routes_inspector(exception),
  11194. :source_extract => wrapper.source_extract,
  11195. :line_number => wrapper.line_number,
  11196. :file => wrapper.file
  11197. )
  11198. file = "rescues/#{wrapper.rescue_template}"
  11199. body = template.render(:template => file, :layout => 'rescues/layout')
  11200. render(wrapper.status_code, body)
  11201. else
  11202. raise exception
  11203. end
  11204. end
  11205. def render(status, body)
  11206. [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
  11207. end
  11208. def log_error(env, wrapper)
  11209. logger = logger(env)
  11210. return unless logger
  11211. exception = wrapper.exception
  11212. trace = wrapper.application_trace
  11213. trace = wrapper.framework_trace if trace.empty?
  11214. ActiveSupport::Deprecation.silence do
  11215. message = "\n#{exception.class} (#{exception.message}):\n"
  11216. message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
  11217. message << " " << trace.join("\n ")
  11218. logger.fatal("#{message}\n\n")
  11219. end
  11220. end
  11221. def logger(env)
  11222. env['action_dispatch.logger'] || stderr_logger
  11223. end
  11224. def stderr_logger
  11225. @stderr_logger ||= ActiveSupport::Logger.new($stderr)
  11226. end
  11227. def routes_inspector(exception)
  11228. if @routes_app.respond_to?(:routes) && (exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error))
  11229. ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
  11230. end
  11231. end
  11232. end
  11233. end
  11234. require 'action_controller/metal/exceptions'
  11235. require 'active_support/core_ext/class/attribute_accessors'
  11236. module ActionDispatch
  11237. class ExceptionWrapper
  11238. cattr_accessor :rescue_responses
  11239. @@rescue_responses = Hash.new(:internal_server_error)
  11240. @@rescue_responses.merge!(
  11241. 'ActionController::RoutingError' => :not_found,
  11242. 'AbstractController::ActionNotFound' => :not_found,
  11243. 'ActionController::MethodNotAllowed' => :method_not_allowed,
  11244. 'ActionController::NotImplemented' => :not_implemented,
  11245. 'ActionController::UnknownFormat' => :not_acceptable,
  11246. 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
  11247. 'ActionController::BadRequest' => :bad_request,
  11248. 'ActionController::ParameterMissing' => :bad_request
  11249. )
  11250. cattr_accessor :rescue_templates
  11251. @@rescue_templates = Hash.new('diagnostics')
  11252. @@rescue_templates.merge!(
  11253. 'ActionView::MissingTemplate' => 'missing_template',
  11254. 'ActionController::RoutingError' => 'routing_error',
  11255. 'AbstractController::ActionNotFound' => 'unknown_action',
  11256. 'ActionView::Template::Error' => 'template_error'
  11257. )
  11258. attr_reader :env, :exception, :line_number, :file
  11259. def initialize(env, exception)
  11260. @env = env
  11261. @exception = original_exception(exception)
  11262. end
  11263. def rescue_template
  11264. @@rescue_templates[@exception.class.name]
  11265. end
  11266. def status_code
  11267. self.class.status_code_for_exception(@exception.class.name)
  11268. end
  11269. def application_trace
  11270. clean_backtrace(:silent)
  11271. end
  11272. def framework_trace
  11273. clean_backtrace(:noise)
  11274. end
  11275. def full_trace
  11276. clean_backtrace(:all)
  11277. end
  11278. def self.status_code_for_exception(class_name)
  11279. Rack::Utils.status_code(@@rescue_responses[class_name])
  11280. end
  11281. def source_extract
  11282. if application_trace && trace = application_trace.first
  11283. file, line, _ = trace.split(":")
  11284. @file = file
  11285. @line_number = line.to_i
  11286. source_fragment(@file, @line_number)
  11287. end
  11288. end
  11289. private
  11290. def original_exception(exception)
  11291. if registered_original_exception?(exception)
  11292. exception.original_exception
  11293. else
  11294. exception
  11295. end
  11296. end
  11297. def registered_original_exception?(exception)
  11298. exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
  11299. end
  11300. def clean_backtrace(*args)
  11301. if backtrace_cleaner
  11302. backtrace_cleaner.clean(@exception.backtrace, *args)
  11303. else
  11304. @exception.backtrace
  11305. end
  11306. end
  11307. def backtrace_cleaner
  11308. @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
  11309. end
  11310. def source_fragment(path, line)
  11311. return unless Rails.respond_to?(:root) && Rails.root
  11312. full_path = Rails.root.join(path)
  11313. if File.exists?(full_path)
  11314. File.open(full_path, "r") do |file|
  11315. start = [line - 3, 0].max
  11316. lines = file.each_line.drop(start).take(6)
  11317. Hash[*(start+1..(lines.count+start)).zip(lines).flatten]
  11318. end
  11319. end
  11320. end
  11321. end
  11322. end
  11323. module ActionDispatch
  11324. class Request < Rack::Request
  11325. # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
  11326. # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
  11327. # to put a new one.
  11328. def flash
  11329. @env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
  11330. end
  11331. end
  11332. # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
  11333. # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
  11334. # action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
  11335. # then expose the flash to its template. Actually, that exposure is automatically done.
  11336. #
  11337. # class PostsController < ActionController::Base
  11338. # def create
  11339. # # save post
  11340. # flash[:notice] = "Post successfully created"
  11341. # redirect_to @post
  11342. # end
  11343. #
  11344. # def show
  11345. # # doesn't need to assign the flash notice to the template, that's done automatically
  11346. # end
  11347. # end
  11348. #
  11349. # show.html.erb
  11350. # <% if flash[:notice] %>
  11351. # <div class="notice"><%= flash[:notice] %></div>
  11352. # <% end %>
  11353. #
  11354. # Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
  11355. #
  11356. # flash.alert = "You must be logged in"
  11357. # flash.notice = "Post successfully created"
  11358. #
  11359. # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
  11360. # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
  11361. #
  11362. # See docs on the FlashHash class for more details about the flash.
  11363. class Flash
  11364. KEY = 'action_dispatch.request.flash_hash'.freeze
  11365. class FlashNow #:nodoc:
  11366. attr_accessor :flash
  11367. def initialize(flash)
  11368. @flash = flash
  11369. end
  11370. def []=(k, v)
  11371. @flash[k] = v
  11372. @flash.discard(k)
  11373. v
  11374. end
  11375. def [](k)
  11376. @flash[k]
  11377. end
  11378. # Convenience accessor for <tt>flash.now[:alert]=</tt>.
  11379. def alert=(message)
  11380. self[:alert] = message
  11381. end
  11382. # Convenience accessor for <tt>flash.now[:notice]=</tt>.
  11383. def notice=(message)
  11384. self[:notice] = message
  11385. end
  11386. end
  11387. class FlashHash
  11388. include Enumerable
  11389. def self.from_session_value(value)
  11390. flash = case value
  11391. when FlashHash # Rails 3.1, 3.2
  11392. new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
  11393. when Hash # Rails 4.0
  11394. new(value['flashes'], value['discard'])
  11395. else
  11396. new
  11397. end
  11398. flash.tap(&:sweep)
  11399. end
  11400. def to_session_value
  11401. return nil if empty?
  11402. {'discard' => @discard.to_a, 'flashes' => @flashes}
  11403. end
  11404. def initialize(flashes = {}, discard = []) #:nodoc:
  11405. @discard = Set.new(discard)
  11406. @flashes = flashes
  11407. @now = nil
  11408. end
  11409. def initialize_copy(other)
  11410. if other.now_is_loaded?
  11411. @now = other.now.dup
  11412. @now.flash = self
  11413. end
  11414. super
  11415. end
  11416. def []=(k, v)
  11417. @discard.delete k
  11418. @flashes[k] = v
  11419. end
  11420. def [](k)
  11421. @flashes[k]
  11422. end
  11423. def update(h) #:nodoc:
  11424. @discard.subtract h.keys
  11425. @flashes.update h
  11426. self
  11427. end
  11428. def keys
  11429. @flashes.keys
  11430. end
  11431. def key?(name)
  11432. @flashes.key? name
  11433. end
  11434. def delete(key)
  11435. @discard.delete key
  11436. @flashes.delete key
  11437. self
  11438. end
  11439. def to_hash
  11440. @flashes.dup
  11441. end
  11442. def empty?
  11443. @flashes.empty?
  11444. end
  11445. def clear
  11446. @discard.clear
  11447. @flashes.clear
  11448. end
  11449. def each(&block)
  11450. @flashes.each(&block)
  11451. end
  11452. alias :merge! :update
  11453. def replace(h) #:nodoc:
  11454. @discard.clear
  11455. @flashes.replace h
  11456. self
  11457. end
  11458. # Sets a flash that will not be available to the next action, only to the current.
  11459. #
  11460. # flash.now[:message] = "Hello current action"
  11461. #
  11462. # This method enables you to use the flash as a central messaging system in your app.
  11463. # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
  11464. # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
  11465. # vanish when the current action is done.
  11466. #
  11467. # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
  11468. #
  11469. # Also, brings two convenience accessors:
  11470. #
  11471. # flash.now.alert = "Beware now!"
  11472. # # Equivalent to flash.now[:alert] = "Beware now!"
  11473. #
  11474. # flash.now.notice = "Good luck now!"
  11475. # # Equivalent to flash.now[:notice] = "Good luck now!"
  11476. def now
  11477. @now ||= FlashNow.new(self)
  11478. end
  11479. # Keeps either the entire current flash or a specific flash entry available for the next action:
  11480. #
  11481. # flash.keep # keeps the entire flash
  11482. # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
  11483. def keep(k = nil)
  11484. @discard.subtract Array(k || keys)
  11485. k ? self[k] : self
  11486. end
  11487. # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
  11488. #
  11489. # flash.discard # discard the entire flash at the end of the current action
  11490. # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
  11491. def discard(k = nil)
  11492. @discard.merge Array(k || keys)
  11493. k ? self[k] : self
  11494. end
  11495. # Mark for removal entries that were kept, and delete unkept ones.
  11496. #
  11497. # This method is called automatically by filters, so you generally don't need to care about it.
  11498. def sweep #:nodoc:
  11499. @discard.each { |k| @flashes.delete k }
  11500. @discard.replace @flashes.keys
  11501. end
  11502. # Convenience accessor for <tt>flash[:alert]</tt>.
  11503. def alert
  11504. self[:alert]
  11505. end
  11506. # Convenience accessor for <tt>flash[:alert]=</tt>.
  11507. def alert=(message)
  11508. self[:alert] = message
  11509. end
  11510. # Convenience accessor for <tt>flash[:notice]</tt>.
  11511. def notice
  11512. self[:notice]
  11513. end
  11514. # Convenience accessor for <tt>flash[:notice]=</tt>.
  11515. def notice=(message)
  11516. self[:notice] = message
  11517. end
  11518. protected
  11519. def now_is_loaded?
  11520. @now
  11521. end
  11522. end
  11523. def initialize(app)
  11524. @app = app
  11525. end
  11526. def call(env)
  11527. @app.call(env)
  11528. ensure
  11529. session = Request::Session.find(env) || {}
  11530. flash_hash = env[KEY]
  11531. if flash_hash
  11532. if !flash_hash.empty? || session.key?('flash')
  11533. session["flash"] = flash_hash.to_session_value
  11534. new_hash = flash_hash.dup
  11535. else
  11536. new_hash = flash_hash
  11537. end
  11538. env[KEY] = new_hash
  11539. end
  11540. if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
  11541. session.key?('flash') && session['flash'].nil?
  11542. session.delete('flash')
  11543. end
  11544. end
  11545. end
  11546. end
  11547. require 'active_support/core_ext/hash/conversions'
  11548. require 'action_dispatch/http/request'
  11549. require 'active_support/core_ext/hash/indifferent_access'
  11550. module ActionDispatch
  11551. class ParamsParser
  11552. class ParseError < StandardError
  11553. attr_reader :original_exception
  11554. def initialize(message, original_exception)
  11555. super(message)
  11556. @original_exception = original_exception
  11557. end
  11558. end
  11559. DEFAULT_PARSERS = {
  11560. Mime::XML => :xml_simple,
  11561. Mime::JSON => :json
  11562. }
  11563. def initialize(app, parsers = {})
  11564. @app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
  11565. end
  11566. def call(env)
  11567. if params = parse_formatted_parameters(env)
  11568. env["action_dispatch.request.request_parameters"] = params
  11569. end
  11570. @app.call(env)
  11571. end
  11572. private
  11573. def parse_formatted_parameters(env)
  11574. request = Request.new(env)
  11575. return false if request.content_length.zero?
  11576. mime_type = content_type_from_legacy_post_data_format_header(env) ||
  11577. request.content_mime_type
  11578. strategy = @parsers[mime_type]
  11579. return false unless strategy
  11580. case strategy
  11581. when Proc
  11582. strategy.call(request.raw_post)
  11583. when :xml_simple, :xml_node
  11584. data = request.deep_munge(Hash.from_xml(request.body.read) || {})
  11585. data.with_indifferent_access
  11586. when :json
  11587. data = ActiveSupport::JSON.decode(request.body)
  11588. data = {:_json => data} unless data.is_a?(Hash)
  11589. request.deep_munge(data).with_indifferent_access
  11590. else
  11591. false
  11592. end
  11593. rescue Exception => e # YAML, XML or Ruby code block errors
  11594. logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
  11595. raise ParseError.new(e.message, e)
  11596. end
  11597. def content_type_from_legacy_post_data_format_header(env)
  11598. if x_post_format = env['HTTP_X_POST_DATA_FORMAT']
  11599. case x_post_format.to_s.downcase
  11600. when 'yaml' then return Mime::YAML
  11601. when 'xml' then return Mime::XML
  11602. end
  11603. end
  11604. nil
  11605. end
  11606. def logger(env)
  11607. env['action_dispatch.logger'] || ActiveSupport::Logger.new($stderr)
  11608. end
  11609. end
  11610. end
  11611. module ActionDispatch
  11612. class PublicExceptions
  11613. attr_accessor :public_path
  11614. def initialize(public_path)
  11615. @public_path = public_path
  11616. end
  11617. def call(env)
  11618. exception = env["action_dispatch.exception"]
  11619. status = env["PATH_INFO"][1..-1]
  11620. request = ActionDispatch::Request.new(env)
  11621. content_type = request.formats.first
  11622. body = { :status => status, :error => exception.message }
  11623. render(status, content_type, body)
  11624. end
  11625. private
  11626. def render(status, content_type, body)
  11627. format = content_type && "to_#{content_type.to_sym}"
  11628. if format && body.respond_to?(format)
  11629. render_format(status, content_type, body.public_send(format))
  11630. else
  11631. render_html(status)
  11632. end
  11633. end
  11634. def render_format(status, content_type, body)
  11635. [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
  11636. 'Content-Length' => body.bytesize.to_s}, [body]]
  11637. end
  11638. def render_html(status)
  11639. found = false
  11640. path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
  11641. path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
  11642. if found || File.exist?(path)
  11643. render_format(status, 'text/html', File.read(path))
  11644. else
  11645. [404, { "X-Cascade" => "pass" }, []]
  11646. end
  11647. end
  11648. end
  11649. end
  11650. module ActionDispatch
  11651. # ActionDispatch::Reloader provides prepare and cleanup callbacks,
  11652. # intended to assist with code reloading during development.
  11653. #
  11654. # Prepare callbacks are run before each request, and cleanup callbacks
  11655. # after each request. In this respect they are analogs of ActionDispatch::Callback's
  11656. # before and after callbacks. However, cleanup callbacks are not called until the
  11657. # request is fully complete -- that is, after #close has been called on
  11658. # the response body. This is important for streaming responses such as the
  11659. # following:
  11660. #
  11661. # self.response_body = lambda { |response, output|
  11662. # # code here which refers to application models
  11663. # }
  11664. #
  11665. # Cleanup callbacks will not be called until after the response_body lambda
  11666. # is evaluated, ensuring that it can refer to application models and other
  11667. # classes before they are unloaded.
  11668. #
  11669. # By default, ActionDispatch::Reloader is included in the middleware stack
  11670. # only in the development environment; specifically, when +config.cache_classes+
  11671. # is false. Callbacks may be registered even when it is not included in the
  11672. # middleware stack, but are executed only when <tt>ActionDispatch::Reloader.prepare!</tt>
  11673. # or <tt>ActionDispatch::Reloader.cleanup!</tt> are called manually.
  11674. #
  11675. class Reloader
  11676. include ActiveSupport::Callbacks
  11677. define_callbacks :prepare, :scope => :name
  11678. define_callbacks :cleanup, :scope => :name
  11679. # Add a prepare callback. Prepare callbacks are run before each request, prior
  11680. # to ActionDispatch::Callback's before callbacks.
  11681. def self.to_prepare(*args, &block)
  11682. set_callback(:prepare, *args, &block)
  11683. end
  11684. # Add a cleanup callback. Cleanup callbacks are run after each request is
  11685. # complete (after #close is called on the response body).
  11686. def self.to_cleanup(*args, &block)
  11687. set_callback(:cleanup, *args, &block)
  11688. end
  11689. # Execute all prepare callbacks.
  11690. def self.prepare!
  11691. new(nil).prepare!
  11692. end
  11693. # Execute all cleanup callbacks.
  11694. def self.cleanup!
  11695. new(nil).cleanup!
  11696. end
  11697. def initialize(app, condition=nil)
  11698. @app = app
  11699. @condition = condition || lambda { true }
  11700. @validated = true
  11701. end
  11702. def call(env)
  11703. @validated = @condition.call
  11704. prepare!
  11705. response = @app.call(env)
  11706. response[2] = ::Rack::BodyProxy.new(response[2]) { cleanup! }
  11707. response
  11708. rescue Exception
  11709. cleanup!
  11710. raise
  11711. end
  11712. def prepare! #:nodoc:
  11713. run_callbacks :prepare if validated?
  11714. end
  11715. def cleanup! #:nodoc:
  11716. run_callbacks :cleanup if validated?
  11717. ensure
  11718. @validated = true
  11719. end
  11720. private
  11721. def validated? #:nodoc:
  11722. @validated
  11723. end
  11724. end
  11725. end
  11726. module ActionDispatch
  11727. # This middleware calculates the IP address of the remote client that is
  11728. # making the request. It does this by checking various headers that could
  11729. # contain the address, and then picking the last-set address that is not
  11730. # on the list of trusted IPs. This follows the precedent set by e.g.
  11731. # {the Tomcat server}[https://issues.apache.org/bugzilla/show_bug.cgi?id=50453],
  11732. # with {reasoning explained at length}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection]
  11733. # by @gingerlime. A more detailed explanation of the algorithm is given
  11734. # at GetIp#calculate_ip.
  11735. #
  11736. # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
  11737. # requires. Some Rack servers simply drop preceeding headers, and only report
  11738. # the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
  11739. # If you are behind multiple proxy servers (like Nginx to HAProxy to Unicorn)
  11740. # then you should test your Rack server to make sure your data is good.
  11741. #
  11742. # IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
  11743. # This middleware assumes that there is at least one proxy sitting around
  11744. # and setting headers with the client's remote IP address. If you don't use
  11745. # a proxy, because you are hosted on e.g. Heroku without SSL, any client can
  11746. # claim to have any IP address by setting the X-Forwarded-For header. If you
  11747. # care about that, then you need to explicitly drop or ignore those headers
  11748. # sometime before this middleware runs.
  11749. class RemoteIp
  11750. class IpSpoofAttackError < StandardError; end
  11751. # The default trusted IPs list simply includes IP addresses that are
  11752. # guaranteed by the IP specification to be private addresses. Those will
  11753. # not be the ultimate client IP in production, and so are discarded. See
  11754. # http://en.wikipedia.org/wiki/Private_network for details.
  11755. TRUSTED_PROXIES = %r{
  11756. ^127\.0\.0\.1$ | # localhost IPv4
  11757. ^::1$ | # localhost IPv6
  11758. ^fc00: | # private IPv6 range fc00
  11759. ^10\. | # private IPv4 range 10.x.x.x
  11760. ^172\.(1[6-9]|2[0-9]|3[0-1])\.| # private IPv4 range 172.16.0.0 .. 172.31.255.255
  11761. ^192\.168\. # private IPv4 range 192.168.x.x
  11762. }x
  11763. attr_reader :check_ip, :proxies
  11764. # Create a new +RemoteIp+ middleware instance.
  11765. #
  11766. # The +check_ip_spoofing+ option is on by default. When on, an exception
  11767. # is raised if it looks like the client is trying to lie about its own IP
  11768. # address. It makes sense to turn off this check on sites aimed at non-IP
  11769. # clients (like WAP devices), or behind proxies that set headers in an
  11770. # incorrect or confusing way (like AWS ELB).
  11771. #
  11772. # The +custom_trusted+ argument can take a regex, which will be used
  11773. # instead of +TRUSTED_PROXIES+, or a string, which will be used in addition
  11774. # to +TRUSTED_PROXIES+. Any proxy setup will put the value you want in the
  11775. # middle (or at the beginning) of the X-Forwarded-For list, with your proxy
  11776. # servers after it. If your proxies aren't removed, pass them in via the
  11777. # +custom_trusted+ parameter. That way, the middleware will ignore those
  11778. # IP addresses, and return the one that you want.
  11779. def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
  11780. @app = app
  11781. @check_ip = check_ip_spoofing
  11782. @proxies = case custom_proxies
  11783. when Regexp
  11784. custom_proxies
  11785. when nil
  11786. TRUSTED_PROXIES
  11787. else
  11788. Regexp.union(TRUSTED_PROXIES, custom_proxies)
  11789. end
  11790. end
  11791. # Since the IP address may not be needed, we store the object here
  11792. # without calculating the IP to keep from slowing down the majority of
  11793. # requests. For those requests that do need to know the IP, the
  11794. # GetIp#calculate_ip method will calculate the memoized client IP address.
  11795. def call(env)
  11796. env["action_dispatch.remote_ip"] = GetIp.new(env, self)
  11797. @app.call(env)
  11798. end
  11799. # The GetIp class exists as a way to defer processing of the request data
  11800. # into an actual IP address. If the ActionDispatch::Request#remote_ip method
  11801. # is called, this class will calculate the value and then memoize it.
  11802. class GetIp
  11803. # This constant contains a regular expression that validates every known
  11804. # form of IP v4 and v6 address, with or without abbreviations, adapted
  11805. # from {this gist}[https://gist.github.com/1289635].
  11806. VALID_IP = %r{
  11807. (^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
  11808. (^(
  11809. (([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}) | # ip v6 not abbreviated
  11810. (([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4}) | # ip v6 with double colon in the end
  11811. (([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4}) | # - ip addresses v6
  11812. (([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4}) | # - with
  11813. (([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4}) | # - double colon
  11814. (([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4}) | # - in the middle
  11815. (([0-9A-Fa-f]{1,4}:){6} ((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3} (\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  11816. (([0-9A-Fa-f]{1,4}:){1,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  11817. (([0-9A-Fa-f]{1,4}:){1}:([0-9A-Fa-f]{1,4}:){0,4}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  11818. (([0-9A-Fa-f]{1,4}:){0,2}:([0-9A-Fa-f]{1,4}:){0,3}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  11819. (([0-9A-Fa-f]{1,4}:){0,3}:([0-9A-Fa-f]{1,4}:){0,2}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  11820. (([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  11821. (::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
  11822. ([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4
  11823. (::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the begining
  11824. (([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending
  11825. )$)
  11826. }x
  11827. def initialize(env, middleware)
  11828. @env = env
  11829. @check_ip = middleware.check_ip
  11830. @proxies = middleware.proxies
  11831. end
  11832. # Sort through the various IP address headers, looking for the IP most
  11833. # likely to be the address of the actual remote client making this
  11834. # request.
  11835. #
  11836. # REMOTE_ADDR will be correct if the request is made directly against the
  11837. # Ruby process, on e.g. Heroku. When the request is proxied by another
  11838. # server like HAProxy or Nginx, the IP address that made the original
  11839. # request will be put in an X-Forwarded-For header. If there are multiple
  11840. # proxies, that header may contain a list of IPs. Other proxy services
  11841. # set the Client-Ip header instead, so we check that too.
  11842. #
  11843. # As discussed in {this post about Rails IP Spoofing}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/],
  11844. # while the first IP in the list is likely to be the "originating" IP,
  11845. # it could also have been set by the client maliciously.
  11846. #
  11847. # In order to find the first address that is (probably) accurate, we
  11848. # take the list of IPs, remove known and trusted proxies, and then take
  11849. # the last address left, which was presumably set by one of those proxies.
  11850. def calculate_ip
  11851. # Set by the Rack web server, this is a single value.
  11852. remote_addr = ips_from('REMOTE_ADDR').last
  11853. # Could be a CSV list and/or repeated headers that were concatenated.
  11854. client_ips = ips_from('HTTP_CLIENT_IP').reverse
  11855. forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR').reverse
  11856. # +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
  11857. # If they are both set, it means that this request passed through two
  11858. # proxies with incompatible IP header conventions, and there is no way
  11859. # for us to determine which header is the right one after the fact.
  11860. # Since we have no idea, we give up and explode.
  11861. should_check_ip = @check_ip && client_ips.last
  11862. if should_check_ip && !forwarded_ips.include?(client_ips.last)
  11863. # We don't know which came from the proxy, and which from the user
  11864. raise IpSpoofAttackError, "IP spoofing attack?! " +
  11865. "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} " +
  11866. "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
  11867. end
  11868. # We assume these things about the IP headers:
  11869. #
  11870. # - X-Forwarded-For will be a list of IPs, one per proxy, or blank
  11871. # - Client-Ip is propagated from the outermost proxy, or is blank
  11872. # - REMOTE_ADDR will be the IP that made the request to Rack
  11873. ips = [forwarded_ips, client_ips, remote_addr].flatten.compact
  11874. # If every single IP option is in the trusted list, just return REMOTE_ADDR
  11875. filter_proxies(ips).first || remote_addr
  11876. end
  11877. # Memoizes the value returned by #calculate_ip and returns it for
  11878. # ActionDispatch::Request to use.
  11879. def to_s
  11880. @ip ||= calculate_ip
  11881. end
  11882. protected
  11883. def ips_from(header)
  11884. # Split the comma-separated list into an array of strings
  11885. ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
  11886. # Only return IPs that are valid according to the regex
  11887. ips.select{ |ip| ip =~ VALID_IP }
  11888. end
  11889. def filter_proxies(ips)
  11890. ips.reject { |ip| ip =~ @proxies }
  11891. end
  11892. end
  11893. end
  11894. end
  11895. require 'securerandom'
  11896. require 'active_support/core_ext/string/access'
  11897. module ActionDispatch
  11898. # Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
  11899. # ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
  11900. #
  11901. # The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
  11902. # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
  11903. # header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
  11904. #
  11905. # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
  11906. # from multiple pieces of the stack.
  11907. class RequestId
  11908. def initialize(app)
  11909. @app = app
  11910. end
  11911. def call(env)
  11912. env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
  11913. @app.call(env).tap { |status, headers, body| headers["X-Request-Id"] = env["action_dispatch.request_id"] }
  11914. end
  11915. private
  11916. def external_request_id(env)
  11917. if request_id = env["HTTP_X_REQUEST_ID"].presence
  11918. request_id.gsub(/[^\w\-]/, "").first(255)
  11919. end
  11920. end
  11921. def internal_request_id
  11922. SecureRandom.uuid
  11923. end
  11924. end
  11925. end
  11926. require 'rack/utils'
  11927. require 'rack/request'
  11928. require 'rack/session/abstract/id'
  11929. require 'action_dispatch/middleware/cookies'
  11930. require 'action_dispatch/request/session'
  11931. module ActionDispatch
  11932. module Session
  11933. class SessionRestoreError < StandardError #:nodoc:
  11934. attr_reader :original_exception
  11935. def initialize(const_error)
  11936. @original_exception = const_error
  11937. super("Session contains objects whose class definition isn't available.\n" +
  11938. "Remember to require the classes for all objects kept in the session.\n" +
  11939. "(Original exception: #{const_error.message} [#{const_error.class}])\n")
  11940. end
  11941. end
  11942. module Compatibility
  11943. def initialize(app, options = {})
  11944. options[:key] ||= '_session_id'
  11945. super
  11946. end
  11947. def generate_sid
  11948. sid = SecureRandom.hex(16)
  11949. sid.encode!(Encoding::UTF_8)
  11950. sid
  11951. end
  11952. protected
  11953. def initialize_sid
  11954. @default_options.delete(:sidbits)
  11955. @default_options.delete(:secure_random)
  11956. end
  11957. end
  11958. module StaleSessionCheck
  11959. def load_session(env)
  11960. stale_session_check! { super }
  11961. end
  11962. def extract_session_id(env)
  11963. stale_session_check! { super }
  11964. end
  11965. def stale_session_check!
  11966. yield
  11967. rescue ArgumentError => argument_error
  11968. if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
  11969. begin
  11970. # Note that the regexp does not allow $1 to end with a ':'
  11971. $1.constantize
  11972. rescue LoadError, NameError => e
  11973. raise ActionDispatch::Session::SessionRestoreError, e, e.backtrace
  11974. end
  11975. retry
  11976. else
  11977. raise
  11978. end
  11979. end
  11980. end
  11981. module SessionObject # :nodoc:
  11982. def prepare_session(env)
  11983. Request::Session.create(self, env, @default_options)
  11984. end
  11985. def loaded_session?(session)
  11986. !session.is_a?(Request::Session) || session.loaded?
  11987. end
  11988. end
  11989. class AbstractStore < Rack::Session::Abstract::ID
  11990. include Compatibility
  11991. include StaleSessionCheck
  11992. include SessionObject
  11993. private
  11994. def set_cookie(env, session_id, cookie)
  11995. request = ActionDispatch::Request.new(env)
  11996. request.cookie_jar[key] = cookie
  11997. end
  11998. end
  11999. end
  12000. end
  12001. require 'action_dispatch/middleware/session/abstract_store'
  12002. module ActionDispatch
  12003. module Session
  12004. # Session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful
  12005. # if you don't store critical data in your sessions and you don't need them to live for extended periods
  12006. # of time.
  12007. class CacheStore < AbstractStore
  12008. # Create a new store. The cache to use can be passed in the <tt>:cache</tt> option. If it is
  12009. # not specified, <tt>Rails.cache</tt> will be used.
  12010. def initialize(app, options = {})
  12011. @cache = options[:cache] || Rails.cache
  12012. options[:expire_after] ||= @cache.options[:expires_in]
  12013. super
  12014. end
  12015. # Get a session from the cache.
  12016. def get_session(env, sid)
  12017. sid ||= generate_sid
  12018. session = @cache.read(cache_key(sid))
  12019. session ||= {}
  12020. [sid, session]
  12021. end
  12022. # Set a session in the cache.
  12023. def set_session(env, sid, session, options)
  12024. key = cache_key(sid)
  12025. if session
  12026. @cache.write(key, session, :expires_in => options[:expire_after])
  12027. else
  12028. @cache.delete(key)
  12029. end
  12030. sid
  12031. end
  12032. # Remove a session from the cache.
  12033. def destroy_session(env, sid, options)
  12034. @cache.delete(cache_key(sid))
  12035. generate_sid
  12036. end
  12037. private
  12038. # Turn the session id into a cache key.
  12039. def cache_key(sid)
  12040. "_session_id:#{sid}"
  12041. end
  12042. end
  12043. end
  12044. end
  12045. require 'active_support/core_ext/hash/keys'
  12046. require 'action_dispatch/middleware/session/abstract_store'
  12047. require 'rack/session/cookie'
  12048. module ActionDispatch
  12049. module Session
  12050. # This cookie-based session store is the Rails default. Sessions typically
  12051. # contain at most a user_id and flash message; both fit within the 4K cookie
  12052. # size limit. Cookie-based sessions are dramatically faster than the
  12053. # alternatives.
  12054. #
  12055. # If you have more than 4K of session data or don't want your data to be
  12056. # visible to the user, pick another session store.
  12057. #
  12058. # CookieOverflow is raised if you attempt to store more than 4K of data.
  12059. #
  12060. # A message digest is included with the cookie to ensure data integrity:
  12061. # a user cannot alter his +user_id+ without knowing the secret key
  12062. # included in the hash. New apps are generated with a pregenerated secret
  12063. # in config/environment.rb. Set your own for old apps you're upgrading.
  12064. #
  12065. # Session options:
  12066. #
  12067. # * <tt>:secret</tt>: An application-wide key string. It's important that
  12068. # the secret is not vulnerable to a dictionary attack. Therefore, you
  12069. # should choose a secret consisting of random numbers and letters and
  12070. # more than 30 characters.
  12071. #
  12072. # secret: '449fe2e7daee471bffae2fd8dc02313d'
  12073. #
  12074. # * <tt>:digest</tt>: The message digest algorithm used to verify session
  12075. # integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
  12076. # such as 'MD5', 'RIPEMD160', 'SHA256', etc.
  12077. #
  12078. # To generate a secret key for an existing application, run
  12079. # "rake secret" and set the key in config/initializers/secret_token.rb.
  12080. #
  12081. # Note that changing digest or secret invalidates all existing sessions!
  12082. class CookieStore < Rack::Session::Abstract::ID
  12083. include Compatibility
  12084. include StaleSessionCheck
  12085. include SessionObject
  12086. def initialize(app, options={})
  12087. super(app, options.merge!(:cookie_only => true))
  12088. end
  12089. def destroy_session(env, session_id, options)
  12090. new_sid = generate_sid unless options[:drop]
  12091. # Reset hash and Assign the new session id
  12092. env["action_dispatch.request.unsigned_session_cookie"] = new_sid ? { "session_id" => new_sid } : {}
  12093. new_sid
  12094. end
  12095. def load_session(env)
  12096. stale_session_check! do
  12097. data = unpacked_cookie_data(env)
  12098. data = persistent_session_id!(data)
  12099. [data["session_id"], data]
  12100. end
  12101. end
  12102. private
  12103. def extract_session_id(env)
  12104. stale_session_check! do
  12105. unpacked_cookie_data(env)["session_id"]
  12106. end
  12107. end
  12108. def unpacked_cookie_data(env)
  12109. env["action_dispatch.request.unsigned_session_cookie"] ||= begin
  12110. stale_session_check! do
  12111. if data = get_cookie(env)
  12112. data.stringify_keys!
  12113. end
  12114. data || {}
  12115. end
  12116. end
  12117. end
  12118. def persistent_session_id!(data, sid=nil)
  12119. data ||= {}
  12120. data["session_id"] ||= sid || generate_sid
  12121. data
  12122. end
  12123. def set_session(env, sid, session_data, options)
  12124. session_data["session_id"] = sid
  12125. session_data
  12126. end
  12127. def set_cookie(env, session_id, cookie)
  12128. cookie_jar(env)[@key] = cookie
  12129. end
  12130. def get_cookie(env)
  12131. cookie_jar(env)[@key]
  12132. end
  12133. def cookie_jar(env)
  12134. request = ActionDispatch::Request.new(env)
  12135. request.cookie_jar.signed
  12136. end
  12137. end
  12138. class EncryptedCookieStore < CookieStore
  12139. private
  12140. def cookie_jar(env)
  12141. request = ActionDispatch::Request.new(env)
  12142. request.cookie_jar.encrypted
  12143. end
  12144. end
  12145. # This cookie store helps you upgrading apps that use +CookieStore+ to the new default +EncryptedCookieStore+
  12146. # To use this CookieStore set
  12147. #
  12148. # Myapp::Application.config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session'
  12149. #
  12150. # in your config/initializers/session_store.rb
  12151. #
  12152. # You will also need to add
  12153. #
  12154. # Myapp::Application.config.secret_key_base = 'some secret'
  12155. #
  12156. # in your config/initializers/secret_token.rb, but do not remove +Myapp::Application.config.secret_token = 'some secret'+
  12157. class UpgradeSignatureToEncryptionCookieStore < EncryptedCookieStore
  12158. private
  12159. def get_cookie(env)
  12160. signed_using_old_secret_cookie_jar(env)[@key] || cookie_jar(env)[@key]
  12161. end
  12162. def signed_using_old_secret_cookie_jar(env)
  12163. request = ActionDispatch::Request.new(env)
  12164. request.cookie_jar.signed_using_old_secret
  12165. end
  12166. end
  12167. end
  12168. end
  12169. require 'action_dispatch/middleware/session/abstract_store'
  12170. begin
  12171. require 'rack/session/dalli'
  12172. rescue LoadError => e
  12173. $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
  12174. raise e
  12175. end
  12176. module ActionDispatch
  12177. module Session
  12178. class MemCacheStore < Rack::Session::Dalli
  12179. include Compatibility
  12180. include StaleSessionCheck
  12181. include SessionObject
  12182. def initialize(app, options = {})
  12183. options[:expire_after] ||= options[:expires]
  12184. super
  12185. end
  12186. end
  12187. end
  12188. end
  12189. require 'action_dispatch/http/request'
  12190. require 'action_dispatch/middleware/exception_wrapper'
  12191. module ActionDispatch
  12192. # This middleware rescues any exception returned by the application
  12193. # and calls an exceptions app that will wrap it in a format for the end user.
  12194. #
  12195. # The exceptions app should be passed as parameter on initialization
  12196. # of ShowExceptions. Every time there is an exception, ShowExceptions will
  12197. # store the exception in env["action_dispatch.exception"], rewrite the
  12198. # PATH_INFO to the exception status code and call the rack app.
  12199. #
  12200. # If the application returns a "X-Cascade" pass response, this middleware
  12201. # will send an empty response as result with the correct status code.
  12202. # If any exception happens inside the exceptions app, this middleware
  12203. # catches the exceptions and returns a FAILSAFE_RESPONSE.
  12204. class ShowExceptions
  12205. FAILSAFE_RESPONSE = [500, { 'Content-Type' => 'text/plain' },
  12206. ["500 Internal Server Error\n" \
  12207. "If you are the administrator of this website, then please read this web " \
  12208. "application's log file and/or the web server's log file to find out what " \
  12209. "went wrong."]]
  12210. def initialize(app, exceptions_app)
  12211. @app = app
  12212. @exceptions_app = exceptions_app
  12213. end
  12214. def call(env)
  12215. @app.call(env)
  12216. rescue Exception => exception
  12217. raise exception if env['action_dispatch.show_exceptions'] == false
  12218. render_exception(env, exception)
  12219. end
  12220. private
  12221. def render_exception(env, exception)
  12222. wrapper = ExceptionWrapper.new(env, exception)
  12223. status = wrapper.status_code
  12224. env["action_dispatch.exception"] = wrapper.exception
  12225. env["PATH_INFO"] = "/#{status}"
  12226. response = @exceptions_app.call(env)
  12227. response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
  12228. rescue Exception => failsafe_error
  12229. $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
  12230. FAILSAFE_RESPONSE
  12231. end
  12232. def pass_response(status)
  12233. [status, {"Content-Type" => "text/html; charset=#{Response.default_charset}", "Content-Length" => "0"}, []]
  12234. end
  12235. end
  12236. end
  12237. module ActionDispatch
  12238. class SSL
  12239. YEAR = 31536000
  12240. def self.default_hsts_options
  12241. { :expires => YEAR, :subdomains => false }
  12242. end
  12243. def initialize(app, options = {})
  12244. @app = app
  12245. @hsts = options.fetch(:hsts, {})
  12246. @hsts = {} if @hsts == true
  12247. @hsts = self.class.default_hsts_options.merge(@hsts) if @hsts
  12248. @host = options[:host]
  12249. @port = options[:port]
  12250. end
  12251. def call(env)
  12252. request = Request.new(env)
  12253. if request.ssl?
  12254. status, headers, body = @app.call(env)
  12255. headers = hsts_headers.merge(headers)
  12256. flag_cookies_as_secure!(headers)
  12257. [status, headers, body]
  12258. else
  12259. redirect_to_https(request)
  12260. end
  12261. end
  12262. private
  12263. def redirect_to_https(request)
  12264. url = URI(request.url)
  12265. url.scheme = "https"
  12266. url.host = @host if @host
  12267. url.port = @port if @port
  12268. headers = hsts_headers.merge('Content-Type' => 'text/html',
  12269. 'Location' => url.to_s)
  12270. [301, headers, []]
  12271. end
  12272. # http://tools.ietf.org/html/draft-hodges-strict-transport-sec-02
  12273. def hsts_headers
  12274. if @hsts
  12275. value = "max-age=#{@hsts[:expires].to_i}"
  12276. value += "; includeSubDomains" if @hsts[:subdomains]
  12277. { 'Strict-Transport-Security' => value }
  12278. else
  12279. {}
  12280. end
  12281. end
  12282. def flag_cookies_as_secure!(headers)
  12283. if cookies = headers['Set-Cookie']
  12284. cookies = cookies.split("\n")
  12285. headers['Set-Cookie'] = cookies.map { |cookie|
  12286. if cookie !~ /;\s+secure(;|$)/
  12287. "#{cookie}; secure"
  12288. else
  12289. cookie
  12290. end
  12291. }.join("\n")
  12292. end
  12293. end
  12294. end
  12295. end
  12296. require "active_support/inflector/methods"
  12297. require "active_support/dependencies"
  12298. module ActionDispatch
  12299. class MiddlewareStack
  12300. class Middleware
  12301. attr_reader :args, :block, :name, :classcache
  12302. def initialize(klass_or_name, *args, &block)
  12303. @klass = nil
  12304. if klass_or_name.respond_to?(:name)
  12305. @klass = klass_or_name
  12306. @name = @klass.name
  12307. else
  12308. @name = klass_or_name.to_s
  12309. end
  12310. @classcache = ActiveSupport::Dependencies::Reference
  12311. @args, @block = args, block
  12312. end
  12313. def klass
  12314. @klass || classcache[@name]
  12315. end
  12316. def ==(middleware)
  12317. case middleware
  12318. when Middleware
  12319. klass == middleware.klass
  12320. when Class
  12321. klass == middleware
  12322. else
  12323. normalize(@name) == normalize(middleware)
  12324. end
  12325. end
  12326. def inspect
  12327. klass.to_s
  12328. end
  12329. def build(app)
  12330. klass.new(app, *args, &block)
  12331. end
  12332. private
  12333. def normalize(object)
  12334. object.to_s.strip.sub(/^::/, '')
  12335. end
  12336. end
  12337. include Enumerable
  12338. attr_accessor :middlewares
  12339. def initialize(*args)
  12340. @middlewares = []
  12341. yield(self) if block_given?
  12342. end
  12343. def each
  12344. @middlewares.each { |x| yield x }
  12345. end
  12346. def size
  12347. middlewares.size
  12348. end
  12349. def last
  12350. middlewares.last
  12351. end
  12352. def [](i)
  12353. middlewares[i]
  12354. end
  12355. def unshift(*args, &block)
  12356. middleware = self.class::Middleware.new(*args, &block)
  12357. middlewares.unshift(middleware)
  12358. end
  12359. def initialize_copy(other)
  12360. self.middlewares = other.middlewares.dup
  12361. end
  12362. def insert(index, *args, &block)
  12363. index = assert_index(index, :before)
  12364. middleware = self.class::Middleware.new(*args, &block)
  12365. middlewares.insert(index, middleware)
  12366. end
  12367. alias_method :insert_before, :insert
  12368. def insert_after(index, *args, &block)
  12369. index = assert_index(index, :after)
  12370. insert(index + 1, *args, &block)
  12371. end
  12372. def swap(target, *args, &block)
  12373. index = assert_index(target, :before)
  12374. insert(index, *args, &block)
  12375. middlewares.delete_at(index + 1)
  12376. end
  12377. def delete(target)
  12378. middlewares.delete target
  12379. end
  12380. def use(*args, &block)
  12381. middleware = self.class::Middleware.new(*args, &block)
  12382. middlewares.push(middleware)
  12383. end
  12384. def build(app = nil, &block)
  12385. app ||= block
  12386. raise "MiddlewareStack#build requires an app" unless app
  12387. middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
  12388. end
  12389. protected
  12390. def assert_index(index, where)
  12391. i = index.is_a?(Integer) ? index : middlewares.index(index)
  12392. raise "No such middleware to insert #{where}: #{index.inspect}" unless i
  12393. i
  12394. end
  12395. end
  12396. end
  12397. require 'rack/utils'
  12398. require 'active_support/core_ext/uri'
  12399. module ActionDispatch
  12400. class FileHandler
  12401. def initialize(root, cache_control)
  12402. @root = root.chomp('/')
  12403. @compiled_root = /^#{Regexp.escape(root)}/
  12404. headers = cache_control && { 'Cache-Control' => cache_control }
  12405. @file_server = ::Rack::File.new(@root, headers)
  12406. end
  12407. def match?(path)
  12408. path = path.dup
  12409. full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(unescape_path(path)))
  12410. paths = "#{full_path}#{ext}"
  12411. matches = Dir[paths]
  12412. match = matches.detect { |m| File.file?(m) }
  12413. if match
  12414. match.sub!(@compiled_root, '')
  12415. ::Rack::Utils.escape(match)
  12416. end
  12417. end
  12418. def call(env)
  12419. @file_server.call(env)
  12420. end
  12421. def ext
  12422. @ext ||= begin
  12423. ext = ::ActionController::Base.default_static_extension
  12424. "{,#{ext},/index#{ext}}"
  12425. end
  12426. end
  12427. def unescape_path(path)
  12428. URI.parser.unescape(path)
  12429. end
  12430. def escape_glob_chars(path)
  12431. path.force_encoding('binary') if path.respond_to? :force_encoding
  12432. path.gsub(/[*?{}\[\]]/, "\\\\\\&")
  12433. end
  12434. end
  12435. class Static
  12436. def initialize(app, path, cache_control=nil)
  12437. @app = app
  12438. @file_handler = FileHandler.new(path, cache_control)
  12439. end
  12440. def call(env)
  12441. case env['REQUEST_METHOD']
  12442. when 'GET', 'HEAD'
  12443. path = env['PATH_INFO'].chomp('/')
  12444. if match = @file_handler.match?(path)
  12445. env["PATH_INFO"] = match
  12446. return @file_handler.call(env)
  12447. end
  12448. end
  12449. @app.call(env)
  12450. end
  12451. end
  12452. end
  12453. require "action_dispatch"
  12454. module ActionDispatch
  12455. class Railtie < Rails::Railtie # :nodoc:
  12456. config.action_dispatch = ActiveSupport::OrderedOptions.new
  12457. config.action_dispatch.x_sendfile_header = nil
  12458. config.action_dispatch.ip_spoofing_check = true
  12459. config.action_dispatch.show_exceptions = true
  12460. config.action_dispatch.tld_length = 1
  12461. config.action_dispatch.ignore_accept_header = false
  12462. config.action_dispatch.rescue_templates = { }
  12463. config.action_dispatch.rescue_responses = { }
  12464. config.action_dispatch.default_charset = nil
  12465. config.action_dispatch.rack_cache = false
  12466. config.action_dispatch.http_auth_salt = 'http authentication'
  12467. config.action_dispatch.signed_cookie_salt = 'signed cookie'
  12468. config.action_dispatch.encrypted_cookie_salt = 'encrypted cookie'
  12469. config.action_dispatch.encrypted_signed_cookie_salt = 'signed encrypted cookie'
  12470. config.action_dispatch.default_headers = {
  12471. 'X-Frame-Options' => 'SAMEORIGIN',
  12472. 'X-XSS-Protection' => '1; mode=block',
  12473. 'X-Content-Type-Options' => 'nosniff',
  12474. 'X-UA-Compatible' => 'chrome=1'
  12475. }
  12476. config.eager_load_namespaces << ActionDispatch
  12477. initializer "action_dispatch.configure" do |app|
  12478. ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
  12479. ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
  12480. ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding
  12481. ActionDispatch::Response.default_headers = app.config.action_dispatch.default_headers
  12482. ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses)
  12483. ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates)
  12484. config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
  12485. ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
  12486. ActionDispatch.test_app = app
  12487. end
  12488. end
  12489. end
  12490. require 'rack/session/abstract/id'
  12491. module ActionDispatch
  12492. class Request < Rack::Request
  12493. # Session is responsible for lazily loading the session from store.
  12494. class Session # :nodoc:
  12495. ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc:
  12496. ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc:
  12497. def self.create(store, env, default_options)
  12498. session_was = find env
  12499. session = Request::Session.new(store, env)
  12500. session.merge! session_was if session_was
  12501. set(env, session)
  12502. Options.set(env, Request::Session::Options.new(store, env, default_options))
  12503. session
  12504. end
  12505. def self.find(env)
  12506. env[ENV_SESSION_KEY]
  12507. end
  12508. def self.set(env, session)
  12509. env[ENV_SESSION_KEY] = session
  12510. end
  12511. class Options #:nodoc:
  12512. def self.set(env, options)
  12513. env[ENV_SESSION_OPTIONS_KEY] = options
  12514. end
  12515. def self.find(env)
  12516. env[ENV_SESSION_OPTIONS_KEY]
  12517. end
  12518. def initialize(by, env, default_options)
  12519. @by = by
  12520. @env = env
  12521. @delegate = default_options.dup
  12522. end
  12523. def [](key)
  12524. if key == :id
  12525. @delegate.fetch(key) {
  12526. @delegate[:id] = @by.send(:extract_session_id, @env)
  12527. }
  12528. else
  12529. @delegate[key]
  12530. end
  12531. end
  12532. def []=(k,v); @delegate[k] = v; end
  12533. def to_hash; @delegate.dup; end
  12534. def values_at(*args); @delegate.values_at(*args); end
  12535. end
  12536. def initialize(by, env)
  12537. @by = by
  12538. @env = env
  12539. @delegate = {}
  12540. @loaded = false
  12541. @exists = nil # we haven't checked yet
  12542. end
  12543. def id
  12544. options[:id]
  12545. end
  12546. def options
  12547. Options.find @env
  12548. end
  12549. def destroy
  12550. clear
  12551. options = self.options || {}
  12552. new_sid = @by.send(:destroy_session, @env, options[:id], options)
  12553. options[:id] = new_sid # Reset session id with a new value or nil
  12554. # Load the new sid to be written with the response
  12555. @loaded = false
  12556. load_for_write!
  12557. end
  12558. def [](key)
  12559. load_for_read!
  12560. @delegate[key.to_s]
  12561. end
  12562. def has_key?(key)
  12563. load_for_read!
  12564. @delegate.key?(key.to_s)
  12565. end
  12566. alias :key? :has_key?
  12567. alias :include? :has_key?
  12568. def keys
  12569. @delegate.keys
  12570. end
  12571. def values
  12572. @delegate.values
  12573. end
  12574. def []=(key, value)
  12575. load_for_write!
  12576. @delegate[key.to_s] = value
  12577. end
  12578. def clear
  12579. load_for_write!
  12580. @delegate.clear
  12581. end
  12582. def to_hash
  12583. load_for_read!
  12584. @delegate.dup.delete_if { |_,v| v.nil? }
  12585. end
  12586. def update(hash)
  12587. load_for_write!
  12588. @delegate.update stringify_keys(hash)
  12589. end
  12590. def delete(key)
  12591. load_for_write!
  12592. @delegate.delete key.to_s
  12593. end
  12594. def inspect
  12595. if loaded?
  12596. super
  12597. else
  12598. "#<#{self.class}:0x#{(object_id << 1).to_s(16)} not yet loaded>"
  12599. end
  12600. end
  12601. def exists?
  12602. return @exists unless @exists.nil?
  12603. @exists = @by.send(:session_exists?, @env)
  12604. end
  12605. def loaded?
  12606. @loaded
  12607. end
  12608. def empty?
  12609. load_for_read!
  12610. @delegate.empty?
  12611. end
  12612. def merge!(other)
  12613. load_for_write!
  12614. @delegate.merge!(other)
  12615. end
  12616. private
  12617. def load_for_read!
  12618. load! if !loaded? && exists?
  12619. end
  12620. def load_for_write!
  12621. load! unless loaded?
  12622. end
  12623. def load!
  12624. id, session = @by.load_session @env
  12625. options[:id] = id
  12626. @delegate.replace(stringify_keys(session))
  12627. @loaded = true
  12628. end
  12629. def stringify_keys(other)
  12630. other.each_with_object({}) { |(key, value), hash|
  12631. hash[key.to_s] = value
  12632. }
  12633. end
  12634. end
  12635. end
  12636. end
  12637. require 'delegate'
  12638. module ActionDispatch
  12639. module Routing
  12640. class RouteWrapper < SimpleDelegator
  12641. def endpoint
  12642. rack_app ? rack_app.inspect : "#{controller}##{action}"
  12643. end
  12644. def constraints
  12645. requirements.except(:controller, :action)
  12646. end
  12647. def rack_app(app = self.app)
  12648. @rack_app ||= begin
  12649. class_name = app.class.name.to_s
  12650. if class_name == "ActionDispatch::Routing::Mapper::Constraints"
  12651. rack_app(app.app)
  12652. elsif ActionDispatch::Routing::Redirect === app || class_name !~ /^ActionDispatch::Routing/
  12653. app
  12654. end
  12655. end
  12656. end
  12657. def verb
  12658. super.source.gsub(/[$^]/, '')
  12659. end
  12660. def path
  12661. super.spec.to_s
  12662. end
  12663. def name
  12664. super.to_s
  12665. end
  12666. def regexp
  12667. __getobj__.path.to_regexp
  12668. end
  12669. def json_regexp
  12670. str = regexp.inspect.
  12671. sub('\\A' , '^').
  12672. sub('\\Z' , '$').
  12673. sub('\\z' , '$').
  12674. sub(/^\// , '').
  12675. sub(/\/[a-z]*$/ , '').
  12676. gsub(/\(\?#.+\)/ , '').
  12677. gsub(/\(\?-\w+:/ , '(').
  12678. gsub(/\s/ , '')
  12679. Regexp.new(str).source
  12680. end
  12681. def reqs
  12682. @reqs ||= begin
  12683. reqs = endpoint
  12684. reqs += " #{constraints.to_s}" unless constraints.empty?
  12685. reqs
  12686. end
  12687. end
  12688. def controller
  12689. requirements[:controller] || ':controller'
  12690. end
  12691. def action
  12692. requirements[:action] || ':action'
  12693. end
  12694. def internal?
  12695. controller =~ %r{\Arails/(info|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}}
  12696. end
  12697. def engine?
  12698. rack_app && rack_app.respond_to?(:routes)
  12699. end
  12700. end
  12701. ##
  12702. # This class is just used for displaying route information when someone
  12703. # executes `rake routes` or looks at the RoutingError page.
  12704. # People should not use this class.
  12705. class RoutesInspector # :nodoc:
  12706. def initialize(routes)
  12707. @engines = {}
  12708. @routes = routes
  12709. end
  12710. def format(formatter, filter = nil)
  12711. routes_to_display = filter_routes(filter)
  12712. routes = collect_routes(routes_to_display)
  12713. formatter.section routes
  12714. @engines.each do |name, engine_routes|
  12715. formatter.section_title "Routes for #{name}"
  12716. formatter.section engine_routes
  12717. end
  12718. formatter.result
  12719. end
  12720. private
  12721. def filter_routes(filter)
  12722. if filter
  12723. @routes.select { |route| route.defaults[:controller] == filter }
  12724. else
  12725. @routes
  12726. end
  12727. end
  12728. def collect_routes(routes)
  12729. routes.collect do |route|
  12730. RouteWrapper.new(route)
  12731. end.reject do |route|
  12732. route.internal?
  12733. end.collect do |route|
  12734. collect_engine_routes(route)
  12735. { name: route.name,
  12736. verb: route.verb,
  12737. path: route.path,
  12738. reqs: route.reqs,
  12739. regexp: route.json_regexp }
  12740. end
  12741. end
  12742. def collect_engine_routes(route)
  12743. name = route.endpoint
  12744. return unless route.engine?
  12745. return if @engines[name]
  12746. routes = route.rack_app.routes
  12747. if routes.is_a?(ActionDispatch::Routing::RouteSet)
  12748. @engines[name] = collect_routes(routes.routes)
  12749. end
  12750. end
  12751. end
  12752. class ConsoleFormatter
  12753. def initialize
  12754. @buffer = []
  12755. end
  12756. def result
  12757. @buffer.join("\n")
  12758. end
  12759. def section_title(title)
  12760. @buffer << "\n#{title}:"
  12761. end
  12762. def section(routes)
  12763. @buffer << draw_section(routes)
  12764. end
  12765. private
  12766. def draw_section(routes)
  12767. name_width = routes.map { |r| r[:name].length }.max
  12768. verb_width = routes.map { |r| r[:verb].length }.max
  12769. path_width = routes.map { |r| r[:path].length }.max
  12770. routes.map do |r|
  12771. "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
  12772. end
  12773. end
  12774. end
  12775. class HtmlTableFormatter
  12776. def initialize(view)
  12777. @view = view
  12778. @buffer = []
  12779. end
  12780. def section_title(title)
  12781. @buffer << %(<tr><th colspan="4">#{title}</th></tr>)
  12782. end
  12783. def section(routes)
  12784. @buffer << @view.render(partial: "routes/route", collection: routes)
  12785. end
  12786. def result
  12787. @view.raw @view.render(layout: "routes/table") {
  12788. @view.raw @buffer.join("\n")
  12789. }
  12790. end
  12791. end
  12792. end
  12793. end
  12794. require 'active_support/core_ext/hash/except'
  12795. require 'active_support/core_ext/hash/reverse_merge'
  12796. require 'active_support/core_ext/hash/slice'
  12797. require 'active_support/core_ext/enumerable'
  12798. require 'active_support/core_ext/array/extract_options'
  12799. require 'active_support/inflector'
  12800. require 'action_dispatch/routing/redirection'
  12801. module ActionDispatch
  12802. module Routing
  12803. class Mapper
  12804. URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
  12805. class Constraints #:nodoc:
  12806. def self.new(app, constraints, request = Rack::Request)
  12807. if constraints.any?
  12808. super(app, constraints, request)
  12809. else
  12810. app
  12811. end
  12812. end
  12813. attr_reader :app, :constraints
  12814. def initialize(app, constraints, request)
  12815. @app, @constraints, @request = app, constraints, request
  12816. end
  12817. def matches?(env)
  12818. req = @request.new(env)
  12819. @constraints.all? do |constraint|
  12820. (constraint.respond_to?(:matches?) && constraint.matches?(req)) ||
  12821. (constraint.respond_to?(:call) && constraint.call(*constraint_args(constraint, req)))
  12822. end
  12823. ensure
  12824. req.reset_parameters
  12825. end
  12826. def call(env)
  12827. matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
  12828. end
  12829. private
  12830. def constraint_args(constraint, request)
  12831. constraint.arity == 1 ? [request] : [request.symbolized_path_parameters, request]
  12832. end
  12833. end
  12834. class Mapping #:nodoc:
  12835. IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix, :format]
  12836. ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
  12837. SHORTHAND_REGEX = %r{/[\w/]+$}
  12838. WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
  12839. attr_reader :scope, :path, :options, :requirements, :conditions, :defaults
  12840. def initialize(set, scope, path, options)
  12841. @set, @scope, @path, @options = set, scope, path, options
  12842. @requirements, @conditions, @defaults = {}, {}, {}
  12843. normalize_path!
  12844. normalize_options!
  12845. normalize_requirements!
  12846. normalize_conditions!
  12847. normalize_defaults!
  12848. end
  12849. def to_route
  12850. [ app, conditions, requirements, defaults, options[:as], options[:anchor] ]
  12851. end
  12852. private
  12853. def normalize_path!
  12854. raise ArgumentError, "path is required" if @path.blank?
  12855. @path = Mapper.normalize_path(@path)
  12856. if required_format?
  12857. @path = "#{@path}.:format"
  12858. elsif optional_format?
  12859. @path = "#{@path}(.:format)"
  12860. end
  12861. end
  12862. def required_format?
  12863. options[:format] == true
  12864. end
  12865. def optional_format?
  12866. options[:format] != false && !path.include?(':format') && !path.end_with?('/')
  12867. end
  12868. def normalize_options!
  12869. @options.reverse_merge!(scope[:options]) if scope[:options]
  12870. path_without_format = path.sub(/\(\.:format\)$/, '')
  12871. # Add a constraint for wildcard route to make it non-greedy and match the
  12872. # optional format part of the route by default
  12873. if path_without_format.match(WILDCARD_PATH) && @options[:format] != false
  12874. @options[$1.to_sym] ||= /.+?/
  12875. end
  12876. if path_without_format.match(':controller')
  12877. raise ArgumentError, ":controller segment is not allowed within a namespace block" if scope[:module]
  12878. # Add a default constraint for :controller path segments that matches namespaced
  12879. # controllers with default routes like :controller/:action/:id(.:format), e.g:
  12880. # GET /admin/products/show/1
  12881. # => { controller: 'admin/products', action: 'show', id: '1' }
  12882. @options[:controller] ||= /.+?/
  12883. end
  12884. if using_match_shorthand?(path_without_format, @options)
  12885. to_shorthand = @options[:to].blank?
  12886. @options[:to] ||= path_without_format.gsub(/\(.*\)/, "")[1..-1].sub(%r{/([^/]*)$}, '#\1')
  12887. end
  12888. @options.merge!(default_controller_and_action(to_shorthand))
  12889. end
  12890. # match "account/overview"
  12891. def using_match_shorthand?(path, options)
  12892. path && (options[:to] || options[:action]).nil? && path =~ SHORTHAND_REGEX
  12893. end
  12894. def normalize_format!
  12895. if options[:format] == true
  12896. options[:format] = /.+/
  12897. elsif options[:format] == false
  12898. options.delete(:format)
  12899. end
  12900. end
  12901. def normalize_requirements!
  12902. constraints.each do |key, requirement|
  12903. next unless segment_keys.include?(key) || key == :controller
  12904. if requirement.source =~ ANCHOR_CHARACTERS_REGEX
  12905. raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
  12906. end
  12907. if requirement.multiline?
  12908. raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
  12909. end
  12910. @requirements[key] = requirement
  12911. end
  12912. if options[:format] == true
  12913. @requirements[:format] = /.+/
  12914. elsif Regexp === options[:format]
  12915. @requirements[:format] = options[:format]
  12916. elsif String === options[:format]
  12917. @requirements[:format] = Regexp.compile(options[:format])
  12918. end
  12919. end
  12920. def normalize_defaults!
  12921. @defaults.merge!(scope[:defaults]) if scope[:defaults]
  12922. @defaults.merge!(options[:defaults]) if options[:defaults]
  12923. options.each do |key, default|
  12924. next if Regexp === default || IGNORE_OPTIONS.include?(key)
  12925. @defaults[key] = default
  12926. end
  12927. if options[:constraints].is_a?(Hash)
  12928. options[:constraints].each do |key, default|
  12929. next unless URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
  12930. @defaults[key] ||= default
  12931. end
  12932. end
  12933. if Regexp === options[:format]
  12934. @defaults[:format] = nil
  12935. elsif String === options[:format]
  12936. @defaults[:format] = options[:format]
  12937. end
  12938. end
  12939. def normalize_conditions!
  12940. @conditions.merge!(:path_info => path)
  12941. constraints.each do |key, condition|
  12942. next if segment_keys.include?(key) || key == :controller
  12943. @conditions[key] = condition
  12944. end
  12945. @conditions[:required_defaults] = []
  12946. options.each do |key, required_default|
  12947. next if segment_keys.include?(key) || IGNORE_OPTIONS.include?(key)
  12948. next if Regexp === required_default
  12949. @conditions[:required_defaults] << key
  12950. end
  12951. via_all = options.delete(:via) if options[:via] == :all
  12952. if !via_all && options[:via].blank?
  12953. msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
  12954. "If you want to expose your action to GET, use `get` in the router:\n\n" \
  12955. " Instead of: match \"controller#action\"\n" \
  12956. " Do: get \"controller#action\""
  12957. raise msg
  12958. end
  12959. if via = options[:via]
  12960. list = Array(via).map { |m| m.to_s.dasherize.upcase }
  12961. @conditions.merge!(:request_method => list)
  12962. end
  12963. end
  12964. def app
  12965. Constraints.new(endpoint, blocks, @set.request_class)
  12966. end
  12967. def default_controller_and_action(to_shorthand=nil)
  12968. if to.respond_to?(:call)
  12969. { }
  12970. else
  12971. if to.is_a?(String)
  12972. controller, action = to.split('#')
  12973. elsif to.is_a?(Symbol)
  12974. action = to.to_s
  12975. end
  12976. controller ||= default_controller
  12977. action ||= default_action
  12978. unless controller.is_a?(Regexp) || to_shorthand
  12979. controller = [@scope[:module], controller].compact.join("/").presence
  12980. end
  12981. if controller.is_a?(String) && controller =~ %r{\A/}
  12982. raise ArgumentError, "controller name should not start with a slash"
  12983. end
  12984. controller = controller.to_s unless controller.is_a?(Regexp)
  12985. action = action.to_s unless action.is_a?(Regexp)
  12986. if controller.blank? && segment_keys.exclude?(:controller)
  12987. raise ArgumentError, "missing :controller"
  12988. end
  12989. if action.blank? && segment_keys.exclude?(:action)
  12990. raise ArgumentError, "missing :action"
  12991. end
  12992. if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/
  12993. message = "'#{controller}' is not a supported controller name. This can lead to potential routing problems."
  12994. message << " See http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use"
  12995. raise ArgumentError, message
  12996. end
  12997. hash = {}
  12998. hash[:controller] = controller unless controller.blank?
  12999. hash[:action] = action unless action.blank?
  13000. hash
  13001. end
  13002. end
  13003. def blocks
  13004. if options[:constraints].present? && !options[:constraints].is_a?(Hash)
  13005. [options[:constraints]]
  13006. else
  13007. scope[:blocks] || []
  13008. end
  13009. end
  13010. def constraints
  13011. @constraints ||= {}.tap do |constraints|
  13012. constraints.merge!(scope[:constraints]) if scope[:constraints]
  13013. options.except(*IGNORE_OPTIONS).each do |key, option|
  13014. constraints[key] = option if Regexp === option
  13015. end
  13016. constraints.merge!(options[:constraints]) if options[:constraints].is_a?(Hash)
  13017. end
  13018. end
  13019. def segment_keys
  13020. @segment_keys ||= path_pattern.names.map{ |s| s.to_sym }
  13021. end
  13022. def path_pattern
  13023. Journey::Path::Pattern.new(strexp)
  13024. end
  13025. def strexp
  13026. Journey::Router::Strexp.compile(path, requirements, SEPARATORS)
  13027. end
  13028. def endpoint
  13029. to.respond_to?(:call) ? to : dispatcher
  13030. end
  13031. def dispatcher
  13032. Routing::RouteSet::Dispatcher.new(:defaults => defaults)
  13033. end
  13034. def to
  13035. options[:to]
  13036. end
  13037. def default_controller
  13038. options[:controller] || scope[:controller]
  13039. end
  13040. def default_action
  13041. options[:action] || scope[:action]
  13042. end
  13043. end
  13044. # Invokes Rack::Mount::Utils.normalize path and ensure that
  13045. # (:locale) becomes (/:locale) instead of /(:locale). Except
  13046. # for root cases, where the latter is the correct one.
  13047. def self.normalize_path(path)
  13048. path = Journey::Router::Utils.normalize_path(path)
  13049. path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^)]+\)$}
  13050. path
  13051. end
  13052. def self.normalize_name(name)
  13053. normalize_path(name)[1..-1].tr("/", "_")
  13054. end
  13055. module Base
  13056. # You can specify what Rails should route "/" to with the root method:
  13057. #
  13058. # root to: 'pages#main'
  13059. #
  13060. # For options, see +match+, as +root+ uses it internally.
  13061. #
  13062. # You can also pass a string which will expand
  13063. #
  13064. # root 'pages#main'
  13065. #
  13066. # You should put the root route at the top of <tt>config/routes.rb</tt>,
  13067. # because this means it will be matched first. As this is the most popular route
  13068. # of most Rails applications, this is beneficial.
  13069. def root(options = {})
  13070. options = { :to => options } if options.is_a?(String)
  13071. match '/', { :as => :root, :via => :get }.merge!(options)
  13072. end
  13073. # Matches a url pattern to one or more routes. Any symbols in a pattern
  13074. # are interpreted as url query parameters and thus available as +params+
  13075. # in an action:
  13076. #
  13077. # # sets :controller, :action and :id in params
  13078. # match ':controller/:action/:id'
  13079. #
  13080. # Two of these symbols are special, +:controller+ maps to the controller
  13081. # and +:action+ to the controller's action. A pattern can also map
  13082. # wildcard segments (globs) to params:
  13083. #
  13084. # match 'songs/*category/:title', to: 'songs#show'
  13085. #
  13086. # # 'songs/rock/classic/stairway-to-heaven' sets
  13087. # # params[:category] = 'rock/classic'
  13088. # # params[:title] = 'stairway-to-heaven'
  13089. #
  13090. # When a pattern points to an internal route, the route's +:action+ and
  13091. # +:controller+ should be set in options or hash shorthand. Examples:
  13092. #
  13093. # match 'photos/:id' => 'photos#show'
  13094. # match 'photos/:id', to: 'photos#show'
  13095. # match 'photos/:id', controller: 'photos', action: 'show'
  13096. #
  13097. # A pattern can also point to a +Rack+ endpoint i.e. anything that
  13098. # responds to +call+:
  13099. #
  13100. # match 'photos/:id', to: lambda {|hash| [200, {}, ["Coming soon"]] }
  13101. # match 'photos/:id', to: PhotoRackApp
  13102. # # Yes, controller actions are just rack endpoints
  13103. # match 'photos/:id', to: PhotosController.action(:show)
  13104. #
  13105. # Because request various HTTP verbs with a single action has security
  13106. # implications, is recommendable use HttpHelpers[rdoc-ref:HttpHelpers]
  13107. # instead +match+
  13108. #
  13109. # === Options
  13110. #
  13111. # Any options not seen here are passed on as params with the url.
  13112. #
  13113. # [:controller]
  13114. # The route's controller.
  13115. #
  13116. # [:action]
  13117. # The route's action.
  13118. #
  13119. # [:path]
  13120. # The path prefix for the routes.
  13121. #
  13122. # [:module]
  13123. # The namespace for :controller.
  13124. #
  13125. # match 'path', to: 'c#a', module: 'sekret', controller: 'posts'
  13126. # #=> Sekret::PostsController
  13127. #
  13128. # See <tt>Scoping#namespace</tt> for its scope equivalent.
  13129. #
  13130. # [:as]
  13131. # The name used to generate routing helpers.
  13132. #
  13133. # [:via]
  13134. # Allowed HTTP verb(s) for route.
  13135. #
  13136. # match 'path', to: 'c#a', via: :get
  13137. # match 'path', to: 'c#a', via: [:get, :post]
  13138. # match 'path', to: 'c#a', via: :all
  13139. #
  13140. # [:to]
  13141. # Points to a +Rack+ endpoint. Can be an object that responds to
  13142. # +call+ or a string representing a controller's action.
  13143. #
  13144. # match 'path', to: 'controller#action'
  13145. # match 'path', to: lambda { |env| [200, {}, ["Success!"]] }
  13146. # match 'path', to: RackApp
  13147. #
  13148. # [:on]
  13149. # Shorthand for wrapping routes in a specific RESTful context. Valid
  13150. # values are +:member+, +:collection+, and +:new+. Only use within
  13151. # <tt>resource(s)</tt> block. For example:
  13152. #
  13153. # resource :bar do
  13154. # match 'foo', to: 'c#a', on: :member, via: [:get, :post]
  13155. # end
  13156. #
  13157. # Is equivalent to:
  13158. #
  13159. # resource :bar do
  13160. # member do
  13161. # match 'foo', to: 'c#a', via: [:get, :post]
  13162. # end
  13163. # end
  13164. #
  13165. # [:constraints]
  13166. # Constrains parameters with a hash of regular expressions or an
  13167. # object that responds to <tt>matches?</tt>
  13168. #
  13169. # match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }
  13170. #
  13171. # class Blacklist
  13172. # def matches?(request) request.remote_ip == '1.2.3.4' end
  13173. # end
  13174. # match 'path', to: 'c#a', constraints: Blacklist.new
  13175. #
  13176. # See <tt>Scoping#constraints</tt> for more examples with its scope
  13177. # equivalent.
  13178. #
  13179. # [:defaults]
  13180. # Sets defaults for parameters
  13181. #
  13182. # # Sets params[:format] to 'jpg' by default
  13183. # match 'path', to: 'c#a', defaults: { format: 'jpg' }
  13184. #
  13185. # See <tt>Scoping#defaults</tt> for its scope equivalent.
  13186. #
  13187. # [:anchor]
  13188. # Boolean to anchor a <tt>match</tt> pattern. Default is true. When set to
  13189. # false, the pattern matches any request prefixed with the given path.
  13190. #
  13191. # # Matches any request starting with 'path'
  13192. # match 'path', to: 'c#a', anchor: false
  13193. #
  13194. # [:format]
  13195. # Allows you to specify the default value for optional +format+
  13196. # segment or disable it by supplying +false+.
  13197. def match(path, options=nil)
  13198. end
  13199. # Mount a Rack-based application to be used within the application.
  13200. #
  13201. # mount SomeRackApp, at: "some_route"
  13202. #
  13203. # Alternatively:
  13204. #
  13205. # mount(SomeRackApp => "some_route")
  13206. #
  13207. # For options, see +match+, as +mount+ uses it internally.
  13208. #
  13209. # All mounted applications come with routing helpers to access them.
  13210. # These are named after the class specified, so for the above example
  13211. # the helper is either +some_rack_app_path+ or +some_rack_app_url+.
  13212. # To customize this helper's name, use the +:as+ option:
  13213. #
  13214. # mount(SomeRackApp => "some_route", as: "exciting")
  13215. #
  13216. # This will generate the +exciting_path+ and +exciting_url+ helpers
  13217. # which can be used to navigate to this mounted app.
  13218. def mount(app, options = nil)
  13219. if options
  13220. path = options.delete(:at)
  13221. else
  13222. unless Hash === app
  13223. raise ArgumentError, "must be called with mount point"
  13224. end
  13225. options = app
  13226. app, path = options.find { |k, v| k.respond_to?(:call) }
  13227. options.delete(app) if app
  13228. end
  13229. raise "A rack application must be specified" unless path
  13230. options[:as] ||= app_name(app)
  13231. options[:via] ||= :all
  13232. match(path, options.merge(:to => app, :anchor => false, :format => false))
  13233. define_generate_prefix(app, options[:as])
  13234. self
  13235. end
  13236. def default_url_options=(options)
  13237. @set.default_url_options = options
  13238. end
  13239. alias_method :default_url_options, :default_url_options=
  13240. def with_default_scope(scope, &block)
  13241. scope(scope) do
  13242. instance_exec(&block)
  13243. end
  13244. end
  13245. private
  13246. def app_name(app)
  13247. return unless app.respond_to?(:routes)
  13248. if app.respond_to?(:railtie_name)
  13249. app.railtie_name
  13250. else
  13251. class_name = app.class.is_a?(Class) ? app.name : app.class.name
  13252. ActiveSupport::Inflector.underscore(class_name).tr("/", "_")
  13253. end
  13254. end
  13255. def define_generate_prefix(app, name)
  13256. return unless app.respond_to?(:routes) && app.routes.respond_to?(:define_mounted_helper)
  13257. _route = @set.named_routes.routes[name.to_sym]
  13258. _routes = @set
  13259. app.routes.define_mounted_helper(name)
  13260. app.routes.singleton_class.class_eval do
  13261. define_method :mounted? do
  13262. true
  13263. end
  13264. define_method :_generate_prefix do |options|
  13265. prefix_options = options.slice(*_rou