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

https://github.com/rimolive/core · Ruby · 93875 lines · 57220 code · 6681 blank · 29974 comment · 2986 complexity · b0245bf2875f2473b31162a1cc38de04 MD5 · raw file

  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 shouldnt 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(*_route.segment_keys)
  13266. # we must actually delete prefix segment keys to avoid passing them to next url_for
  13267. _route.segment_keys.each { |k| options.delete(k) }
  13268. _routes.url_helpers.send("#{name}_path", prefix_options)
  13269. end
  13270. end
  13271. end
  13272. end
  13273. module HttpHelpers
  13274. # Define a route that only recognizes HTTP GET.
  13275. # For supported arguments, see match[rdoc-ref:Base#match]
  13276. #
  13277. # get 'bacon', to: 'food#bacon'
  13278. def get(*args, &block)
  13279. map_method(:get, args, &block)
  13280. end
  13281. # Define a route that only recognizes HTTP POST.
  13282. # For supported arguments, see match[rdoc-ref:Base#match]
  13283. #
  13284. # post 'bacon', to: 'food#bacon'
  13285. def post(*args, &block)
  13286. map_method(:post, args, &block)
  13287. end
  13288. # Define a route that only recognizes HTTP PATCH.
  13289. # For supported arguments, see match[rdoc-ref:Base#match]
  13290. #
  13291. # patch 'bacon', to: 'food#bacon'
  13292. def patch(*args, &block)
  13293. map_method(:patch, args, &block)
  13294. end
  13295. # Define a route that only recognizes HTTP PUT.
  13296. # For supported arguments, see match[rdoc-ref:Base#match]
  13297. #
  13298. # put 'bacon', to: 'food#bacon'
  13299. def put(*args, &block)
  13300. map_method(:put, args, &block)
  13301. end
  13302. # Define a route that only recognizes HTTP DELETE.
  13303. # For supported arguments, see match[rdoc-ref:Base#match]
  13304. #
  13305. # delete 'broccoli', to: 'food#broccoli'
  13306. def delete(*args, &block)
  13307. map_method(:delete, args, &block)
  13308. end
  13309. private
  13310. def map_method(method, args, &block)
  13311. options = args.extract_options!
  13312. options[:via] = method
  13313. options[:path] ||= args.first if args.first.is_a?(String)
  13314. match(*args, options, &block)
  13315. self
  13316. end
  13317. end
  13318. # You may wish to organize groups of controllers under a namespace.
  13319. # Most commonly, you might group a number of administrative controllers
  13320. # under an +admin+ namespace. You would place these controllers under
  13321. # the <tt>app/controllers/admin</tt> directory, and you can group them
  13322. # together in your router:
  13323. #
  13324. # namespace "admin" do
  13325. # resources :posts, :comments
  13326. # end
  13327. #
  13328. # This will create a number of routes for each of the posts and comments
  13329. # controller. For <tt>Admin::PostsController</tt>, Rails will create:
  13330. #
  13331. # GET /admin/posts
  13332. # GET /admin/posts/new
  13333. # POST /admin/posts
  13334. # GET /admin/posts/1
  13335. # GET /admin/posts/1/edit
  13336. # PATCH/PUT /admin/posts/1
  13337. # DELETE /admin/posts/1
  13338. #
  13339. # If you want to route /posts (without the prefix /admin) to
  13340. # <tt>Admin::PostsController</tt>, you could use
  13341. #
  13342. # scope module: "admin" do
  13343. # resources :posts
  13344. # end
  13345. #
  13346. # or, for a single case
  13347. #
  13348. # resources :posts, module: "admin"
  13349. #
  13350. # If you want to route /admin/posts to +PostsController+
  13351. # (without the Admin:: module prefix), you could use
  13352. #
  13353. # scope "/admin" do
  13354. # resources :posts
  13355. # end
  13356. #
  13357. # or, for a single case
  13358. #
  13359. # resources :posts, path: "/admin/posts"
  13360. #
  13361. # In each of these cases, the named routes remain the same as if you did
  13362. # not use scope. In the last case, the following paths map to
  13363. # +PostsController+:
  13364. #
  13365. # GET /admin/posts
  13366. # GET /admin/posts/new
  13367. # POST /admin/posts
  13368. # GET /admin/posts/1
  13369. # GET /admin/posts/1/edit
  13370. # PATCH/PUT /admin/posts/1
  13371. # DELETE /admin/posts/1
  13372. module Scoping
  13373. # Scopes a set of routes to the given default options.
  13374. #
  13375. # Take the following route definition as an example:
  13376. #
  13377. # scope path: ":account_id", as: "account" do
  13378. # resources :projects
  13379. # end
  13380. #
  13381. # This generates helpers such as +account_projects_path+, just like +resources+ does.
  13382. # The difference here being that the routes generated are like /:account_id/projects,
  13383. # rather than /accounts/:account_id/projects.
  13384. #
  13385. # === Options
  13386. #
  13387. # Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
  13388. #
  13389. # # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt>
  13390. # scope module: "admin" do
  13391. # resources :posts
  13392. # end
  13393. #
  13394. # # prefix the posts resource's requests with '/admin'
  13395. # scope path: "/admin" do
  13396. # resources :posts
  13397. # end
  13398. #
  13399. # # prefix the routing helper name: +sekret_posts_path+ instead of +posts_path+
  13400. # scope as: "sekret" do
  13401. # resources :posts
  13402. # end
  13403. def scope(*args)
  13404. options = args.extract_options!.dup
  13405. recover = {}
  13406. options[:path] = args.flatten.join('/') if args.any?
  13407. options[:constraints] ||= {}
  13408. if options[:constraints].is_a?(Hash)
  13409. defaults = options[:constraints].select do
  13410. |k, v| URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum))
  13411. end
  13412. (options[:defaults] ||= {}).reverse_merge!(defaults)
  13413. else
  13414. block, options[:constraints] = options[:constraints], {}
  13415. end
  13416. scope_options.each do |option|
  13417. if value = options.delete(option)
  13418. recover[option] = @scope[option]
  13419. @scope[option] = send("merge_#{option}_scope", @scope[option], value)
  13420. end
  13421. end
  13422. recover[:blocks] = @scope[:blocks]
  13423. @scope[:blocks] = merge_blocks_scope(@scope[:blocks], block)
  13424. recover[:options] = @scope[:options]
  13425. @scope[:options] = merge_options_scope(@scope[:options], options)
  13426. yield
  13427. self
  13428. ensure
  13429. @scope.merge!(recover)
  13430. end
  13431. # Scopes routes to a specific controller
  13432. #
  13433. # controller "food" do
  13434. # match "bacon", action: "bacon"
  13435. # end
  13436. def controller(controller, options={})
  13437. options[:controller] = controller
  13438. scope(options) { yield }
  13439. end
  13440. # Scopes routes to a specific namespace. For example:
  13441. #
  13442. # namespace :admin do
  13443. # resources :posts
  13444. # end
  13445. #
  13446. # This generates the following routes:
  13447. #
  13448. # admin_posts GET /admin/posts(.:format) admin/posts#index
  13449. # admin_posts POST /admin/posts(.:format) admin/posts#create
  13450. # new_admin_post GET /admin/posts/new(.:format) admin/posts#new
  13451. # edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit
  13452. # admin_post GET /admin/posts/:id(.:format) admin/posts#show
  13453. # admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update
  13454. # admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy
  13455. #
  13456. # === Options
  13457. #
  13458. # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
  13459. # options all default to the name of the namespace.
  13460. #
  13461. # For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
  13462. # <tt>Resources#resources</tt>.
  13463. #
  13464. # # accessible through /sekret/posts rather than /admin/posts
  13465. # namespace :admin, path: "sekret" do
  13466. # resources :posts
  13467. # end
  13468. #
  13469. # # maps to <tt>Sekret::PostsController</tt> rather than <tt>Admin::PostsController</tt>
  13470. # namespace :admin, module: "sekret" do
  13471. # resources :posts
  13472. # end
  13473. #
  13474. # # generates +sekret_posts_path+ rather than +admin_posts_path+
  13475. # namespace :admin, as: "sekret" do
  13476. # resources :posts
  13477. # end
  13478. def namespace(path, options = {})
  13479. path = path.to_s
  13480. options = { :path => path, :as => path, :module => path,
  13481. :shallow_path => path, :shallow_prefix => path }.merge!(options)
  13482. scope(options) { yield }
  13483. end
  13484. # === Parameter Restriction
  13485. # Allows you to constrain the nested routes based on a set of rules.
  13486. # For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
  13487. #
  13488. # constraints(id: /\d+\.\d+/) do
  13489. # resources :posts
  13490. # end
  13491. #
  13492. # Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
  13493. # The +id+ parameter must match the constraint passed in for this example.
  13494. #
  13495. # You may use this to also restrict other parameters:
  13496. #
  13497. # resources :posts do
  13498. # constraints(post_id: /\d+\.\d+/) do
  13499. # resources :comments
  13500. # end
  13501. # end
  13502. #
  13503. # === Restricting based on IP
  13504. #
  13505. # Routes can also be constrained to an IP or a certain range of IP addresses:
  13506. #
  13507. # constraints(ip: /192\.168\.\d+\.\d+/) do
  13508. # resources :posts
  13509. # end
  13510. #
  13511. # Any user connecting from the 192.168.* range will be able to see this resource,
  13512. # where as any user connecting outside of this range will be told there is no such route.
  13513. #
  13514. # === Dynamic request matching
  13515. #
  13516. # Requests to routes can be constrained based on specific criteria:
  13517. #
  13518. # constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
  13519. # resources :iphones
  13520. # end
  13521. #
  13522. # You are able to move this logic out into a class if it is too complex for routes.
  13523. # This class must have a +matches?+ method defined on it which either returns +true+
  13524. # if the user should be given access to that route, or +false+ if the user should not.
  13525. #
  13526. # class Iphone
  13527. # def self.matches?(request)
  13528. # request.env["HTTP_USER_AGENT"] =~ /iPhone/
  13529. # end
  13530. # end
  13531. #
  13532. # An expected place for this code would be +lib/constraints+.
  13533. #
  13534. # This class is then used like this:
  13535. #
  13536. # constraints(Iphone) do
  13537. # resources :iphones
  13538. # end
  13539. def constraints(constraints = {})
  13540. scope(:constraints => constraints) { yield }
  13541. end
  13542. # Allows you to set default parameters for a route, such as this:
  13543. # defaults id: 'home' do
  13544. # match 'scoped_pages/(:id)', to: 'pages#show'
  13545. # end
  13546. # Using this, the +:id+ parameter here will default to 'home'.
  13547. def defaults(defaults = {})
  13548. scope(:defaults => defaults) { yield }
  13549. end
  13550. private
  13551. def scope_options #:nodoc:
  13552. @scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
  13553. end
  13554. def merge_path_scope(parent, child) #:nodoc:
  13555. Mapper.normalize_path("#{parent}/#{child}")
  13556. end
  13557. def merge_shallow_path_scope(parent, child) #:nodoc:
  13558. Mapper.normalize_path("#{parent}/#{child}")
  13559. end
  13560. def merge_as_scope(parent, child) #:nodoc:
  13561. parent ? "#{parent}_#{child}" : child
  13562. end
  13563. def merge_shallow_prefix_scope(parent, child) #:nodoc:
  13564. parent ? "#{parent}_#{child}" : child
  13565. end
  13566. def merge_module_scope(parent, child) #:nodoc:
  13567. parent ? "#{parent}/#{child}" : child
  13568. end
  13569. def merge_controller_scope(parent, child) #:nodoc:
  13570. child
  13571. end
  13572. def merge_path_names_scope(parent, child) #:nodoc:
  13573. merge_options_scope(parent, child)
  13574. end
  13575. def merge_constraints_scope(parent, child) #:nodoc:
  13576. merge_options_scope(parent, child)
  13577. end
  13578. def merge_defaults_scope(parent, child) #:nodoc:
  13579. merge_options_scope(parent, child)
  13580. end
  13581. def merge_blocks_scope(parent, child) #:nodoc:
  13582. merged = parent ? parent.dup : []
  13583. merged << child if child
  13584. merged
  13585. end
  13586. def merge_options_scope(parent, child) #:nodoc:
  13587. (parent || {}).except(*override_keys(child)).merge!(child)
  13588. end
  13589. def merge_shallow_scope(parent, child) #:nodoc:
  13590. child ? true : false
  13591. end
  13592. def override_keys(child) #:nodoc:
  13593. child.key?(:only) || child.key?(:except) ? [:only, :except] : []
  13594. end
  13595. end
  13596. # Resource routing allows you to quickly declare all of the common routes
  13597. # for a given resourceful controller. Instead of declaring separate routes
  13598. # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
  13599. # actions, a resourceful route declares them in a single line of code:
  13600. #
  13601. # resources :photos
  13602. #
  13603. # Sometimes, you have a resource that clients always look up without
  13604. # referencing an ID. A common example, /profile always shows the profile of
  13605. # the currently logged in user. In this case, you can use a singular resource
  13606. # to map /profile (rather than /profile/:id) to the show action.
  13607. #
  13608. # resource :profile
  13609. #
  13610. # It's common to have resources that are logically children of other
  13611. # resources:
  13612. #
  13613. # resources :magazines do
  13614. # resources :ads
  13615. # end
  13616. #
  13617. # You may wish to organize groups of controllers under a namespace. Most
  13618. # commonly, you might group a number of administrative controllers under
  13619. # an +admin+ namespace. You would place these controllers under the
  13620. # <tt>app/controllers/admin</tt> directory, and you can group them together
  13621. # in your router:
  13622. #
  13623. # namespace "admin" do
  13624. # resources :posts, :comments
  13625. # end
  13626. #
  13627. # By default the +:id+ parameter doesn't accept dots. If you need to
  13628. # use dots as part of the +:id+ parameter add a constraint which
  13629. # overrides this restriction, e.g:
  13630. #
  13631. # resources :articles, id: /[^\/]+/
  13632. #
  13633. # This allows any character other than a slash as part of your +:id+.
  13634. #
  13635. module Resources
  13636. # CANONICAL_ACTIONS holds all actions that does not need a prefix or
  13637. # a path appended since they fit properly in their scope level.
  13638. VALID_ON_OPTIONS = [:new, :collection, :member]
  13639. RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
  13640. CANONICAL_ACTIONS = %w(index create new show update destroy)
  13641. class Resource #:nodoc:
  13642. attr_reader :controller, :path, :options, :param
  13643. def initialize(entities, options = {})
  13644. @name = entities.to_s
  13645. @path = (options[:path] || @name).to_s
  13646. @controller = (options[:controller] || @name).to_s
  13647. @as = options[:as]
  13648. @param = (options[:param] || :id).to_sym
  13649. @options = options
  13650. end
  13651. def default_actions
  13652. [:index, :create, :new, :show, :update, :destroy, :edit]
  13653. end
  13654. def actions
  13655. if only = @options[:only]
  13656. Array(only).map(&:to_sym)
  13657. elsif except = @options[:except]
  13658. default_actions - Array(except).map(&:to_sym)
  13659. else
  13660. default_actions
  13661. end
  13662. end
  13663. def name
  13664. @as || @name
  13665. end
  13666. def plural
  13667. @plural ||= name.to_s
  13668. end
  13669. def singular
  13670. @singular ||= name.to_s.singularize
  13671. end
  13672. alias :member_name :singular
  13673. # Checks for uncountable plurals, and appends "_index" if the plural
  13674. # and singular form are the same.
  13675. def collection_name
  13676. singular == plural ? "#{plural}_index" : plural
  13677. end
  13678. def resource_scope
  13679. { :controller => controller }
  13680. end
  13681. alias :collection_scope :path
  13682. def member_scope
  13683. "#{path}/:#{param}"
  13684. end
  13685. alias :shallow_scope :member_scope
  13686. def new_scope(new_path)
  13687. "#{path}/#{new_path}"
  13688. end
  13689. def nested_param
  13690. :"#{singular}_#{param}"
  13691. end
  13692. def nested_scope
  13693. "#{path}/:#{nested_param}"
  13694. end
  13695. end
  13696. class SingletonResource < Resource #:nodoc:
  13697. def initialize(entities, options)
  13698. super
  13699. @as = nil
  13700. @controller = (options[:controller] || plural).to_s
  13701. @as = options[:as]
  13702. end
  13703. def default_actions
  13704. [:show, :create, :update, :destroy, :new, :edit]
  13705. end
  13706. def plural
  13707. @plural ||= name.to_s.pluralize
  13708. end
  13709. def singular
  13710. @singular ||= name.to_s
  13711. end
  13712. alias :member_name :singular
  13713. alias :collection_name :singular
  13714. alias :member_scope :path
  13715. alias :nested_scope :path
  13716. end
  13717. def resources_path_names(options)
  13718. @scope[:path_names].merge!(options)
  13719. end
  13720. # Sometimes, you have a resource that clients always look up without
  13721. # referencing an ID. A common example, /profile always shows the
  13722. # profile of the currently logged in user. In this case, you can use
  13723. # a singular resource to map /profile (rather than /profile/:id) to
  13724. # the show action:
  13725. #
  13726. # resource :geocoder
  13727. #
  13728. # creates six different routes in your application, all mapping to
  13729. # the +GeoCoders+ controller (note that the controller is named after
  13730. # the plural):
  13731. #
  13732. # GET /geocoder/new
  13733. # POST /geocoder
  13734. # GET /geocoder
  13735. # GET /geocoder/edit
  13736. # PATCH/PUT /geocoder
  13737. # DELETE /geocoder
  13738. #
  13739. # === Options
  13740. # Takes same options as +resources+.
  13741. def resource(*resources, &block)
  13742. options = resources.extract_options!.dup
  13743. if apply_common_behavior_for(:resource, resources, options, &block)
  13744. return self
  13745. end
  13746. resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
  13747. yield if block_given?
  13748. concerns(options[:concerns]) if options[:concerns]
  13749. collection do
  13750. post :create
  13751. end if parent_resource.actions.include?(:create)
  13752. new do
  13753. get :new
  13754. end if parent_resource.actions.include?(:new)
  13755. set_member_mappings_for_resource
  13756. end
  13757. self
  13758. end
  13759. # In Rails, a resourceful route provides a mapping between HTTP verbs
  13760. # and URLs and controller actions. By convention, each action also maps
  13761. # to particular CRUD operations in a database. A single entry in the
  13762. # routing file, such as
  13763. #
  13764. # resources :photos
  13765. #
  13766. # creates seven different routes in your application, all mapping to
  13767. # the +Photos+ controller:
  13768. #
  13769. # GET /photos
  13770. # GET /photos/new
  13771. # POST /photos
  13772. # GET /photos/:id
  13773. # GET /photos/:id/edit
  13774. # PATCH/PUT /photos/:id
  13775. # DELETE /photos/:id
  13776. #
  13777. # Resources can also be nested infinitely by using this block syntax:
  13778. #
  13779. # resources :photos do
  13780. # resources :comments
  13781. # end
  13782. #
  13783. # This generates the following comments routes:
  13784. #
  13785. # GET /photos/:photo_id/comments
  13786. # GET /photos/:photo_id/comments/new
  13787. # POST /photos/:photo_id/comments
  13788. # GET /photos/:photo_id/comments/:id
  13789. # GET /photos/:photo_id/comments/:id/edit
  13790. # PATCH/PUT /photos/:photo_id/comments/:id
  13791. # DELETE /photos/:photo_id/comments/:id
  13792. #
  13793. # === Options
  13794. # Takes same options as <tt>Base#match</tt> as well as:
  13795. #
  13796. # [:path_names]
  13797. # Allows you to change the segment component of the +edit+ and +new+ actions.
  13798. # Actions not specified are not changed.
  13799. #
  13800. # resources :posts, path_names: { new: "brand_new" }
  13801. #
  13802. # The above example will now change /posts/new to /posts/brand_new
  13803. #
  13804. # [:path]
  13805. # Allows you to change the path prefix for the resource.
  13806. #
  13807. # resources :posts, path: 'postings'
  13808. #
  13809. # The resource and all segments will now route to /postings instead of /posts
  13810. #
  13811. # [:only]
  13812. # Only generate routes for the given actions.
  13813. #
  13814. # resources :cows, only: :show
  13815. # resources :cows, only: [:show, :index]
  13816. #
  13817. # [:except]
  13818. # Generate all routes except for the given actions.
  13819. #
  13820. # resources :cows, except: :show
  13821. # resources :cows, except: [:show, :index]
  13822. #
  13823. # [:shallow]
  13824. # Generates shallow routes for nested resource(s). When placed on a parent resource,
  13825. # generates shallow routes for all nested resources.
  13826. #
  13827. # resources :posts, shallow: true do
  13828. # resources :comments
  13829. # end
  13830. #
  13831. # Is the same as:
  13832. #
  13833. # resources :posts do
  13834. # resources :comments, except: [:show, :edit, :update, :destroy]
  13835. # end
  13836. # resources :comments, only: [:show, :edit, :update, :destroy]
  13837. #
  13838. # This allows URLs for resources that otherwise would be deeply nested such
  13839. # as a comment on a blog post like <tt>/posts/a-long-permalink/comments/1234</tt>
  13840. # to be shortened to just <tt>/comments/1234</tt>.
  13841. #
  13842. # [:shallow_path]
  13843. # Prefixes nested shallow routes with the specified path.
  13844. #
  13845. # scope shallow_path: "sekret" do
  13846. # resources :posts do
  13847. # resources :comments, shallow: true
  13848. # end
  13849. # end
  13850. #
  13851. # The +comments+ resource here will have the following routes generated for it:
  13852. #
  13853. # post_comments GET /posts/:post_id/comments(.:format)
  13854. # post_comments POST /posts/:post_id/comments(.:format)
  13855. # new_post_comment GET /posts/:post_id/comments/new(.:format)
  13856. # edit_comment GET /sekret/comments/:id/edit(.:format)
  13857. # comment GET /sekret/comments/:id(.:format)
  13858. # comment PATCH/PUT /sekret/comments/:id(.:format)
  13859. # comment DELETE /sekret/comments/:id(.:format)
  13860. #
  13861. # [:shallow_prefix]
  13862. # Prefixes nested shallow route names with specified prefix.
  13863. #
  13864. # scope shallow_prefix: "sekret" do
  13865. # resources :posts do
  13866. # resources :comments, shallow: true
  13867. # end
  13868. # end
  13869. #
  13870. # The +comments+ resource here will have the following routes generated for it:
  13871. #
  13872. # post_comments GET /posts/:post_id/comments(.:format)
  13873. # post_comments POST /posts/:post_id/comments(.:format)
  13874. # new_post_comment GET /posts/:post_id/comments/new(.:format)
  13875. # edit_sekret_comment GET /comments/:id/edit(.:format)
  13876. # sekret_comment GET /comments/:id(.:format)
  13877. # sekret_comment PATCH/PUT /comments/:id(.:format)
  13878. # sekret_comment DELETE /comments/:id(.:format)
  13879. #
  13880. # [:format]
  13881. # Allows you to specify the default value for optional +format+
  13882. # segment or disable it by supplying +false+.
  13883. #
  13884. # === Examples
  13885. #
  13886. # # routes call <tt>Admin::PostsController</tt>
  13887. # resources :posts, module: "admin"
  13888. #
  13889. # # resource actions are at /admin/posts.
  13890. # resources :posts, path: "admin/posts"
  13891. def resources(*resources, &block)
  13892. options = resources.extract_options!.dup
  13893. if apply_common_behavior_for(:resources, resources, options, &block)
  13894. return self
  13895. end
  13896. resource_scope(:resources, Resource.new(resources.pop, options)) do
  13897. yield if block_given?
  13898. concerns(options[:concerns]) if options[:concerns]
  13899. collection do
  13900. get :index if parent_resource.actions.include?(:index)
  13901. post :create if parent_resource.actions.include?(:create)
  13902. end
  13903. new do
  13904. get :new
  13905. end if parent_resource.actions.include?(:new)
  13906. set_member_mappings_for_resource
  13907. end
  13908. self
  13909. end
  13910. # To add a route to the collection:
  13911. #
  13912. # resources :photos do
  13913. # collection do
  13914. # get 'search'
  13915. # end
  13916. # end
  13917. #
  13918. # This will enable Rails to recognize paths such as <tt>/photos/search</tt>
  13919. # with GET, and route to the search action of +PhotosController+. It will also
  13920. # create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
  13921. # route helpers.
  13922. def collection
  13923. unless resource_scope?
  13924. raise ArgumentError, "can't use collection outside resource(s) scope"
  13925. end
  13926. with_scope_level(:collection) do
  13927. scope(parent_resource.collection_scope) do
  13928. yield
  13929. end
  13930. end
  13931. end
  13932. # To add a member route, add a member block into the resource block:
  13933. #
  13934. # resources :photos do
  13935. # member do
  13936. # get 'preview'
  13937. # end
  13938. # end
  13939. #
  13940. # This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
  13941. # preview action of +PhotosController+. It will also create the
  13942. # <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
  13943. def member
  13944. unless resource_scope?
  13945. raise ArgumentError, "can't use member outside resource(s) scope"
  13946. end
  13947. with_scope_level(:member) do
  13948. scope(parent_resource.member_scope) do
  13949. yield
  13950. end
  13951. end
  13952. end
  13953. def new
  13954. unless resource_scope?
  13955. raise ArgumentError, "can't use new outside resource(s) scope"
  13956. end
  13957. with_scope_level(:new) do
  13958. scope(parent_resource.new_scope(action_path(:new))) do
  13959. yield
  13960. end
  13961. end
  13962. end
  13963. def nested
  13964. unless resource_scope?
  13965. raise ArgumentError, "can't use nested outside resource(s) scope"
  13966. end
  13967. with_scope_level(:nested) do
  13968. if shallow?
  13969. with_exclusive_scope do
  13970. if @scope[:shallow_path].blank?
  13971. scope(parent_resource.nested_scope, nested_options) { yield }
  13972. else
  13973. scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do
  13974. scope(parent_resource.nested_scope, nested_options) { yield }
  13975. end
  13976. end
  13977. end
  13978. else
  13979. scope(parent_resource.nested_scope, nested_options) { yield }
  13980. end
  13981. end
  13982. end
  13983. # See ActionDispatch::Routing::Mapper::Scoping#namespace
  13984. def namespace(path, options = {})
  13985. if resource_scope?
  13986. nested { super }
  13987. else
  13988. super
  13989. end
  13990. end
  13991. def shallow
  13992. scope(:shallow => true, :shallow_path => @scope[:path]) do
  13993. yield
  13994. end
  13995. end
  13996. def shallow?
  13997. parent_resource.instance_of?(Resource) && @scope[:shallow]
  13998. end
  13999. # match 'path' => 'controller#action'
  14000. # match 'path', to: 'controller#action'
  14001. # match 'path', 'otherpath', on: :member, via: :get
  14002. def match(path, *rest)
  14003. if rest.empty? && Hash === path
  14004. options = path
  14005. path, to = options.find { |name, value| name.is_a?(String) }
  14006. options[:to] = to
  14007. options.delete(path)
  14008. paths = [path]
  14009. else
  14010. options = rest.pop || {}
  14011. paths = [path] + rest
  14012. end
  14013. options[:anchor] = true unless options.key?(:anchor)
  14014. if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
  14015. raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
  14016. end
  14017. paths.each { |_path| decomposed_match(_path, options.dup) }
  14018. self
  14019. end
  14020. def decomposed_match(path, options) # :nodoc:
  14021. if on = options.delete(:on)
  14022. send(on) { decomposed_match(path, options) }
  14023. else
  14024. case @scope[:scope_level]
  14025. when :resources
  14026. nested { decomposed_match(path, options) }
  14027. when :resource
  14028. member { decomposed_match(path, options) }
  14029. else
  14030. add_route(path, options)
  14031. end
  14032. end
  14033. end
  14034. def add_route(action, options) # :nodoc:
  14035. path = path_for_action(action, options.delete(:path))
  14036. action = action.to_s.dup
  14037. if action =~ /^[\w\/]+$/
  14038. options[:action] ||= action unless action.include?("/")
  14039. else
  14040. action = nil
  14041. end
  14042. if !options.fetch(:as, true)
  14043. options.delete(:as)
  14044. else
  14045. options[:as] = name_for_action(options[:as], action)
  14046. end
  14047. mapping = Mapping.new(@set, @scope, URI.parser.escape(path), options)
  14048. app, conditions, requirements, defaults, as, anchor = mapping.to_route
  14049. @set.add_route(app, conditions, requirements, defaults, as, anchor)
  14050. end
  14051. def root(options={})
  14052. if @scope[:scope_level] == :resources
  14053. with_scope_level(:root) do
  14054. scope(parent_resource.path) do
  14055. super(options)
  14056. end
  14057. end
  14058. else
  14059. super(options)
  14060. end
  14061. end
  14062. protected
  14063. def parent_resource #:nodoc:
  14064. @scope[:scope_level_resource]
  14065. end
  14066. def apply_common_behavior_for(method, resources, options, &block) #:nodoc:
  14067. if resources.length > 1
  14068. resources.each { |r| send(method, r, options, &block) }
  14069. return true
  14070. end
  14071. if resource_scope?
  14072. nested { send(method, resources.pop, options, &block) }
  14073. return true
  14074. end
  14075. options.keys.each do |k|
  14076. (options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
  14077. end
  14078. scope_options = options.slice!(*RESOURCE_OPTIONS)
  14079. unless scope_options.empty?
  14080. scope(scope_options) do
  14081. send(method, resources.pop, options, &block)
  14082. end
  14083. return true
  14084. end
  14085. unless action_options?(options)
  14086. options.merge!(scope_action_options) if scope_action_options?
  14087. end
  14088. false
  14089. end
  14090. def action_options?(options) #:nodoc:
  14091. options[:only] || options[:except]
  14092. end
  14093. def scope_action_options? #:nodoc:
  14094. @scope[:options] && (@scope[:options][:only] || @scope[:options][:except])
  14095. end
  14096. def scope_action_options #:nodoc:
  14097. @scope[:options].slice(:only, :except)
  14098. end
  14099. def resource_scope? #:nodoc:
  14100. [:resource, :resources].include? @scope[:scope_level]
  14101. end
  14102. def resource_method_scope? #:nodoc:
  14103. [:collection, :member, :new].include? @scope[:scope_level]
  14104. end
  14105. def with_exclusive_scope
  14106. begin
  14107. old_name_prefix, old_path = @scope[:as], @scope[:path]
  14108. @scope[:as], @scope[:path] = nil, nil
  14109. with_scope_level(:exclusive) do
  14110. yield
  14111. end
  14112. ensure
  14113. @scope[:as], @scope[:path] = old_name_prefix, old_path
  14114. end
  14115. end
  14116. def with_scope_level(kind, resource = parent_resource)
  14117. old, @scope[:scope_level] = @scope[:scope_level], kind
  14118. old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource
  14119. yield
  14120. ensure
  14121. @scope[:scope_level] = old
  14122. @scope[:scope_level_resource] = old_resource
  14123. end
  14124. def resource_scope(kind, resource) #:nodoc:
  14125. with_scope_level(kind, resource) do
  14126. scope(parent_resource.resource_scope) do
  14127. yield
  14128. end
  14129. end
  14130. end
  14131. def nested_options #:nodoc:
  14132. options = { :as => parent_resource.member_name }
  14133. options[:constraints] = {
  14134. parent_resource.nested_param => param_constraint
  14135. } if param_constraint?
  14136. options
  14137. end
  14138. def param_constraint? #:nodoc:
  14139. @scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
  14140. end
  14141. def param_constraint #:nodoc:
  14142. @scope[:constraints][parent_resource.param]
  14143. end
  14144. def canonical_action?(action, flag) #:nodoc:
  14145. flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
  14146. end
  14147. def shallow_scoping? #:nodoc:
  14148. shallow? && @scope[:scope_level] == :member
  14149. end
  14150. def path_for_action(action, path) #:nodoc:
  14151. prefix = shallow_scoping? ?
  14152. "#{@scope[:shallow_path]}/#{parent_resource.shallow_scope}" : @scope[:path]
  14153. if canonical_action?(action, path.blank?)
  14154. prefix.to_s
  14155. else
  14156. "#{prefix}/#{action_path(action, path)}"
  14157. end
  14158. end
  14159. def action_path(name, path = nil) #:nodoc:
  14160. name = name.to_sym if name.is_a?(String)
  14161. path || @scope[:path_names][name] || name.to_s
  14162. end
  14163. def prefix_name_for_action(as, action) #:nodoc:
  14164. if as
  14165. as.to_s
  14166. elsif !canonical_action?(action, @scope[:scope_level])
  14167. action.to_s
  14168. end
  14169. end
  14170. def name_for_action(as, action) #:nodoc:
  14171. prefix = prefix_name_for_action(as, action)
  14172. prefix = Mapper.normalize_name(prefix) if prefix
  14173. name_prefix = @scope[:as]
  14174. if parent_resource
  14175. return nil unless as || action
  14176. collection_name = parent_resource.collection_name
  14177. member_name = parent_resource.member_name
  14178. end
  14179. name = case @scope[:scope_level]
  14180. when :nested
  14181. [name_prefix, prefix]
  14182. when :collection
  14183. [prefix, name_prefix, collection_name]
  14184. when :new
  14185. [prefix, :new, name_prefix, member_name]
  14186. when :member
  14187. [prefix, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name]
  14188. when :root
  14189. [name_prefix, collection_name, prefix]
  14190. else
  14191. [name_prefix, member_name, prefix]
  14192. end
  14193. if candidate = name.select(&:present?).join("_").presence
  14194. # If a name was not explicitly given, we check if it is valid
  14195. # and return nil in case it isn't. Otherwise, we pass the invalid name
  14196. # forward so the underlying router engine treats it and raises an exception.
  14197. if as.nil?
  14198. candidate unless @set.routes.find { |r| r.name == candidate } || candidate !~ /\A[_a-z]/i
  14199. else
  14200. candidate
  14201. end
  14202. end
  14203. end
  14204. def set_member_mappings_for_resource
  14205. member do
  14206. get :edit if parent_resource.actions.include?(:edit)
  14207. get :show if parent_resource.actions.include?(:show)
  14208. if parent_resource.actions.include?(:update)
  14209. patch :update
  14210. put :update
  14211. end
  14212. delete :destroy if parent_resource.actions.include?(:destroy)
  14213. end
  14214. end
  14215. end
  14216. # Routing Concerns allow you to declare common routes that can be reused
  14217. # inside others resources and routes.
  14218. #
  14219. # concern :commentable do
  14220. # resources :comments
  14221. # end
  14222. #
  14223. # concern :image_attachable do
  14224. # resources :images, only: :index
  14225. # end
  14226. #
  14227. # These concerns are used in Resources routing:
  14228. #
  14229. # resources :messages, concerns: [:commentable, :image_attachable]
  14230. #
  14231. # or in a scope or namespace:
  14232. #
  14233. # namespace :posts do
  14234. # concerns :commentable
  14235. # end
  14236. module Concerns
  14237. # Define a routing concern using a name.
  14238. #
  14239. # Concerns may be defined inline, using a block, or handled by
  14240. # another object, by passing that object as the second parameter.
  14241. #
  14242. # The concern object, if supplied, should respond to <tt>call</tt>,
  14243. # which will receive two parameters:
  14244. #
  14245. # * The current mapper
  14246. # * A hash of options which the concern object may use
  14247. #
  14248. # Options may also be used by concerns defined in a block by accepting
  14249. # a block parameter. So, using a block, you might do something as
  14250. # simple as limit the actions available on certain resources, passing
  14251. # standard resource options through the concern:
  14252. #
  14253. # concern :commentable do |options|
  14254. # resources :comments, options
  14255. # end
  14256. #
  14257. # resources :posts, concerns: :commentable
  14258. # resources :archived_posts do
  14259. # # Don't allow comments on archived posts
  14260. # concerns :commentable, only: [:index, :show]
  14261. # end
  14262. #
  14263. # Or, using a callable object, you might implement something more
  14264. # specific to your application, which would be out of place in your
  14265. # routes file.
  14266. #
  14267. # # purchasable.rb
  14268. # class Purchasable
  14269. # def initialize(defaults = {})
  14270. # @defaults = defaults
  14271. # end
  14272. #
  14273. # def call(mapper, options = {})
  14274. # options = @defaults.merge(options)
  14275. # mapper.resources :purchases
  14276. # mapper.resources :receipts
  14277. # mapper.resources :returns if options[:returnable]
  14278. # end
  14279. # end
  14280. #
  14281. # # routes.rb
  14282. # concern :purchasable, Purchasable.new(returnable: true)
  14283. #
  14284. # resources :toys, concerns: :purchasable
  14285. # resources :electronics, concerns: :purchasable
  14286. # resources :pets do
  14287. # concerns :purchasable, returnable: false
  14288. # end
  14289. #
  14290. # Any routing helpers can be used inside a concern. If using a
  14291. # callable, they're accessible from the Mapper that's passed to
  14292. # <tt>call</tt>.
  14293. def concern(name, callable = nil, &block)
  14294. callable ||= lambda { |mapper, options| mapper.instance_exec(options, &block) }
  14295. @concerns[name] = callable
  14296. end
  14297. # Use the named concerns
  14298. #
  14299. # resources :posts do
  14300. # concerns :commentable
  14301. # end
  14302. #
  14303. # concerns also work in any routes helper that you want to use:
  14304. #
  14305. # namespace :posts do
  14306. # concerns :commentable
  14307. # end
  14308. def concerns(*args)
  14309. options = args.extract_options!
  14310. args.flatten.each do |name|
  14311. if concern = @concerns[name]
  14312. concern.call(self, options)
  14313. else
  14314. raise ArgumentError, "No concern named #{name} was found!"
  14315. end
  14316. end
  14317. end
  14318. end
  14319. def initialize(set) #:nodoc:
  14320. @set = set
  14321. @scope = { :path_names => @set.resources_path_names }
  14322. @concerns = {}
  14323. end
  14324. include Base
  14325. include HttpHelpers
  14326. include Redirection
  14327. include Scoping
  14328. include Concerns
  14329. include Resources
  14330. end
  14331. end
  14332. end
  14333. require 'action_controller/model_naming'
  14334. module ActionDispatch
  14335. module Routing
  14336. # Polymorphic URL helpers are methods for smart resolution to a named route call when
  14337. # given an Active Record model instance. They are to be used in combination with
  14338. # ActionController::Resources.
  14339. #
  14340. # These methods are useful when you want to generate correct URL or path to a RESTful
  14341. # resource without having to know the exact type of the record in question.
  14342. #
  14343. # Nested resources and/or namespaces are also supported, as illustrated in the example:
  14344. #
  14345. # polymorphic_url([:admin, @article, @comment])
  14346. #
  14347. # results in:
  14348. #
  14349. # admin_article_comment_url(@article, @comment)
  14350. #
  14351. # == Usage within the framework
  14352. #
  14353. # Polymorphic URL helpers are used in a number of places throughout the \Rails framework:
  14354. #
  14355. # * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
  14356. # <tt>url_for(@article)</tt>;
  14357. # * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
  14358. # <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
  14359. # action;
  14360. # * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
  14361. # <tt>redirect_to(post)</tt> in your controllers;
  14362. # * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
  14363. # for feed entries.
  14364. #
  14365. # == Prefixed polymorphic helpers
  14366. #
  14367. # In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
  14368. # number of prefixed helpers are available as a shorthand to <tt>action: "..."</tt>
  14369. # in options. Those are:
  14370. #
  14371. # * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
  14372. # * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
  14373. #
  14374. # Example usage:
  14375. #
  14376. # edit_polymorphic_path(@post) # => "/posts/1/edit"
  14377. # polymorphic_path(@post, format: :pdf) # => "/posts/1.pdf"
  14378. #
  14379. # == Usage with mounted engines
  14380. #
  14381. # If you are using a mounted engine and you need to use a polymorphic_url
  14382. # pointing at the engine's routes, pass in the engine's route proxy as the first
  14383. # argument to the method. For example:
  14384. #
  14385. # polymorphic_url([blog, @post]) # calls blog.post_path(@post)
  14386. # form_for([blog, @post]) # => "/blog/posts/1"
  14387. #
  14388. module PolymorphicRoutes
  14389. include ActionController::ModelNaming
  14390. # Constructs a call to a named RESTful route for the given record and returns the
  14391. # resulting URL string. For example:
  14392. #
  14393. # # calls post_url(post)
  14394. # polymorphic_url(post) # => "http://example.com/posts/1"
  14395. # polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
  14396. # polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
  14397. # polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
  14398. # polymorphic_url(Comment) # => "http://example.com/comments"
  14399. #
  14400. # ==== Options
  14401. #
  14402. # * <tt>:action</tt> - Specifies the action prefix for the named route:
  14403. # <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
  14404. # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
  14405. # Default is <tt>:url</tt>.
  14406. #
  14407. # # an Article record
  14408. # polymorphic_url(record) # same as article_url(record)
  14409. #
  14410. # # a Comment record
  14411. # polymorphic_url(record) # same as comment_url(record)
  14412. #
  14413. # # it recognizes new records and maps to the collection
  14414. # record = Comment.new
  14415. # polymorphic_url(record) # same as comments_url()
  14416. #
  14417. # # the class of a record will also map to the collection
  14418. # polymorphic_url(Comment) # same as comments_url()
  14419. #
  14420. def polymorphic_url(record_or_hash_or_array, options = {})
  14421. if record_or_hash_or_array.kind_of?(Array)
  14422. record_or_hash_or_array = record_or_hash_or_array.compact
  14423. if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
  14424. proxy = record_or_hash_or_array.shift
  14425. end
  14426. record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
  14427. end
  14428. record = extract_record(record_or_hash_or_array)
  14429. record = convert_to_model(record)
  14430. args = Array === record_or_hash_or_array ?
  14431. record_or_hash_or_array.dup :
  14432. [ record_or_hash_or_array ]
  14433. inflection = if options[:action] && options[:action].to_s == "new"
  14434. args.pop
  14435. :singular
  14436. elsif (record.respond_to?(:persisted?) && !record.persisted?)
  14437. args.pop
  14438. :plural
  14439. elsif record.is_a?(Class)
  14440. args.pop
  14441. :plural
  14442. else
  14443. :singular
  14444. end
  14445. args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
  14446. named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
  14447. url_options = options.except(:action, :routing_type)
  14448. unless url_options.empty?
  14449. args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
  14450. end
  14451. args.collect! { |a| convert_to_model(a) }
  14452. (proxy || self).send(named_route, *args)
  14453. end
  14454. # Returns the path component of a URL for the given record. It uses
  14455. # <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
  14456. def polymorphic_path(record_or_hash_or_array, options = {})
  14457. polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
  14458. end
  14459. %w(edit new).each do |action|
  14460. module_eval <<-EOT, __FILE__, __LINE__ + 1
  14461. def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
  14462. polymorphic_url( # polymorphic_url(
  14463. record_or_hash, # record_or_hash,
  14464. options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
  14465. end # end
  14466. #
  14467. def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
  14468. polymorphic_url( # polymorphic_url(
  14469. record_or_hash, # record_or_hash,
  14470. options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
  14471. end # end
  14472. EOT
  14473. end
  14474. private
  14475. def action_prefix(options)
  14476. options[:action] ? "#{options[:action]}_" : ''
  14477. end
  14478. def routing_type(options)
  14479. options[:routing_type] || :url
  14480. end
  14481. def build_named_route_call(records, inflection, options = {})
  14482. if records.is_a?(Array)
  14483. record = records.pop
  14484. route = records.map do |parent|
  14485. if parent.is_a?(Symbol) || parent.is_a?(String)
  14486. parent
  14487. else
  14488. model_name_from_record_or_class(parent).singular_route_key
  14489. end
  14490. end
  14491. else
  14492. record = extract_record(records)
  14493. route = []
  14494. end
  14495. if record.is_a?(Symbol) || record.is_a?(String)
  14496. route << record
  14497. elsif record
  14498. if inflection == :singular
  14499. route << model_name_from_record_or_class(record).singular_route_key
  14500. else
  14501. route << model_name_from_record_or_class(record).route_key
  14502. end
  14503. else
  14504. raise ArgumentError, "Nil location provided. Can't build URI."
  14505. end
  14506. route << routing_type(options)
  14507. action_prefix(options) + route.join("_")
  14508. end
  14509. def extract_record(record_or_hash_or_array)
  14510. case record_or_hash_or_array
  14511. when Array; record_or_hash_or_array.last
  14512. when Hash; record_or_hash_or_array[:id]
  14513. else record_or_hash_or_array
  14514. end
  14515. end
  14516. end
  14517. end
  14518. end
  14519. require 'action_dispatch/http/request'
  14520. require 'active_support/core_ext/uri'
  14521. require 'active_support/core_ext/array/extract_options'
  14522. require 'rack/utils'
  14523. require 'action_controller/metal/exceptions'
  14524. module ActionDispatch
  14525. module Routing
  14526. class Redirect # :nodoc:
  14527. attr_reader :status, :block
  14528. def initialize(status, block)
  14529. @status = status
  14530. @block = block
  14531. end
  14532. def call(env)
  14533. req = Request.new(env)
  14534. # If any of the path parameters has a invalid encoding then
  14535. # raise since it's likely to trigger errors further on.
  14536. req.symbolized_path_parameters.each do |key, value|
  14537. unless value.valid_encoding?
  14538. raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
  14539. end
  14540. end
  14541. uri = URI.parse(path(req.symbolized_path_parameters, req))
  14542. uri.scheme ||= req.scheme
  14543. uri.host ||= req.host
  14544. uri.port ||= req.port unless req.standard_port?
  14545. body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
  14546. headers = {
  14547. 'Location' => uri.to_s,
  14548. 'Content-Type' => 'text/html',
  14549. 'Content-Length' => body.length.to_s
  14550. }
  14551. [ status, headers, [body] ]
  14552. end
  14553. def path(params, request)
  14554. block.call params, request
  14555. end
  14556. def inspect
  14557. "redirect(#{status})"
  14558. end
  14559. end
  14560. class PathRedirect < Redirect
  14561. def path(params, request)
  14562. (params.empty? || !block.match(/%\{\w*\}/)) ? block : (block % escape(params))
  14563. end
  14564. def inspect
  14565. "redirect(#{status}, #{block})"
  14566. end
  14567. private
  14568. def escape(params)
  14569. Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
  14570. end
  14571. end
  14572. class OptionRedirect < Redirect # :nodoc:
  14573. alias :options :block
  14574. def path(params, request)
  14575. url_options = {
  14576. :protocol => request.protocol,
  14577. :host => request.host,
  14578. :port => request.optional_port,
  14579. :path => request.path,
  14580. :params => request.query_parameters
  14581. }.merge! options
  14582. if !params.empty? && url_options[:path].match(/%\{\w*\}/)
  14583. url_options[:path] = (url_options[:path] % escape_path(params))
  14584. end
  14585. ActionDispatch::Http::URL.url_for url_options
  14586. end
  14587. def inspect
  14588. "redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})"
  14589. end
  14590. private
  14591. def escape_path(params)
  14592. Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }]
  14593. end
  14594. end
  14595. module Redirection
  14596. # Redirect any path to another path:
  14597. #
  14598. # get "/stories" => redirect("/posts")
  14599. #
  14600. # You can also use interpolation in the supplied redirect argument:
  14601. #
  14602. # get 'docs/:article', to: redirect('/wiki/%{article}')
  14603. #
  14604. # Alternatively you can use one of the other syntaxes:
  14605. #
  14606. # The block version of redirect allows for the easy encapsulation of any logic associated with
  14607. # the redirect in question. Either the params and request are supplied as arguments, or just
  14608. # params, depending of how many arguments your block accepts. A string is required as a
  14609. # return value.
  14610. #
  14611. # get 'jokes/:number', to: redirect { |params, request|
  14612. # path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
  14613. # "http://#{request.host_with_port}/#{path}"
  14614. # }
  14615. #
  14616. # Note that the +do end+ syntax for the redirect block wouldn't work, as Ruby would pass
  14617. # the block to +get+ instead of +redirect+. Use <tt>{ ... }</tt> instead.
  14618. #
  14619. # The options version of redirect allows you to supply only the parts of the url which need
  14620. # to change, it also supports interpolation of the path similar to the first example.
  14621. #
  14622. # get 'stores/:name', to: redirect(subdomain: 'stores', path: '/%{name}')
  14623. # get 'stores/:name(*all)', to: redirect(subdomain: 'stores', path: '/%{name}%{all}')
  14624. #
  14625. # Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
  14626. # common redirect routes. The call method must accept two arguments, params and request, and return
  14627. # a string.
  14628. #
  14629. # get 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
  14630. #
  14631. def redirect(*args, &block)
  14632. options = args.extract_options!
  14633. status = options.delete(:status) || 301
  14634. path = args.shift
  14635. return OptionRedirect.new(status, options) if options.any?
  14636. return PathRedirect.new(status, path) if String === path
  14637. block = path if path.respond_to? :call
  14638. raise ArgumentError, "redirection argument not supported" unless block
  14639. Redirect.new status, block
  14640. end
  14641. end
  14642. end
  14643. end
  14644. require 'action_dispatch/journey'
  14645. require 'forwardable'
  14646. require 'thread_safe'
  14647. require 'active_support/core_ext/object/to_query'
  14648. require 'active_support/core_ext/hash/slice'
  14649. require 'active_support/core_ext/module/remove_method'
  14650. require 'active_support/core_ext/array/extract_options'
  14651. require 'action_controller/metal/exceptions'
  14652. module ActionDispatch
  14653. module Routing
  14654. class RouteSet #:nodoc:
  14655. # Since the router holds references to many parts of the system
  14656. # like engines, controllers and the application itself, inspecting
  14657. # the route set can actually be really slow, therefore we default
  14658. # alias inspect to to_s.
  14659. alias inspect to_s
  14660. PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
  14661. class Dispatcher #:nodoc:
  14662. def initialize(options={})
  14663. @defaults = options[:defaults]
  14664. @glob_param = options.delete(:glob)
  14665. @controller_class_names = ThreadSafe::Cache.new
  14666. end
  14667. def call(env)
  14668. params = env[PARAMETERS_KEY]
  14669. # If any of the path parameters has a invalid encoding then
  14670. # raise since it's likely to trigger errors further on.
  14671. params.each do |key, value|
  14672. unless value.valid_encoding?
  14673. raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
  14674. end
  14675. end
  14676. prepare_params!(params)
  14677. # Just raise undefined constant errors if a controller was specified as default.
  14678. unless controller = controller(params, @defaults.key?(:controller))
  14679. return [404, {'X-Cascade' => 'pass'}, []]
  14680. end
  14681. dispatch(controller, params[:action], env)
  14682. end
  14683. def prepare_params!(params)
  14684. normalize_controller!(params)
  14685. merge_default_action!(params)
  14686. split_glob_param!(params) if @glob_param
  14687. end
  14688. # If this is a default_controller (i.e. a controller specified by the user)
  14689. # we should raise an error in case it's not found, because it usually means
  14690. # a user error. However, if the controller was retrieved through a dynamic
  14691. # segment, as in :controller(/:action), we should simply return nil and
  14692. # delegate the control back to Rack cascade. Besides, if this is not a default
  14693. # controller, it means we should respect the @scope[:module] parameter.
  14694. def controller(params, default_controller=true)
  14695. if params && params.key?(:controller)
  14696. controller_param = params[:controller]
  14697. controller_reference(controller_param)
  14698. end
  14699. rescue NameError => e
  14700. raise ActionController::RoutingError, e.message, e.backtrace if default_controller
  14701. end
  14702. private
  14703. def controller_reference(controller_param)
  14704. const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
  14705. ActiveSupport::Dependencies.constantize(const_name)
  14706. end
  14707. def dispatch(controller, action, env)
  14708. controller.action(action).call(env)
  14709. end
  14710. def normalize_controller!(params)
  14711. params[:controller] = params[:controller].underscore if params.key?(:controller)
  14712. end
  14713. def merge_default_action!(params)
  14714. params[:action] ||= 'index'
  14715. end
  14716. def split_glob_param!(params)
  14717. params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
  14718. end
  14719. end
  14720. # A NamedRouteCollection instance is a collection of named routes, and also
  14721. # maintains an anonymous module that can be used to install helpers for the
  14722. # named routes.
  14723. class NamedRouteCollection #:nodoc:
  14724. include Enumerable
  14725. attr_reader :routes, :helpers, :module
  14726. def initialize
  14727. @routes = {}
  14728. @helpers = []
  14729. @module = Module.new
  14730. end
  14731. def helper_names
  14732. @helpers.map(&:to_s)
  14733. end
  14734. def clear!
  14735. @helpers.each do |helper|
  14736. @module.remove_possible_method helper
  14737. end
  14738. @routes.clear
  14739. @helpers.clear
  14740. end
  14741. def add(name, route)
  14742. routes[name.to_sym] = route
  14743. define_named_route_methods(name, route)
  14744. end
  14745. def get(name)
  14746. routes[name.to_sym]
  14747. end
  14748. alias []= add
  14749. alias [] get
  14750. alias clear clear!
  14751. def each
  14752. routes.each { |name, route| yield name, route }
  14753. self
  14754. end
  14755. def names
  14756. routes.keys
  14757. end
  14758. def length
  14759. routes.length
  14760. end
  14761. class UrlHelper # :nodoc:
  14762. def self.create(route, options)
  14763. if optimize_helper?(route)
  14764. OptimizedUrlHelper.new(route, options)
  14765. else
  14766. new route, options
  14767. end
  14768. end
  14769. def self.optimize_helper?(route)
  14770. route.requirements.except(:controller, :action).empty?
  14771. end
  14772. class OptimizedUrlHelper < UrlHelper # :nodoc:
  14773. attr_reader :arg_size
  14774. def initialize(route, options)
  14775. super
  14776. @path_parts = @route.required_parts
  14777. @arg_size = @path_parts.size
  14778. @string_route = string_route(route)
  14779. end
  14780. def call(t, args)
  14781. if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t)
  14782. @options.merge!(t.url_options) if t.respond_to?(:url_options)
  14783. @options[:path] = optimized_helper(args)
  14784. ActionDispatch::Http::URL.url_for(@options)
  14785. else
  14786. super
  14787. end
  14788. end
  14789. private
  14790. def string_route(route)
  14791. string_route = route.ast.to_s.dup
  14792. while string_route.gsub!(/\([^\)]*\)/, "")
  14793. true
  14794. end
  14795. string_route
  14796. end
  14797. def optimized_helper(args)
  14798. path = @string_route.dup
  14799. klass = Journey::Router::Utils
  14800. @path_parts.zip(args) do |part, arg|
  14801. # Replace each route parameter
  14802. # e.g. :id for regular parameter or *path for globbing
  14803. # with ruby string interpolation code
  14804. path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(arg.to_param))
  14805. end
  14806. path
  14807. end
  14808. def optimize_routes_generation?(t)
  14809. t.send(:optimize_routes_generation?)
  14810. end
  14811. end
  14812. def initialize(route, options)
  14813. @options = options
  14814. @segment_keys = route.segment_keys
  14815. @route = route
  14816. end
  14817. def call(t, args)
  14818. t.url_for(handle_positional_args(t, args, @options, @segment_keys))
  14819. end
  14820. def handle_positional_args(t, args, options, keys)
  14821. inner_options = args.extract_options!
  14822. result = options.dup
  14823. if args.size > 0
  14824. if args.size < keys.size - 1 # take format into account
  14825. keys -= t.url_options.keys if t.respond_to?(:url_options)
  14826. keys -= options.keys
  14827. end
  14828. result.merge!(Hash[keys.zip(args)])
  14829. end
  14830. result.merge!(inner_options)
  14831. end
  14832. end
  14833. private
  14834. # Create a url helper allowing ordered parameters to be associated
  14835. # with corresponding dynamic segments, so you can do:
  14836. #
  14837. # foo_url(bar, baz, bang)
  14838. #
  14839. # Instead of:
  14840. #
  14841. # foo_url(bar: bar, baz: baz, bang: bang)
  14842. #
  14843. # Also allow options hash, so you can do:
  14844. #
  14845. # foo_url(bar, baz, bang, sort_by: 'baz')
  14846. #
  14847. def define_url_helper(route, name, options)
  14848. helper = UrlHelper.create(route, options.dup)
  14849. @module.remove_possible_method name
  14850. @module.module_eval do
  14851. define_method(name) do |*args|
  14852. helper.call self, args
  14853. end
  14854. end
  14855. helpers << name
  14856. end
  14857. def define_named_route_methods(name, route)
  14858. define_url_helper route, :"#{name}_path",
  14859. route.defaults.merge(:use_route => name, :only_path => true)
  14860. define_url_helper route, :"#{name}_url",
  14861. route.defaults.merge(:use_route => name, :only_path => false)
  14862. end
  14863. end
  14864. attr_accessor :formatter, :set, :named_routes, :default_scope, :router
  14865. attr_accessor :disable_clear_and_finalize, :resources_path_names
  14866. attr_accessor :default_url_options, :request_class
  14867. alias :routes :set
  14868. def self.default_resources_path_names
  14869. { :new => 'new', :edit => 'edit' }
  14870. end
  14871. def initialize(request_class = ActionDispatch::Request)
  14872. self.named_routes = NamedRouteCollection.new
  14873. self.resources_path_names = self.class.default_resources_path_names.dup
  14874. self.default_url_options = {}
  14875. self.request_class = request_class
  14876. @append = []
  14877. @prepend = []
  14878. @disable_clear_and_finalize = false
  14879. @finalized = false
  14880. @set = Journey::Routes.new
  14881. @router = Journey::Router.new(@set, {
  14882. :parameters_key => PARAMETERS_KEY,
  14883. :request_class => request_class})
  14884. @formatter = Journey::Formatter.new @set
  14885. end
  14886. def draw(&block)
  14887. clear! unless @disable_clear_and_finalize
  14888. eval_block(block)
  14889. finalize! unless @disable_clear_and_finalize
  14890. nil
  14891. end
  14892. def append(&block)
  14893. @append << block
  14894. end
  14895. def prepend(&block)
  14896. @prepend << block
  14897. end
  14898. def eval_block(block)
  14899. if block.arity == 1
  14900. raise "You are using the old router DSL which has been removed in Rails 3.1. " <<
  14901. "Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
  14902. end
  14903. mapper = Mapper.new(self)
  14904. if default_scope
  14905. mapper.with_default_scope(default_scope, &block)
  14906. else
  14907. mapper.instance_exec(&block)
  14908. end
  14909. end
  14910. def finalize!
  14911. return if @finalized
  14912. @append.each { |blk| eval_block(blk) }
  14913. @finalized = true
  14914. end
  14915. def clear!
  14916. @finalized = false
  14917. named_routes.clear
  14918. set.clear
  14919. formatter.clear
  14920. @prepend.each { |blk| eval_block(blk) }
  14921. end
  14922. module MountedHelpers #:nodoc:
  14923. extend ActiveSupport::Concern
  14924. include UrlFor
  14925. end
  14926. # Contains all the mounted helpers accross different
  14927. # engines and the `main_app` helper for the application.
  14928. # You can include this in your classes if you want to
  14929. # access routes for other engines.
  14930. def mounted_helpers
  14931. MountedHelpers
  14932. end
  14933. def define_mounted_helper(name)
  14934. return if MountedHelpers.method_defined?(name)
  14935. routes = self
  14936. MountedHelpers.class_eval do
  14937. define_method "_#{name}" do
  14938. RoutesProxy.new(routes, _routes_context)
  14939. end
  14940. end
  14941. MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
  14942. def #{name}
  14943. @_#{name} ||= _#{name}
  14944. end
  14945. RUBY
  14946. end
  14947. def url_helpers
  14948. @url_helpers ||= begin
  14949. routes = self
  14950. Module.new do
  14951. extend ActiveSupport::Concern
  14952. include UrlFor
  14953. # Define url_for in the singleton level so one can do:
  14954. # Rails.application.routes.url_helpers.url_for(args)
  14955. @_routes = routes
  14956. class << self
  14957. delegate :url_for, :optimize_routes_generation?, :to => '@_routes'
  14958. end
  14959. # Make named_routes available in the module singleton
  14960. # as well, so one can do:
  14961. # Rails.application.routes.url_helpers.posts_path
  14962. extend routes.named_routes.module
  14963. # Any class that includes this module will get all
  14964. # named routes...
  14965. include routes.named_routes.module
  14966. # plus a singleton class method called _routes ...
  14967. included do
  14968. singleton_class.send(:redefine_method, :_routes) { routes }
  14969. end
  14970. # And an instance method _routes. Note that
  14971. # UrlFor (included in this module) add extra
  14972. # conveniences for working with @_routes.
  14973. define_method(:_routes) { @_routes || routes }
  14974. end
  14975. end
  14976. end
  14977. def empty?
  14978. routes.empty?
  14979. end
  14980. def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
  14981. raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
  14982. path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
  14983. conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
  14984. route = @set.add_route(app, path, conditions, defaults, name)
  14985. named_routes[name] = route if name && !named_routes[name]
  14986. route
  14987. end
  14988. def build_path(path, requirements, separators, anchor)
  14989. strexp = Journey::Router::Strexp.new(
  14990. path,
  14991. requirements,
  14992. SEPARATORS,
  14993. anchor)
  14994. pattern = Journey::Path::Pattern.new(strexp)
  14995. builder = Journey::GTG::Builder.new pattern.spec
  14996. # Get all the symbol nodes followed by literals that are not the
  14997. # dummy node.
  14998. symbols = pattern.spec.grep(Journey::Nodes::Symbol).find_all { |n|
  14999. builder.followpos(n).first.literal?
  15000. }
  15001. # Get all the symbol nodes preceded by literals.
  15002. symbols.concat pattern.spec.find_all(&:literal?).map { |n|
  15003. builder.followpos(n).first
  15004. }.find_all(&:symbol?)
  15005. symbols.each { |x|
  15006. x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
  15007. }
  15008. pattern
  15009. end
  15010. private :build_path
  15011. def build_conditions(current_conditions, path_values)
  15012. conditions = current_conditions.dup
  15013. # Rack-Mount requires that :request_method be a regular expression.
  15014. # :request_method represents the HTTP verb that matches this route.
  15015. #
  15016. # Here we munge values before they get sent on to rack-mount.
  15017. verbs = conditions[:request_method] || []
  15018. unless verbs.empty?
  15019. conditions[:request_method] = %r[^#{verbs.join('|')}$]
  15020. end
  15021. conditions.keep_if do |k, _|
  15022. k == :action || k == :controller || k == :required_defaults ||
  15023. @request_class.public_method_defined?(k) || path_values.include?(k)
  15024. end
  15025. end
  15026. private :build_conditions
  15027. class Generator #:nodoc:
  15028. PARAMETERIZE = lambda do |name, value|
  15029. if name == :controller
  15030. value
  15031. elsif value.is_a?(Array)
  15032. value.map { |v| v.to_param }.join('/')
  15033. elsif param = value.to_param
  15034. param
  15035. end
  15036. end
  15037. attr_reader :options, :recall, :set, :named_route
  15038. def initialize(options, recall, set)
  15039. @named_route = options.delete(:use_route)
  15040. @options = options.dup
  15041. @recall = recall.dup
  15042. @set = set
  15043. normalize_options!
  15044. normalize_controller_action_id!
  15045. use_relative_controller!
  15046. normalize_controller!
  15047. handle_nil_action!
  15048. end
  15049. def controller
  15050. @options[:controller]
  15051. end
  15052. def current_controller
  15053. @recall[:controller]
  15054. end
  15055. def use_recall_for(key)
  15056. if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
  15057. if !named_route_exists? || segment_keys.include?(key)
  15058. @options[key] = @recall.delete(key)
  15059. end
  15060. end
  15061. end
  15062. def normalize_options!
  15063. # If an explicit :controller was given, always make :action explicit
  15064. # too, so that action expiry works as expected for things like
  15065. #
  15066. # generate({controller: 'content'}, {controller: 'content', action: 'show'})
  15067. #
  15068. # (the above is from the unit tests). In the above case, because the
  15069. # controller was explicitly given, but no action, the action is implied to
  15070. # be "index", not the recalled action of "show".
  15071. if options[:controller]
  15072. options[:action] ||= 'index'
  15073. options[:controller] = options[:controller].to_s
  15074. end
  15075. if options[:action]
  15076. options[:action] = options[:action].to_s
  15077. end
  15078. end
  15079. # This pulls :controller, :action, and :id out of the recall.
  15080. # The recall key is only used if there is no key in the options
  15081. # or if the key in the options is identical. If any of
  15082. # :controller, :action or :id is not found, don't pull any
  15083. # more keys from the recall.
  15084. def normalize_controller_action_id!
  15085. @recall[:action] ||= 'index' if current_controller
  15086. use_recall_for(:controller) or return
  15087. use_recall_for(:action) or return
  15088. use_recall_for(:id)
  15089. end
  15090. # if the current controller is "foo/bar/baz" and controller: "baz/bat"
  15091. # is specified, the controller becomes "foo/baz/bat"
  15092. def use_relative_controller!
  15093. if !named_route && different_controller? && !controller.start_with?("/")
  15094. old_parts = current_controller.split('/')
  15095. size = controller.count("/") + 1
  15096. parts = old_parts[0...-size] << controller
  15097. @options[:controller] = parts.join("/")
  15098. end
  15099. end
  15100. # Remove leading slashes from controllers
  15101. def normalize_controller!
  15102. @options[:controller] = controller.sub(%r{^/}, '') if controller
  15103. end
  15104. # This handles the case of action: nil being explicitly passed.
  15105. # It is identical to action: "index"
  15106. def handle_nil_action!
  15107. if options.has_key?(:action) && options[:action].nil?
  15108. options[:action] = 'index'
  15109. end
  15110. recall[:action] = options.delete(:action) if options[:action] == 'index'
  15111. end
  15112. # Generates a path from routes, returns [path, params].
  15113. # If no route is generated the formatter will raise ActionController::UrlGenerationError
  15114. def generate
  15115. @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
  15116. end
  15117. def different_controller?
  15118. return false unless current_controller
  15119. controller.to_param != current_controller.to_param
  15120. end
  15121. private
  15122. def named_route_exists?
  15123. named_route && set.named_routes[named_route]
  15124. end
  15125. def segment_keys
  15126. set.named_routes[named_route].segment_keys
  15127. end
  15128. end
  15129. # Generate the path indicated by the arguments, and return an array of
  15130. # the keys that were not used to generate it.
  15131. def extra_keys(options, recall={})
  15132. generate_extras(options, recall).last
  15133. end
  15134. def generate_extras(options, recall={})
  15135. path, params = generate(options, recall)
  15136. return path, params.keys
  15137. end
  15138. def generate(options, recall = {})
  15139. Generator.new(options, recall, self).generate
  15140. end
  15141. RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
  15142. :trailing_slash, :anchor, :params, :only_path, :script_name,
  15143. :original_script_name]
  15144. def mounted?
  15145. false
  15146. end
  15147. def optimize_routes_generation?
  15148. !mounted? && default_url_options.empty?
  15149. end
  15150. def _generate_prefix(options = {})
  15151. nil
  15152. end
  15153. # The +options+ argument must be +nil+ or a hash whose keys are *symbols*.
  15154. def url_for(options)
  15155. options = default_url_options.merge(options || {})
  15156. user, password = extract_authentication(options)
  15157. recall = options.delete(:_recall)
  15158. original_script_name = options.delete(:original_script_name).presence
  15159. script_name = options.delete(:script_name).presence || _generate_prefix(options)
  15160. if script_name && original_script_name
  15161. script_name = original_script_name + script_name
  15162. end
  15163. path_options = options.except(*RESERVED_OPTIONS)
  15164. path_options = yield(path_options) if block_given?
  15165. path, params = generate(path_options, recall || {})
  15166. params.merge!(options[:params] || {})
  15167. ActionDispatch::Http::URL.url_for(options.merge!({
  15168. :path => path,
  15169. :script_name => script_name,
  15170. :params => params,
  15171. :user => user,
  15172. :password => password
  15173. }))
  15174. end
  15175. def call(env)
  15176. @router.call(env)
  15177. end
  15178. def recognize_path(path, environment = {})
  15179. method = (environment[:method] || "GET").to_s.upcase
  15180. path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://}
  15181. extras = environment[:extras] || {}
  15182. begin
  15183. env = Rack::MockRequest.env_for(path, {:method => method})
  15184. rescue URI::InvalidURIError => e
  15185. raise ActionController::RoutingError, e.message
  15186. end
  15187. req = @request_class.new(env)
  15188. @router.recognize(req) do |route, matches, params|
  15189. params.merge!(extras)
  15190. params.each do |key, value|
  15191. if value.is_a?(String)
  15192. value = value.dup.force_encoding(Encoding::BINARY)
  15193. params[key] = URI.parser.unescape(value)
  15194. end
  15195. end
  15196. old_params = env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
  15197. env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] = (old_params || {}).merge(params)
  15198. dispatcher = route.app
  15199. while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
  15200. dispatcher = dispatcher.app
  15201. end
  15202. if dispatcher.is_a?(Dispatcher)
  15203. if dispatcher.controller(params, false)
  15204. dispatcher.prepare_params!(params)
  15205. return params
  15206. else
  15207. raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
  15208. end
  15209. end
  15210. end
  15211. raise ActionController::RoutingError, "No route matches #{path.inspect}"
  15212. end
  15213. private
  15214. def extract_authentication(options)
  15215. if options[:user] && options[:password]
  15216. [options.delete(:user), options.delete(:password)]
  15217. else
  15218. nil
  15219. end
  15220. end
  15221. end
  15222. end
  15223. end
  15224. require 'active_support/core_ext/array/extract_options'
  15225. module ActionDispatch
  15226. module Routing
  15227. class RoutesProxy #:nodoc:
  15228. include ActionDispatch::Routing::UrlFor
  15229. attr_accessor :scope, :routes
  15230. alias :_routes :routes
  15231. def initialize(routes, scope)
  15232. @routes, @scope = routes, scope
  15233. end
  15234. def url_options
  15235. scope.send(:_with_routes, routes) do
  15236. scope.url_options
  15237. end
  15238. end
  15239. def respond_to?(method, include_private = false)
  15240. super || routes.url_helpers.respond_to?(method)
  15241. end
  15242. def method_missing(method, *args)
  15243. if routes.url_helpers.respond_to?(method)
  15244. self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
  15245. def #{method}(*args)
  15246. options = args.extract_options!
  15247. args << url_options.merge((options || {}).symbolize_keys)
  15248. routes.url_helpers.#{method}(*args)
  15249. end
  15250. RUBY
  15251. send(method, *args)
  15252. else
  15253. super
  15254. end
  15255. end
  15256. end
  15257. end
  15258. end
  15259. module ActionDispatch
  15260. module Routing
  15261. # In <tt>config/routes.rb</tt> you define URL-to-controller mappings, but the reverse
  15262. # is also possible: an URL can be generated from one of your routing definitions.
  15263. # URL generation functionality is centralized in this module.
  15264. #
  15265. # See ActionDispatch::Routing for general information about routing and routes.rb.
  15266. #
  15267. # <b>Tip:</b> If you need to generate URLs from your models or some other place,
  15268. # then ActionController::UrlFor is what you're looking for. Read on for
  15269. # an introduction. In general, this module should not be included on its own,
  15270. # as it is usually included by url_helpers (as in Rails.application.routes.url_helpers).
  15271. #
  15272. # == URL generation from parameters
  15273. #
  15274. # As you may know, some functions, such as ActionController::Base#url_for
  15275. # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
  15276. # of parameters. For example, you've probably had the chance to write code
  15277. # like this in one of your views:
  15278. #
  15279. # <%= link_to('Click here', controller: 'users',
  15280. # action: 'new', message: 'Welcome!') %>
  15281. # # => "/users/new?message=Welcome%21"
  15282. #
  15283. # link_to, and all other functions that require URL generation functionality,
  15284. # actually use ActionController::UrlFor under the hood. And in particular,
  15285. # they use the ActionController::UrlFor#url_for method. One can generate
  15286. # the same path as the above example by using the following code:
  15287. #
  15288. # include UrlFor
  15289. # url_for(controller: 'users',
  15290. # action: 'new',
  15291. # message: 'Welcome!',
  15292. # only_path: true)
  15293. # # => "/users/new?message=Welcome%21"
  15294. #
  15295. # Notice the <tt>only_path: true</tt> part. This is because UrlFor has no
  15296. # information about the website hostname that your Rails app is serving. So if you
  15297. # want to include the hostname as well, then you must also pass the <tt>:host</tt>
  15298. # argument:
  15299. #
  15300. # include UrlFor
  15301. # url_for(controller: 'users',
  15302. # action: 'new',
  15303. # message: 'Welcome!',
  15304. # host: 'www.example.com')
  15305. # # => "http://www.example.com/users/new?message=Welcome%21"
  15306. #
  15307. # By default, all controllers and views have access to a special version of url_for,
  15308. # that already knows what the current hostname is. So if you use url_for in your
  15309. # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
  15310. # argument.
  15311. #
  15312. # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
  15313. # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
  15314. # in full. However, mailers don't have hostname information, and that's why you'll still
  15315. # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
  15316. #
  15317. #
  15318. # == URL generation for named routes
  15319. #
  15320. # UrlFor also allows one to access methods that have been auto-generated from
  15321. # named routes. For example, suppose that you have a 'users' resource in your
  15322. # <tt>config/routes.rb</tt>:
  15323. #
  15324. # resources :users
  15325. #
  15326. # This generates, among other things, the method <tt>users_path</tt>. By default,
  15327. # this method is accessible from your controllers, views and mailers. If you need
  15328. # to access this auto-generated method from other places (such as a model), then
  15329. # you can do that by including Rails.application.routes.url_helpers in your class:
  15330. #
  15331. # class User < ActiveRecord::Base
  15332. # include Rails.application.routes.url_helpers
  15333. #
  15334. # def base_uri
  15335. # user_path(self)
  15336. # end
  15337. # end
  15338. #
  15339. # User.find(1).base_uri # => "/users/1"
  15340. #
  15341. module UrlFor
  15342. extend ActiveSupport::Concern
  15343. include PolymorphicRoutes
  15344. included do
  15345. unless method_defined?(:default_url_options)
  15346. # Including in a class uses an inheritable hash. Modules get a plain hash.
  15347. if respond_to?(:class_attribute)
  15348. class_attribute :default_url_options
  15349. else
  15350. mattr_writer :default_url_options
  15351. end
  15352. self.default_url_options = {}
  15353. end
  15354. include(*_url_for_modules) if respond_to?(:_url_for_modules)
  15355. end
  15356. def initialize(*)
  15357. @_routes = nil
  15358. super
  15359. end
  15360. # Hook overridden in controller to add request information
  15361. # with `default_url_options`. Application logic should not
  15362. # go into url_options.
  15363. def url_options
  15364. default_url_options
  15365. end
  15366. # Generate a url based on the options provided, default_url_options and the
  15367. # routes defined in routes.rb. The following options are supported:
  15368. #
  15369. # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
  15370. # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
  15371. # * <tt>:host</tt> - Specifies the host the link should be targeted at.
  15372. # If <tt>:only_path</tt> is false, this option must be
  15373. # provided either explicitly, or via +default_url_options+.
  15374. # * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+
  15375. # to split the subdomain from the host.
  15376. # If false, removes all subdomains from the host part of the link.
  15377. # * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+
  15378. # to split the domain from the host.
  15379. # * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if
  15380. # <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to
  15381. # <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1.
  15382. # * <tt>:port</tt> - Optionally specify the port to connect to.
  15383. # * <tt>:anchor</tt> - An anchor name to be appended to the path.
  15384. # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
  15385. # * <tt>:script_name</tt> - Specifies application path relative to domain root. If provided, prepends application path.
  15386. #
  15387. # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
  15388. # +url_for+ is forwarded to the Routes module.
  15389. #
  15390. # url_for controller: 'tasks', action: 'testing', host: 'somehost.org', port: '8080'
  15391. # # => 'http://somehost.org:8080/tasks/testing'
  15392. # url_for controller: 'tasks', action: 'testing', host: 'somehost.org', anchor: 'ok', only_path: true
  15393. # # => '/tasks/testing#ok'
  15394. # url_for controller: 'tasks', action: 'testing', trailing_slash: true
  15395. # # => 'http://somehost.org/tasks/testing/'
  15396. # url_for controller: 'tasks', action: 'testing', host: 'somehost.org', number: '33'
  15397. # # => 'http://somehost.org/tasks/testing?number=33'
  15398. # url_for controller: 'tasks', action: 'testing', host: 'somehost.org', script_name: "/myapp"
  15399. # # => 'http://somehost.org/myapp/tasks/testing'
  15400. # url_for controller: 'tasks', action: 'testing', host: 'somehost.org', script_name: "/myapp", only_path: true
  15401. # # => '/myapp/tasks/testing'
  15402. def url_for(options = nil)
  15403. case options
  15404. when nil
  15405. _routes.url_for(url_options.symbolize_keys)
  15406. when Hash
  15407. _routes.url_for(options.symbolize_keys.reverse_merge!(url_options))
  15408. when String
  15409. options
  15410. else
  15411. polymorphic_url(options)
  15412. end
  15413. end
  15414. protected
  15415. def optimize_routes_generation?
  15416. return @_optimized_routes if defined?(@_optimized_routes)
  15417. @_optimized_routes = _routes.optimize_routes_generation? && default_url_options.empty?
  15418. end
  15419. def _with_routes(routes)
  15420. old_routes, @_routes = @_routes, routes
  15421. yield
  15422. ensure
  15423. @_routes = old_routes
  15424. end
  15425. def _routes_context
  15426. self
  15427. end
  15428. end
  15429. end
  15430. end
  15431. # encoding: UTF-8
  15432. require 'active_support/core_ext/object/to_param'
  15433. require 'active_support/core_ext/regexp'
  15434. module ActionDispatch
  15435. # The routing module provides URL rewriting in native Ruby. It's a way to
  15436. # redirect incoming requests to controllers and actions. This replaces
  15437. # mod_rewrite rules. Best of all, Rails' \Routing works with any web server.
  15438. # Routes are defined in <tt>config/routes.rb</tt>.
  15439. #
  15440. # Think of creating routes as drawing a map for your requests. The map tells
  15441. # them where to go based on some predefined pattern:
  15442. #
  15443. # AppName::Application.routes.draw do
  15444. # Pattern 1 tells some request to go to one place
  15445. # Pattern 2 tell them to go to another
  15446. # ...
  15447. # end
  15448. #
  15449. # The following symbols are special:
  15450. #
  15451. # :controller maps to your controller name
  15452. # :action maps to an action with your controllers
  15453. #
  15454. # Other names simply map to a parameter as in the case of <tt>:id</tt>.
  15455. #
  15456. # == Resources
  15457. #
  15458. # Resource routing allows you to quickly declare all of the common routes
  15459. # for a given resourceful controller. Instead of declaring separate routes
  15460. # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
  15461. # actions, a resourceful route declares them in a single line of code:
  15462. #
  15463. # resources :photos
  15464. #
  15465. # Sometimes, you have a resource that clients always look up without
  15466. # referencing an ID. A common example, /profile always shows the profile of
  15467. # the currently logged in user. In this case, you can use a singular resource
  15468. # to map /profile (rather than /profile/:id) to the show action.
  15469. #
  15470. # resource :profile
  15471. #
  15472. # It's common to have resources that are logically children of other
  15473. # resources:
  15474. #
  15475. # resources :magazines do
  15476. # resources :ads
  15477. # end
  15478. #
  15479. # You may wish to organize groups of controllers under a namespace. Most
  15480. # commonly, you might group a number of administrative controllers under
  15481. # an +admin+ namespace. You would place these controllers under the
  15482. # <tt>app/controllers/admin</tt> directory, and you can group them together
  15483. # in your router:
  15484. #
  15485. # namespace "admin" do
  15486. # resources :posts, :comments
  15487. # end
  15488. #
  15489. # Alternately, you can add prefixes to your path without using a separate
  15490. # directory by using +scope+. +scope+ takes additional options which
  15491. # apply to all enclosed routes.
  15492. #
  15493. # scope path: "/cpanel", as: 'admin' do
  15494. # resources :posts, :comments
  15495. # end
  15496. #
  15497. # For more, see <tt>Routing::Mapper::Resources#resources</tt>,
  15498. # <tt>Routing::Mapper::Scoping#namespace</tt>, and
  15499. # <tt>Routing::Mapper::Scoping#scope</tt>.
  15500. #
  15501. # == Named routes
  15502. #
  15503. # Routes can be named by passing an <tt>:as</tt> option,
  15504. # allowing for easy reference within your source as +name_of_route_url+
  15505. # for the full URL and +name_of_route_path+ for the URI path.
  15506. #
  15507. # Example:
  15508. #
  15509. # # In routes.rb
  15510. # match '/login' => 'accounts#login', as: 'login'
  15511. #
  15512. # # With render, redirect_to, tests, etc.
  15513. # redirect_to login_url
  15514. #
  15515. # Arguments can be passed as well.
  15516. #
  15517. # redirect_to show_item_path(id: 25)
  15518. #
  15519. # Use <tt>root</tt> as a shorthand to name a route for the root path "/".
  15520. #
  15521. # # In routes.rb
  15522. # root to: 'blogs#index'
  15523. #
  15524. # # would recognize http://www.example.com/ as
  15525. # params = { controller: 'blogs', action: 'index' }
  15526. #
  15527. # # and provide these named routes
  15528. # root_url # => 'http://www.example.com/'
  15529. # root_path # => '/'
  15530. #
  15531. # Note: when using +controller+, the route is simply named after the
  15532. # method you call on the block parameter rather than map.
  15533. #
  15534. # # In routes.rb
  15535. # controller :blog do
  15536. # match 'blog/show' => :list
  15537. # match 'blog/delete' => :delete
  15538. # match 'blog/edit/:id' => :edit
  15539. # end
  15540. #
  15541. # # provides named routes for show, delete, and edit
  15542. # link_to @article.title, show_path(id: @article.id)
  15543. #
  15544. # == Pretty URLs
  15545. #
  15546. # Routes can generate pretty URLs. For example:
  15547. #
  15548. # match '/articles/:year/:month/:day' => 'articles#find_by_id', constraints: {
  15549. # year: /\d{4}/,
  15550. # month: /\d{1,2}/,
  15551. # day: /\d{1,2}/
  15552. # }
  15553. #
  15554. # Using the route above, the URL "http://localhost:3000/articles/2005/11/06"
  15555. # maps to
  15556. #
  15557. # params = {year: '2005', month: '11', day: '06'}
  15558. #
  15559. # == Regular Expressions and parameters
  15560. # You can specify a regular expression to define a format for a parameter.
  15561. #
  15562. # controller 'geocode' do
  15563. # match 'geocode/:postalcode' => :show, constraints: {
  15564. # postalcode: /\d{5}(-\d{4})?/
  15565. # }
  15566. #
  15567. # Constraints can include the 'ignorecase' and 'extended syntax' regular
  15568. # expression modifiers:
  15569. #
  15570. # controller 'geocode' do
  15571. # match 'geocode/:postalcode' => :show, constraints: {
  15572. # postalcode: /hx\d\d\s\d[a-z]{2}/i
  15573. # }
  15574. # end
  15575. #
  15576. # controller 'geocode' do
  15577. # match 'geocode/:postalcode' => :show, constraints: {
  15578. # postalcode: /# Postcode format
  15579. # \d{5} #Prefix
  15580. # (-\d{4})? #Suffix
  15581. # /x
  15582. # }
  15583. # end
  15584. #
  15585. # Using the multiline match modifier will raise an +ArgumentError+.
  15586. # Encoding regular expression modifiers are silently ignored. The
  15587. # match will always use the default encoding or ASCII.
  15588. #
  15589. # == Default route
  15590. #
  15591. # Consider the following route, which you will find commented out at the
  15592. # bottom of your generated <tt>config/routes.rb</tt>:
  15593. #
  15594. # match ':controller(/:action(/:id))(.:format)'
  15595. #
  15596. # This route states that it expects requests to consist of a
  15597. # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
  15598. # turn is followed optionally by an <tt>:id</tt>, which in turn is followed
  15599. # optionally by a <tt>:format</tt>.
  15600. #
  15601. # Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end
  15602. # up with:
  15603. #
  15604. # params = { controller: 'blog',
  15605. # action: 'edit',
  15606. # id: '22'
  15607. # }
  15608. #
  15609. # By not relying on default routes, you improve the security of your
  15610. # application since not all controller actions, which includes actions you
  15611. # might add at a later time, are exposed by default.
  15612. #
  15613. # == HTTP Methods
  15614. #
  15615. # Using the <tt>:via</tt> option when specifying a route allows you to
  15616. # restrict it to a specific HTTP method. Possible values are <tt>:post</tt>,
  15617. # <tt>:get</tt>, <tt>:patch</tt>, <tt>:put</tt>, <tt>:delete</tt> and
  15618. # <tt>:any</tt>. If your route needs to respond to more than one method you
  15619. # can use an array, e.g. <tt>[ :get, :post ]</tt>. The default value is
  15620. # <tt>:any</tt> which means that the route will respond to any of the HTTP
  15621. # methods.
  15622. #
  15623. # match 'post/:id' => 'posts#show', via: :get
  15624. # match 'post/:id' => 'posts#create_comment', via: :post
  15625. #
  15626. # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
  15627. # URL will route to the <tt>show</tt> action.
  15628. #
  15629. # === HTTP helper methods
  15630. #
  15631. # An alternative method of specifying which HTTP method a route should respond to is to use the helper
  15632. # methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>.
  15633. #
  15634. # get 'post/:id' => 'posts#show'
  15635. # post 'post/:id' => 'posts#create_comment'
  15636. #
  15637. # This syntax is less verbose and the intention is more apparent to someone else reading your code,
  15638. # however if your route needs to respond to more than one HTTP method (or all methods) then using the
  15639. # <tt>:via</tt> option on <tt>match</tt> is preferable.
  15640. #
  15641. # == External redirects
  15642. #
  15643. # You can redirect any path to another path using the redirect helper in your router:
  15644. #
  15645. # match "/stories" => redirect("/posts")
  15646. #
  15647. # == Unicode character routes
  15648. #
  15649. # You can specify unicode character routes in your router:
  15650. #
  15651. # match "こんにちは" => "welcome#index"
  15652. #
  15653. # == Routing to Rack Applications
  15654. #
  15655. # Instead of a String, like <tt>posts#index</tt>, which corresponds to the
  15656. # index action in the PostsController, you can specify any Rack application
  15657. # as the endpoint for a matcher:
  15658. #
  15659. # match "/application.js" => Sprockets
  15660. #
  15661. # == Reloading routes
  15662. #
  15663. # You can reload routes if you feel you must:
  15664. #
  15665. # Rails.application.reload_routes!
  15666. #
  15667. # This will clear all named routes and reload routes.rb if the file has been modified from
  15668. # last load. To absolutely force reloading, use <tt>reload!</tt>.
  15669. #
  15670. # == Testing Routes
  15671. #
  15672. # The two main methods for testing your routes:
  15673. #
  15674. # === +assert_routing+
  15675. #
  15676. # def test_movie_route_properly_splits
  15677. # opts = {controller: "plugin", action: "checkout", id: "2"}
  15678. # assert_routing "plugin/checkout/2", opts
  15679. # end
  15680. #
  15681. # +assert_routing+ lets you test whether or not the route properly resolves into options.
  15682. #
  15683. # === +assert_recognizes+
  15684. #
  15685. # def test_route_has_options
  15686. # opts = {controller: "plugin", action: "show", id: "12"}
  15687. # assert_recognizes opts, "/plugins/show/12"
  15688. # end
  15689. #
  15690. # Note the subtle difference between the two: +assert_routing+ tests that
  15691. # a URL fits options while +assert_recognizes+ tests that a URL
  15692. # breaks into parameters properly.
  15693. #
  15694. # In tests you can simply pass the URL or named route to +get+ or +post+.
  15695. #
  15696. # def send_to_jail
  15697. # get '/jail'
  15698. # assert_response :success
  15699. # assert_template "jail/front"
  15700. # end
  15701. #
  15702. # def goes_to_login
  15703. # get login_url
  15704. # #...
  15705. # end
  15706. #
  15707. # == View a list of all your routes
  15708. #
  15709. # rake routes
  15710. #
  15711. # Target specific controllers by prefixing the command with <tt>CONTROLLER=x</tt>.
  15712. #
  15713. module Routing
  15714. autoload :Mapper, 'action_dispatch/routing/mapper'
  15715. autoload :RouteSet, 'action_dispatch/routing/route_set'
  15716. autoload :RoutesProxy, 'action_dispatch/routing/routes_proxy'
  15717. autoload :UrlFor, 'action_dispatch/routing/url_for'
  15718. autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes'
  15719. SEPARATORS = %w( / . ? ) #:nodoc:
  15720. HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:
  15721. end
  15722. end
  15723. require 'action_view/vendor/html-scanner'
  15724. module ActionDispatch
  15725. module Assertions
  15726. module DomAssertions
  15727. # \Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
  15728. #
  15729. # # assert that the referenced method generates the appropriate HTML string
  15730. # assert_dom_equal '<a href="http://www.example.com">Apples</a>', link_to("Apples", "http://www.example.com")
  15731. def assert_dom_equal(expected, actual, message = "")
  15732. expected_dom = HTML::Document.new(expected).root
  15733. actual_dom = HTML::Document.new(actual).root
  15734. assert_equal expected_dom, actual_dom
  15735. end
  15736. # The negated form of +assert_dom_equivalent+.
  15737. #
  15738. # # assert that the referenced method does not generate the specified HTML string
  15739. # assert_dom_not_equal '<a href="http://www.example.com">Apples</a>', link_to("Oranges", "http://www.example.com")
  15740. def assert_dom_not_equal(expected, actual, message = "")
  15741. expected_dom = HTML::Document.new(expected).root
  15742. actual_dom = HTML::Document.new(actual).root
  15743. assert_not_equal expected_dom, actual_dom
  15744. end
  15745. end
  15746. end
  15747. end
  15748. module ActionDispatch
  15749. module Assertions
  15750. # A small suite of assertions that test responses from \Rails applications.
  15751. module ResponseAssertions
  15752. # Asserts that the response is one of the following types:
  15753. #
  15754. # * <tt>:success</tt> - Status code was in the 200-299 range
  15755. # * <tt>:redirect</tt> - Status code was in the 300-399 range
  15756. # * <tt>:missing</tt> - Status code was 404
  15757. # * <tt>:error</tt> - Status code was in the 500-599 range
  15758. #
  15759. # You can also pass an explicit status number like <tt>assert_response(501)</tt>
  15760. # or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
  15761. # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
  15762. #
  15763. # # assert that the response was a redirection
  15764. # assert_response :redirect
  15765. #
  15766. # # assert that the response code was status code 401 (unauthorized)
  15767. # assert_response 401
  15768. def assert_response(type, message = nil)
  15769. message ||= "Expected response to be a <#{type}>, but was <#{@response.response_code}>"
  15770. if Symbol === type
  15771. if [:success, :missing, :redirect, :error].include?(type)
  15772. assert @response.send("#{type}?"), message
  15773. else
  15774. code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
  15775. assert_equal code, @response.response_code, message
  15776. end
  15777. else
  15778. assert_equal type, @response.response_code, message
  15779. end
  15780. end
  15781. # Assert that the redirection options passed in match those of the redirect called in the latest action.
  15782. # This match can be partial, such that <tt>assert_redirected_to(controller: "weblog")</tt> will also
  15783. # match the redirection of <tt>redirect_to(controller: "weblog", action: "show")</tt> and so on.
  15784. #
  15785. # # assert that the redirection was to the "index" action on the WeblogController
  15786. # assert_redirected_to controller: "weblog", action: "index"
  15787. #
  15788. # # assert that the redirection was to the named route login_url
  15789. # assert_redirected_to login_url
  15790. #
  15791. # # assert that the redirection was to the url for @customer
  15792. # assert_redirected_to @customer
  15793. #
  15794. # # asserts that the redirection matches the regular expression
  15795. # assert_redirected_to %r(\Ahttp://example.org)
  15796. def assert_redirected_to(options = {}, message=nil)
  15797. assert_response(:redirect, message)
  15798. return true if options === @response.location
  15799. redirect_is = normalize_argument_to_redirection(@response.location)
  15800. redirect_expected = normalize_argument_to_redirection(options)
  15801. message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
  15802. assert_operator redirect_expected, :===, redirect_is, message
  15803. end
  15804. private
  15805. # Proxy to to_param if the object will respond to it.
  15806. def parameterize(value)
  15807. value.respond_to?(:to_param) ? value.to_param : value
  15808. end
  15809. def normalize_argument_to_redirection(fragment)
  15810. normalized = case fragment
  15811. when Regexp
  15812. fragment
  15813. when %r{^\w[A-Za-z\d+.-]*:.*}
  15814. fragment
  15815. when String
  15816. @request.protocol + @request.host_with_port + fragment
  15817. when :back
  15818. raise RedirectBackError unless refer = @request.headers["Referer"]
  15819. refer
  15820. else
  15821. @controller.url_for(fragment)
  15822. end
  15823. normalized.respond_to?(:delete) ? normalized.delete("\0\r\n") : normalized
  15824. end
  15825. end
  15826. end
  15827. end
  15828. require 'uri'
  15829. require 'active_support/core_ext/hash/indifferent_access'
  15830. require 'active_support/core_ext/string/access'
  15831. require 'action_controller/metal/exceptions'
  15832. module ActionDispatch
  15833. module Assertions
  15834. # Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
  15835. module RoutingAssertions
  15836. # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
  15837. # match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
  15838. #
  15839. # Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
  15840. # requiring a specific HTTP method. The hash should contain a :path with the incoming request path
  15841. # and a :method containing the required HTTP verb.
  15842. #
  15843. # # assert that POSTing to /items will call the create action on ItemsController
  15844. # assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
  15845. #
  15846. # You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
  15847. # to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
  15848. # extras argument, appending the query string on the path directly will not work. For example:
  15849. #
  15850. # # assert that a path of '/items/list/1?view=print' returns the correct options
  15851. # assert_recognizes({controller: 'items', action: 'list', id: '1', view: 'print'}, 'items/list/1', { view: "print" })
  15852. #
  15853. # The +message+ parameter allows you to pass in an error message that is displayed upon failure.
  15854. #
  15855. # # Check the default route (i.e., the index action)
  15856. # assert_recognizes({controller: 'items', action: 'index'}, 'items')
  15857. #
  15858. # # Test a specific action
  15859. # assert_recognizes({controller: 'items', action: 'list'}, 'items/list')
  15860. #
  15861. # # Test an action with a parameter
  15862. # assert_recognizes({controller: 'items', action: 'destroy', id: '1'}, 'items/destroy/1')
  15863. #
  15864. # # Test a custom route
  15865. # assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
  15866. def assert_recognizes(expected_options, path, extras={}, msg=nil)
  15867. request = recognized_request_for(path, extras)
  15868. expected_options = expected_options.clone
  15869. expected_options.stringify_keys!
  15870. msg = message(msg, "") {
  15871. sprintf("The recognized options <%s> did not match <%s>, difference:",
  15872. request.path_parameters, expected_options)
  15873. }
  15874. assert_equal(expected_options, request.path_parameters, msg)
  15875. end
  15876. # Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
  15877. # The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
  15878. # a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
  15879. #
  15880. # The +defaults+ parameter is unused.
  15881. #
  15882. # # Asserts that the default action is generated for a route with no action
  15883. # assert_generates "/items", controller: "items", action: "index"
  15884. #
  15885. # # Tests that the list action is properly routed
  15886. # assert_generates "/items/list", controller: "items", action: "list"
  15887. #
  15888. # # Tests the generation of a route with a parameter
  15889. # assert_generates "/items/list/1", { controller: "items", action: "list", id: "1" }
  15890. #
  15891. # # Asserts that the generated route gives us our custom route
  15892. # assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
  15893. def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
  15894. if expected_path =~ %r{://}
  15895. fail_on(URI::InvalidURIError) do
  15896. uri = URI.parse(expected_path)
  15897. expected_path = uri.path.to_s.empty? ? "/" : uri.path
  15898. end
  15899. else
  15900. expected_path = "/#{expected_path}" unless expected_path.first == '/'
  15901. end
  15902. # Load routes.rb if it hasn't been loaded.
  15903. generated_path, extra_keys = @routes.generate_extras(options, defaults)
  15904. found_extras = options.reject {|k, v| ! extra_keys.include? k}
  15905. msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
  15906. assert_equal(extras, found_extras, msg)
  15907. msg = message || sprintf("The generated path <%s> did not match <%s>", generated_path,
  15908. expected_path)
  15909. assert_equal(expected_path, generated_path, msg)
  15910. end
  15911. # Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
  15912. # <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+
  15913. # and +assert_generates+ into one step.
  15914. #
  15915. # The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
  15916. # +message+ parameter allows you to specify a custom error message to display upon failure.
  15917. #
  15918. # # Assert a basic route: a controller with the default action (index)
  15919. # assert_routing '/home', controller: 'home', action: 'index'
  15920. #
  15921. # # Test a route generated with a specific controller, action, and parameter (id)
  15922. # assert_routing '/entries/show/23', controller: 'entries', action: 'show', id: 23
  15923. #
  15924. # # Assert a basic route (controller + default action), with an error message if it fails
  15925. # assert_routing '/store', { controller: 'store', action: 'index' }, {}, {}, 'Route for store index not generated properly'
  15926. #
  15927. # # Tests a route, providing a defaults hash
  15928. # assert_routing 'controller/action/9', {id: "9", item: "square"}, {controller: "controller", action: "action"}, {}, {item: "square"}
  15929. #
  15930. # # Tests a route with a HTTP method
  15931. # assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
  15932. def assert_routing(path, options, defaults={}, extras={}, message=nil)
  15933. assert_recognizes(options, path, extras, message)
  15934. controller, default_controller = options[:controller], defaults[:controller]
  15935. if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
  15936. options[:controller] = "/#{controller}"
  15937. end
  15938. generate_options = options.dup.delete_if{ |k,v| defaults.key?(k) }
  15939. assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
  15940. end
  15941. # A helper to make it easier to test different route configurations.
  15942. # This method temporarily replaces @routes
  15943. # with a new RouteSet instance.
  15944. #
  15945. # The new instance is yielded to the passed block. Typically the block
  15946. # will create some routes using <tt>set.draw { match ... }</tt>:
  15947. #
  15948. # with_routing do |set|
  15949. # set.draw do
  15950. # resources :users
  15951. # end
  15952. # assert_equal "/users", users_path
  15953. # end
  15954. #
  15955. def with_routing
  15956. old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
  15957. if defined?(@controller) && @controller
  15958. old_controller, @controller = @controller, @controller.clone
  15959. _routes = @routes
  15960. # Unfortunately, there is currently an abstraction leak between AC::Base
  15961. # and AV::Base which requires having the URL helpers in both AC and AV.
  15962. # To do this safely at runtime for tests, we need to bump up the helper serial
  15963. # to that the old AV subclass isn't cached.
  15964. #
  15965. # TODO: Make this unnecessary
  15966. @controller.singleton_class.send(:include, _routes.url_helpers)
  15967. @controller.view_context_class = Class.new(@controller.view_context_class) do
  15968. include _routes.url_helpers
  15969. end
  15970. end
  15971. yield @routes
  15972. ensure
  15973. @routes = old_routes
  15974. if defined?(@controller) && @controller
  15975. @controller = old_controller
  15976. end
  15977. end
  15978. # ROUTES TODO: These assertions should really work in an integration context
  15979. def method_missing(selector, *args, &block)
  15980. if defined?(@controller) && @controller && @routes && @routes.named_routes.helpers.include?(selector)
  15981. @controller.send(selector, *args, &block)
  15982. else
  15983. super
  15984. end
  15985. end
  15986. private
  15987. # Recognizes the route for a given path.
  15988. def recognized_request_for(path, extras = {})
  15989. if path.is_a?(Hash)
  15990. method = path[:method]
  15991. path = path[:path]
  15992. else
  15993. method = :get
  15994. end
  15995. # Assume given controller
  15996. request = ActionController::TestRequest.new
  15997. if path =~ %r{://}
  15998. fail_on(URI::InvalidURIError) do
  15999. uri = URI.parse(path)
  16000. request.env["rack.url_scheme"] = uri.scheme || "http"
  16001. request.host = uri.host if uri.host
  16002. request.port = uri.port if uri.port
  16003. request.path = uri.path.to_s.empty? ? "/" : uri.path
  16004. end
  16005. else
  16006. path = "/#{path}" unless path.first == "/"
  16007. request.path = path
  16008. end
  16009. request.request_method = method if method
  16010. params = fail_on(ActionController::RoutingError) do
  16011. @routes.recognize_path(path, { :method => method, :extras => extras })
  16012. end
  16013. request.path_parameters = params.with_indifferent_access
  16014. request
  16015. end
  16016. def fail_on(exception_class)
  16017. yield
  16018. rescue exception_class => e
  16019. raise MiniTest::Assertion, e.message
  16020. end
  16021. end
  16022. end
  16023. end
  16024. require 'action_view/vendor/html-scanner'
  16025. require 'active_support/core_ext/object/inclusion'
  16026. #--
  16027. # Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
  16028. # Under MIT and/or CC By license.
  16029. #++
  16030. module ActionDispatch
  16031. module Assertions
  16032. NO_STRIP = %w{pre script style textarea}
  16033. # Adds the +assert_select+ method for use in Rails functional
  16034. # test cases, which can be used to make assertions on the response HTML of a controller
  16035. # action. You can also call +assert_select+ within another +assert_select+ to
  16036. # make assertions on elements selected by the enclosing assertion.
  16037. #
  16038. # Use +css_select+ to select elements without making an assertions, either
  16039. # from the response HTML or elements selected by the enclosing assertion.
  16040. #
  16041. # In addition to HTML responses, you can make the following assertions:
  16042. #
  16043. # * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
  16044. # * +assert_select_email+ - Assertions on the HTML body of an e-mail.
  16045. #
  16046. # Also see HTML::Selector to learn how to use selectors.
  16047. module SelectorAssertions
  16048. # Select and return all matching elements.
  16049. #
  16050. # If called with a single argument, uses that argument as a selector
  16051. # to match all elements of the current page. Returns an empty array
  16052. # if no match is found.
  16053. #
  16054. # If called with two arguments, uses the first argument as the base
  16055. # element and the second argument as the selector. Attempts to match the
  16056. # base element and any of its children. Returns an empty array if no
  16057. # match is found.
  16058. #
  16059. # The selector may be a CSS selector expression (String), an expression
  16060. # with substitution values (Array) or an HTML::Selector object.
  16061. #
  16062. # # Selects all div tags
  16063. # divs = css_select("div")
  16064. #
  16065. # # Selects all paragraph tags and does something interesting
  16066. # pars = css_select("p")
  16067. # pars.each do |par|
  16068. # # Do something fun with paragraphs here...
  16069. # end
  16070. #
  16071. # # Selects all list items in unordered lists
  16072. # items = css_select("ul>li")
  16073. #
  16074. # # Selects all form tags and then all inputs inside the form
  16075. # forms = css_select("form")
  16076. # forms.each do |form|
  16077. # inputs = css_select(form, "input")
  16078. # ...
  16079. # end
  16080. def css_select(*args)
  16081. # See assert_select to understand what's going on here.
  16082. arg = args.shift
  16083. if arg.is_a?(HTML::Node)
  16084. root = arg
  16085. arg = args.shift
  16086. elsif arg == nil
  16087. raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
  16088. elsif defined?(@selected) && @selected
  16089. matches = []
  16090. @selected.each do |selected|
  16091. subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
  16092. subset.each do |match|
  16093. matches << match unless matches.any? { |m| m.equal?(match) }
  16094. end
  16095. end
  16096. return matches
  16097. else
  16098. root = response_from_page
  16099. end
  16100. case arg
  16101. when String
  16102. selector = HTML::Selector.new(arg, args)
  16103. when Array
  16104. selector = HTML::Selector.new(*arg)
  16105. when HTML::Selector
  16106. selector = arg
  16107. else raise ArgumentError, "Expecting a selector as the first argument"
  16108. end
  16109. selector.select(root)
  16110. end
  16111. # An assertion that selects elements and makes one or more equality tests.
  16112. #
  16113. # If the first argument is an element, selects all matching elements
  16114. # starting from (and including) that element and all its children in
  16115. # depth-first order.
  16116. #
  16117. # If no element if specified, calling +assert_select+ selects from the
  16118. # response HTML unless +assert_select+ is called from within an +assert_select+ block.
  16119. #
  16120. # When called with a block +assert_select+ passes an array of selected elements
  16121. # to the block. Calling +assert_select+ from the block, with no element specified,
  16122. # runs the assertion on the complete set of elements selected by the enclosing assertion.
  16123. # Alternatively the array may be iterated through so that +assert_select+ can be called
  16124. # separately for each element.
  16125. #
  16126. #
  16127. # ==== Example
  16128. # If the response contains two ordered lists, each with four list elements then:
  16129. # assert_select "ol" do |elements|
  16130. # elements.each do |element|
  16131. # assert_select element, "li", 4
  16132. # end
  16133. # end
  16134. #
  16135. # will pass, as will:
  16136. # assert_select "ol" do
  16137. # assert_select "li", 8
  16138. # end
  16139. #
  16140. # The selector may be a CSS selector expression (String), an expression
  16141. # with substitution values, or an HTML::Selector object.
  16142. #
  16143. # === Equality Tests
  16144. #
  16145. # The equality test may be one of the following:
  16146. # * <tt>true</tt> - Assertion is true if at least one element selected.
  16147. # * <tt>false</tt> - Assertion is true if no element selected.
  16148. # * <tt>String/Regexp</tt> - Assertion is true if the text value of at least
  16149. # one element matches the string or regular expression.
  16150. # * <tt>Integer</tt> - Assertion is true if exactly that number of
  16151. # elements are selected.
  16152. # * <tt>Range</tt> - Assertion is true if the number of selected
  16153. # elements fit the range.
  16154. # If no equality test specified, the assertion is true if at least one
  16155. # element selected.
  16156. #
  16157. # To perform more than one equality tests, use a hash with the following keys:
  16158. # * <tt>:text</tt> - Narrow the selection to elements that have this text
  16159. # value (string or regexp).
  16160. # * <tt>:html</tt> - Narrow the selection to elements that have this HTML
  16161. # content (string or regexp).
  16162. # * <tt>:count</tt> - Assertion is true if the number of selected elements
  16163. # is equal to this value.
  16164. # * <tt>:minimum</tt> - Assertion is true if the number of selected
  16165. # elements is at least this value.
  16166. # * <tt>:maximum</tt> - Assertion is true if the number of selected
  16167. # elements is at most this value.
  16168. #
  16169. # If the method is called with a block, once all equality tests are
  16170. # evaluated the block is called with an array of all matched elements.
  16171. #
  16172. # # At least one form element
  16173. # assert_select "form"
  16174. #
  16175. # # Form element includes four input fields
  16176. # assert_select "form input", 4
  16177. #
  16178. # # Page title is "Welcome"
  16179. # assert_select "title", "Welcome"
  16180. #
  16181. # # Page title is "Welcome" and there is only one title element
  16182. # assert_select "title", {count: 1, text: "Welcome"},
  16183. # "Wrong title or more than one title element"
  16184. #
  16185. # # Page contains no forms
  16186. # assert_select "form", false, "This page must contain no forms"
  16187. #
  16188. # # Test the content and style
  16189. # assert_select "body div.header ul.menu"
  16190. #
  16191. # # Use substitution values
  16192. # assert_select "ol>li#?", /item-\d+/
  16193. #
  16194. # # All input fields in the form have a name
  16195. # assert_select "form input" do
  16196. # assert_select "[name=?]", /.+/ # Not empty
  16197. # end
  16198. def assert_select(*args, &block)
  16199. # Start with optional element followed by mandatory selector.
  16200. arg = args.shift
  16201. @selected ||= nil
  16202. if arg.is_a?(HTML::Node)
  16203. # First argument is a node (tag or text, but also HTML root),
  16204. # so we know what we're selecting from.
  16205. root = arg
  16206. arg = args.shift
  16207. elsif arg == nil
  16208. # This usually happens when passing a node/element that
  16209. # happens to be nil.
  16210. raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
  16211. elsif @selected
  16212. root = HTML::Node.new(nil)
  16213. root.children.concat @selected
  16214. else
  16215. # Otherwise just operate on the response document.
  16216. root = response_from_page
  16217. end
  16218. # First or second argument is the selector: string and we pass
  16219. # all remaining arguments. Array and we pass the argument. Also
  16220. # accepts selector itself.
  16221. case arg
  16222. when String
  16223. selector = HTML::Selector.new(arg, args)
  16224. when Array
  16225. selector = HTML::Selector.new(*arg)
  16226. when HTML::Selector
  16227. selector = arg
  16228. else raise ArgumentError, "Expecting a selector as the first argument"
  16229. end
  16230. # Next argument is used for equality tests.
  16231. equals = {}
  16232. case arg = args.shift
  16233. when Hash
  16234. equals = arg
  16235. when String, Regexp
  16236. equals[:text] = arg
  16237. when Integer
  16238. equals[:count] = arg
  16239. when Range
  16240. equals[:minimum] = arg.begin
  16241. equals[:maximum] = arg.end
  16242. when FalseClass
  16243. equals[:count] = 0
  16244. when NilClass, TrueClass
  16245. equals[:minimum] = 1
  16246. else raise ArgumentError, "I don't understand what you're trying to match"
  16247. end
  16248. # By default we're looking for at least one match.
  16249. if equals[:count]
  16250. equals[:minimum] = equals[:maximum] = equals[:count]
  16251. else
  16252. equals[:minimum] = 1 unless equals[:minimum]
  16253. end
  16254. # Last argument is the message we use if the assertion fails.
  16255. message = args.shift
  16256. #- message = "No match made with selector #{selector.inspect}" unless message
  16257. if args.shift
  16258. raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
  16259. end
  16260. matches = selector.select(root)
  16261. # If text/html, narrow down to those elements that match it.
  16262. content_mismatch = nil
  16263. if match_with = equals[:text]
  16264. matches.delete_if do |match|
  16265. text = ""
  16266. stack = match.children.reverse
  16267. while node = stack.pop
  16268. if node.tag?
  16269. stack.concat node.children.reverse
  16270. else
  16271. content = node.content
  16272. text << content
  16273. end
  16274. end
  16275. text.strip! unless NO_STRIP.include?(match.name)
  16276. text.sub!(/\A\n/, '') if match.name == "textarea"
  16277. unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
  16278. content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, text)
  16279. true
  16280. end
  16281. end
  16282. elsif match_with = equals[:html]
  16283. matches.delete_if do |match|
  16284. html = match.children.map(&:to_s).join
  16285. html.strip! unless NO_STRIP.include?(match.name)
  16286. unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s)
  16287. content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, html)
  16288. true
  16289. end
  16290. end
  16291. end
  16292. # Expecting foo found bar element only if found zero, not if
  16293. # found one but expecting two.
  16294. message ||= content_mismatch if matches.empty?
  16295. # Test minimum/maximum occurrence.
  16296. min, max, count = equals[:minimum], equals[:maximum], equals[:count]
  16297. # FIXME: minitest provides messaging when we use assert_operator,
  16298. # so is this custom message really needed?
  16299. message = message || %(Expected #{count_description(min, max, count)} matching "#{selector.to_s}", found #{matches.size}.)
  16300. if count
  16301. assert_equal matches.size, count, message
  16302. else
  16303. assert_operator matches.size, :>=, min, message if min
  16304. assert_operator matches.size, :<=, max, message if max
  16305. end
  16306. # If a block is given call that block. Set @selected to allow
  16307. # nested assert_select, which can be nested several levels deep.
  16308. if block_given? && !matches.empty?
  16309. begin
  16310. in_scope, @selected = @selected, matches
  16311. yield matches
  16312. ensure
  16313. @selected = in_scope
  16314. end
  16315. end
  16316. # Returns all matches elements.
  16317. matches
  16318. end
  16319. def count_description(min, max, count) #:nodoc:
  16320. pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
  16321. if min && max && (max != min)
  16322. "between #{min} and #{max} elements"
  16323. elsif min && max && max == min && count
  16324. "exactly #{count} #{pluralize['element', min]}"
  16325. elsif min && !(min == 1 && max == 1)
  16326. "at least #{min} #{pluralize['element', min]}"
  16327. elsif max
  16328. "at most #{max} #{pluralize['element', max]}"
  16329. end
  16330. end
  16331. # Extracts the content of an element, treats it as encoded HTML and runs
  16332. # nested assertion on it.
  16333. #
  16334. # You typically call this method within another assertion to operate on
  16335. # all currently selected elements. You can also pass an element or array
  16336. # of elements.
  16337. #
  16338. # The content of each element is un-encoded, and wrapped in the root
  16339. # element +encoded+. It then calls the block with all un-encoded elements.
  16340. #
  16341. # # Selects all bold tags from within the title of an Atom feed's entries (perhaps to nab a section name prefix)
  16342. # assert_select "feed[xmlns='http://www.w3.org/2005/Atom']" do
  16343. # # Select each entry item and then the title item
  16344. # assert_select "entry>title" do
  16345. # # Run assertions on the encoded title elements
  16346. # assert_select_encoded do
  16347. # assert_select "b"
  16348. # end
  16349. # end
  16350. # end
  16351. #
  16352. #
  16353. # # Selects all paragraph tags from within the description of an RSS feed
  16354. # assert_select "rss[version=2.0]" do
  16355. # # Select description element of each feed item.
  16356. # assert_select "channel>item>description" do
  16357. # # Run assertions on the encoded elements.
  16358. # assert_select_encoded do
  16359. # assert_select "p"
  16360. # end
  16361. # end
  16362. # end
  16363. def assert_select_encoded(element = nil, &block)
  16364. case element
  16365. when Array
  16366. elements = element
  16367. when HTML::Node
  16368. elements = [element]
  16369. when nil
  16370. unless elements = @selected
  16371. raise ArgumentError, "First argument is optional, but must be called from a nested assert_select"
  16372. end
  16373. else
  16374. raise ArgumentError, "Argument is optional, and may be node or array of nodes"
  16375. end
  16376. fix_content = lambda do |node|
  16377. # Gets around a bug in the Rails 1.1 HTML parser.
  16378. node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
  16379. end
  16380. selected = elements.map do |_element|
  16381. text = _element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
  16382. root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
  16383. css_select(root, "encoded:root", &block)[0]
  16384. end
  16385. begin
  16386. old_selected, @selected = @selected, selected
  16387. assert_select ":root", &block
  16388. ensure
  16389. @selected = old_selected
  16390. end
  16391. end
  16392. # Extracts the body of an email and runs nested assertions on it.
  16393. #
  16394. # You must enable deliveries for this assertion to work, use:
  16395. # ActionMailer::Base.perform_deliveries = true
  16396. #
  16397. # assert_select_email do
  16398. # assert_select "h1", "Email alert"
  16399. # end
  16400. #
  16401. # assert_select_email do
  16402. # items = assert_select "ol>li"
  16403. # items.each do
  16404. # # Work with items here...
  16405. # end
  16406. # end
  16407. def assert_select_email(&block)
  16408. deliveries = ActionMailer::Base.deliveries
  16409. assert !deliveries.empty?, "No e-mail in delivery list"
  16410. deliveries.each do |delivery|
  16411. (delivery.parts.empty? ? [delivery] : delivery.parts).each do |part|
  16412. if part["Content-Type"].to_s =~ /^text\/html\W/
  16413. root = HTML::Document.new(part.body.to_s).root
  16414. assert_select root, ":root", &block
  16415. end
  16416. end
  16417. end
  16418. end
  16419. protected
  16420. # +assert_select+ and +css_select+ call this to obtain the content in the HTML page.
  16421. def response_from_page
  16422. html_document.root
  16423. end
  16424. end
  16425. end
  16426. end
  16427. require 'action_view/vendor/html-scanner'
  16428. module ActionDispatch
  16429. module Assertions
  16430. # Pair of assertions to testing elements in the HTML output of the response.
  16431. module TagAssertions
  16432. # Asserts that there is a tag/node/element in the body of the response
  16433. # that meets all of the given conditions. The +conditions+ parameter must
  16434. # be a hash of any of the following keys (all are optional):
  16435. #
  16436. # * <tt>:tag</tt>: the node type must match the corresponding value
  16437. # * <tt>:attributes</tt>: a hash. The node's attributes must match the
  16438. # corresponding values in the hash.
  16439. # * <tt>:parent</tt>: a hash. The node's parent must match the
  16440. # corresponding hash.
  16441. # * <tt>:child</tt>: a hash. At least one of the node's immediate children
  16442. # must meet the criteria described by the hash.
  16443. # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
  16444. # meet the criteria described by the hash.
  16445. # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
  16446. # must meet the criteria described by the hash.
  16447. # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
  16448. # meet the criteria described by the hash.
  16449. # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
  16450. # the criteria described by the hash, and at least one sibling must match.
  16451. # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
  16452. # the criteria described by the hash, and at least one sibling must match.
  16453. # * <tt>:children</tt>: a hash, for counting children of a node. Accepts
  16454. # the keys:
  16455. # * <tt>:count</tt>: either a number or a range which must equal (or
  16456. # include) the number of children that match.
  16457. # * <tt>:less_than</tt>: the number of matching children must be less
  16458. # than this number.
  16459. # * <tt>:greater_than</tt>: the number of matching children must be
  16460. # greater than this number.
  16461. # * <tt>:only</tt>: another hash consisting of the keys to use
  16462. # to match on the children, and only matching children will be
  16463. # counted.
  16464. # * <tt>:content</tt>: the textual content of the node must match the
  16465. # given value. This will not match HTML tags in the body of a
  16466. # tag--only text.
  16467. #
  16468. # Conditions are matched using the following algorithm:
  16469. #
  16470. # * if the condition is a string, it must be a substring of the value.
  16471. # * if the condition is a regexp, it must match the value.
  16472. # * if the condition is a number, the value must match number.to_s.
  16473. # * if the condition is +true+, the value must not be +nil+.
  16474. # * if the condition is +false+ or +nil+, the value must be +nil+.
  16475. #
  16476. # # Assert that there is a "span" tag
  16477. # assert_tag tag: "span"
  16478. #
  16479. # # Assert that there is a "span" tag with id="x"
  16480. # assert_tag tag: "span", attributes: { id: "x" }
  16481. #
  16482. # # Assert that there is a "span" tag using the short-hand
  16483. # assert_tag :span
  16484. #
  16485. # # Assert that there is a "span" tag with id="x" using the short-hand
  16486. # assert_tag :span, attributes: { id: "x" }
  16487. #
  16488. # # Assert that there is a "span" inside of a "div"
  16489. # assert_tag tag: "span", parent: { tag: "div" }
  16490. #
  16491. # # Assert that there is a "span" somewhere inside a table
  16492. # assert_tag tag: "span", ancestor: { tag: "table" }
  16493. #
  16494. # # Assert that there is a "span" with at least one "em" child
  16495. # assert_tag tag: "span", child: { tag: "em" }
  16496. #
  16497. # # Assert that there is a "span" containing a (possibly nested)
  16498. # # "strong" tag.
  16499. # assert_tag tag: "span", descendant: { tag: "strong" }
  16500. #
  16501. # # Assert that there is a "span" containing between 2 and 4 "em" tags
  16502. # # as immediate children
  16503. # assert_tag tag: "span",
  16504. # children: { count: 2..4, only: { tag: "em" } }
  16505. #
  16506. # # Get funky: assert that there is a "div", with an "ul" ancestor
  16507. # # and an "li" parent (with "class" = "enum"), and containing a
  16508. # # "span" descendant that contains text matching /hello world/
  16509. # assert_tag tag: "div",
  16510. # ancestor: { tag: "ul" },
  16511. # parent: { tag: "li",
  16512. # attributes: { class: "enum" } },
  16513. # descendant: { tag: "span",
  16514. # child: /hello world/ }
  16515. #
  16516. # <b>Please note</b>: +assert_tag+ and +assert_no_tag+ only work
  16517. # with well-formed XHTML. They recognize a few tags as implicitly self-closing
  16518. # (like br and hr and such) but will not work correctly with tags
  16519. # that allow optional closing tags (p, li, td). <em>You must explicitly
  16520. # close all of your tags to use these assertions.</em>
  16521. def assert_tag(*opts)
  16522. opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
  16523. tag = find_tag(opts)
  16524. assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
  16525. end
  16526. # Identical to +assert_tag+, but asserts that a matching tag does _not_
  16527. # exist. (See +assert_tag+ for a full discussion of the syntax.)
  16528. #
  16529. # # Assert that there is not a "div" containing a "p"
  16530. # assert_no_tag tag: "div", descendant: { tag: "p" }
  16531. #
  16532. # # Assert that an unordered list is empty
  16533. # assert_no_tag tag: "ul", descendant: { tag: "li" }
  16534. #
  16535. # # Assert that there is not a "p" tag with between 1 to 3 "img" tags
  16536. # # as immediate children
  16537. # assert_no_tag tag: "p",
  16538. # children: { count: 1..3, only: { tag: "img" } }
  16539. def assert_no_tag(*opts)
  16540. opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
  16541. tag = find_tag(opts)
  16542. assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
  16543. end
  16544. def find_tag(conditions)
  16545. html_document.find(conditions)
  16546. end
  16547. def find_all_tag(conditions)
  16548. html_document.find_all(conditions)
  16549. end
  16550. def html_document
  16551. xml = @response.content_type =~ /xml$/
  16552. @html_document ||= HTML::Document.new(@response.body, false, xml)
  16553. end
  16554. end
  16555. end
  16556. end
  16557. module ActionDispatch
  16558. module Assertions
  16559. autoload :DomAssertions, 'action_dispatch/testing/assertions/dom'
  16560. autoload :ResponseAssertions, 'action_dispatch/testing/assertions/response'
  16561. autoload :RoutingAssertions, 'action_dispatch/testing/assertions/routing'
  16562. autoload :SelectorAssertions, 'action_dispatch/testing/assertions/selector'
  16563. autoload :TagAssertions, 'action_dispatch/testing/assertions/tag'
  16564. extend ActiveSupport::Concern
  16565. include DomAssertions
  16566. include ResponseAssertions
  16567. include RoutingAssertions
  16568. include SelectorAssertions
  16569. include TagAssertions
  16570. end
  16571. end
  16572. require 'stringio'
  16573. require 'uri'
  16574. require 'active_support/core_ext/kernel/singleton_class'
  16575. require 'active_support/core_ext/object/try'
  16576. require 'rack/test'
  16577. require 'minitest/unit'
  16578. module ActionDispatch
  16579. module Integration #:nodoc:
  16580. module RequestHelpers
  16581. # Performs a GET request with the given parameters.
  16582. #
  16583. # - +path+: The URI (as a String) on which you want to perform a GET
  16584. # request.
  16585. # - +parameters+: The HTTP parameters that you want to pass. This may
  16586. # be +nil+,
  16587. # a Hash, or a String that is appropriately encoded
  16588. # (<tt>application/x-www-form-urlencoded</tt> or
  16589. # <tt>multipart/form-data</tt>).
  16590. # - +headers+: Additional headers to pass, as a Hash. The headers will be
  16591. # merged into the Rack env hash.
  16592. #
  16593. # This method returns a Response object, which one can use to
  16594. # inspect the details of the response. Furthermore, if this method was
  16595. # called from an ActionDispatch::IntegrationTest object, then that
  16596. # object's <tt>@response</tt> instance variable will point to the same
  16597. # response object.
  16598. #
  16599. # You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
  16600. # +#post+, +#patch+, +#put+, +#delete+, and +#head+.
  16601. def get(path, parameters = nil, headers = nil)
  16602. process :get, path, parameters, headers
  16603. end
  16604. # Performs a POST request with the given parameters. See +#get+ for more
  16605. # details.
  16606. def post(path, parameters = nil, headers = nil)
  16607. process :post, path, parameters, headers
  16608. end
  16609. # Performs a PATCH request with the given parameters. See +#get+ for more
  16610. # details.
  16611. def patch(path, parameters = nil, headers = nil)
  16612. process :patch, path, parameters, headers
  16613. end
  16614. # Performs a PUT request with the given parameters. See +#get+ for more
  16615. # details.
  16616. def put(path, parameters = nil, headers = nil)
  16617. process :put, path, parameters, headers
  16618. end
  16619. # Performs a DELETE request with the given parameters. See +#get+ for
  16620. # more details.
  16621. def delete(path, parameters = nil, headers = nil)
  16622. process :delete, path, parameters, headers
  16623. end
  16624. # Performs a HEAD request with the given parameters. See +#get+ for more
  16625. # details.
  16626. def head(path, parameters = nil, headers = nil)
  16627. process :head, path, parameters, headers
  16628. end
  16629. # Performs a OPTIONS request with the given parameters. See +#get+ for
  16630. # more details.
  16631. def options(path, parameters = nil, headers = nil)
  16632. process :options, path, parameters, headers
  16633. end
  16634. # Performs an XMLHttpRequest request with the given parameters, mirroring
  16635. # a request from the Prototype library.
  16636. #
  16637. # The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
  16638. # +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
  16639. # string; the headers are a hash.
  16640. def xml_http_request(request_method, path, parameters = nil, headers = nil)
  16641. headers ||= {}
  16642. headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
  16643. headers['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
  16644. process(request_method, path, parameters, headers)
  16645. end
  16646. alias xhr :xml_http_request
  16647. # Follow a single redirect response. If the last response was not a
  16648. # redirect, an exception will be raised. Otherwise, the redirect is
  16649. # performed on the location header.
  16650. def follow_redirect!
  16651. raise "not a redirect! #{status} #{status_message}" unless redirect?
  16652. get(response.location)
  16653. status
  16654. end
  16655. # Performs a request using the specified method, following any subsequent
  16656. # redirect. Note that the redirects are followed until the response is
  16657. # not a redirect--this means you may run into an infinite loop if your
  16658. # redirect loops back to itself.
  16659. def request_via_redirect(http_method, path, parameters = nil, headers = nil)
  16660. process(http_method, path, parameters, headers)
  16661. follow_redirect! while redirect?
  16662. status
  16663. end
  16664. # Performs a GET request, following any subsequent redirect.
  16665. # See +request_via_redirect+ for more information.
  16666. def get_via_redirect(path, parameters = nil, headers = nil)
  16667. request_via_redirect(:get, path, parameters, headers)
  16668. end
  16669. # Performs a POST request, following any subsequent redirect.
  16670. # See +request_via_redirect+ for more information.
  16671. def post_via_redirect(path, parameters = nil, headers = nil)
  16672. request_via_redirect(:post, path, parameters, headers)
  16673. end
  16674. # Performs a PATCH request, following any subsequent redirect.
  16675. # See +request_via_redirect+ for more information.
  16676. def patch_via_redirect(path, parameters = nil, headers = nil)
  16677. request_via_redirect(:patch, path, parameters, headers)
  16678. end
  16679. # Performs a PUT request, following any subsequent redirect.
  16680. # See +request_via_redirect+ for more information.
  16681. def put_via_redirect(path, parameters = nil, headers = nil)
  16682. request_via_redirect(:put, path, parameters, headers)
  16683. end
  16684. # Performs a DELETE request, following any subsequent redirect.
  16685. # See +request_via_redirect+ for more information.
  16686. def delete_via_redirect(path, parameters = nil, headers = nil)
  16687. request_via_redirect(:delete, path, parameters, headers)
  16688. end
  16689. end
  16690. # An instance of this class represents a set of requests and responses
  16691. # performed sequentially by a test process. Because you can instantiate
  16692. # multiple sessions and run them side-by-side, you can also mimic (to some
  16693. # limited extent) multiple simultaneous users interacting with your system.
  16694. #
  16695. # Typically, you will instantiate a new session using
  16696. # IntegrationTest#open_session, rather than instantiating
  16697. # Integration::Session directly.
  16698. class Session
  16699. DEFAULT_HOST = "www.example.com"
  16700. include MiniTest::Assertions
  16701. include TestProcess, RequestHelpers, Assertions
  16702. %w( status status_message headers body redirect? ).each do |method|
  16703. delegate method, :to => :response, :allow_nil => true
  16704. end
  16705. %w( path ).each do |method|
  16706. delegate method, :to => :request, :allow_nil => true
  16707. end
  16708. # The hostname used in the last request.
  16709. def host
  16710. @host || DEFAULT_HOST
  16711. end
  16712. attr_writer :host
  16713. # The remote_addr used in the last request.
  16714. attr_accessor :remote_addr
  16715. # The Accept header to send.
  16716. attr_accessor :accept
  16717. # A map of the cookies returned by the last response, and which will be
  16718. # sent with the next request.
  16719. def cookies
  16720. _mock_session.cookie_jar
  16721. end
  16722. # A reference to the controller instance used by the last request.
  16723. attr_reader :controller
  16724. # A reference to the request instance used by the last request.
  16725. attr_reader :request
  16726. # A reference to the response instance used by the last request.
  16727. attr_reader :response
  16728. # A running counter of the number of requests processed.
  16729. attr_accessor :request_count
  16730. include ActionDispatch::Routing::UrlFor
  16731. # Create and initialize a new Session instance.
  16732. def initialize(app)
  16733. super()
  16734. @app = app
  16735. # If the app is a Rails app, make url_helpers available on the session
  16736. # This makes app.url_for and app.foo_path available in the console
  16737. if app.respond_to?(:routes)
  16738. singleton_class.class_eval do
  16739. include app.routes.url_helpers if app.routes.respond_to?(:url_helpers)
  16740. include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers)
  16741. end
  16742. end
  16743. reset!
  16744. end
  16745. def url_options
  16746. @url_options ||= default_url_options.dup.tap do |url_options|
  16747. url_options.reverse_merge!(controller.url_options) if controller
  16748. if @app.respond_to?(:routes) && @app.routes.respond_to?(:default_url_options)
  16749. url_options.reverse_merge!(@app.routes.default_url_options)
  16750. end
  16751. url_options.reverse_merge!(:host => host, :protocol => https? ? "https" : "http")
  16752. end
  16753. end
  16754. # Resets the instance. This can be used to reset the state information
  16755. # in an existing session instance, so it can be used from a clean-slate
  16756. # condition.
  16757. #
  16758. # session.reset!
  16759. def reset!
  16760. @https = false
  16761. @controller = @request = @response = nil
  16762. @_mock_session = nil
  16763. @request_count = 0
  16764. @url_options = nil
  16765. self.host = DEFAULT_HOST
  16766. self.remote_addr = "127.0.0.1"
  16767. self.accept = "text/xml,application/xml,application/xhtml+xml," +
  16768. "text/html;q=0.9,text/plain;q=0.8,image/png," +
  16769. "*/*;q=0.5"
  16770. unless defined? @named_routes_configured
  16771. # the helpers are made protected by default--we make them public for
  16772. # easier access during testing and troubleshooting.
  16773. @named_routes_configured = true
  16774. end
  16775. end
  16776. # Specify whether or not the session should mimic a secure HTTPS request.
  16777. #
  16778. # session.https!
  16779. # session.https!(false)
  16780. def https!(flag = true)
  16781. @https = flag
  16782. end
  16783. # Return +true+ if the session is mimicking a secure HTTPS request.
  16784. #
  16785. # if session.https?
  16786. # ...
  16787. # end
  16788. def https?
  16789. @https
  16790. end
  16791. # Set the host name to use in the next request.
  16792. #
  16793. # session.host! "www.example.com"
  16794. alias :host! :host=
  16795. private
  16796. def _mock_session
  16797. @_mock_session ||= Rack::MockSession.new(@app, host)
  16798. end
  16799. # Performs the actual request.
  16800. def process(method, path, parameters = nil, rack_env = nil)
  16801. rack_env ||= {}
  16802. if path =~ %r{://}
  16803. location = URI.parse(path)
  16804. https! URI::HTTPS === location if location.scheme
  16805. host! "#{location.host}:#{location.port}" if location.host
  16806. path = location.query ? "#{location.path}?#{location.query}" : location.path
  16807. end
  16808. unless ActionController::Base < ActionController::Testing
  16809. ActionController::Base.class_eval do
  16810. include ActionController::Testing
  16811. end
  16812. end
  16813. hostname, port = host.split(':')
  16814. env = {
  16815. :method => method,
  16816. :params => parameters,
  16817. "SERVER_NAME" => hostname,
  16818. "SERVER_PORT" => port || (https? ? "443" : "80"),
  16819. "HTTPS" => https? ? "on" : "off",
  16820. "rack.url_scheme" => https? ? "https" : "http",
  16821. "REQUEST_URI" => path,
  16822. "HTTP_HOST" => host,
  16823. "REMOTE_ADDR" => remote_addr,
  16824. "CONTENT_TYPE" => "application/x-www-form-urlencoded",
  16825. "HTTP_ACCEPT" => accept
  16826. }
  16827. session = Rack::Test::Session.new(_mock_session)
  16828. env.merge!(rack_env)
  16829. # NOTE: rack-test v0.5 doesn't build a default uri correctly
  16830. # Make sure requested path is always a full uri
  16831. uri = URI.parse('/')
  16832. uri.scheme ||= env['rack.url_scheme']
  16833. uri.host ||= env['SERVER_NAME']
  16834. uri.port ||= env['SERVER_PORT'].try(:to_i)
  16835. uri += path
  16836. session.request(uri.to_s, env)
  16837. @request_count += 1
  16838. @request = ActionDispatch::Request.new(session.last_request.env)
  16839. response = _mock_session.last_response
  16840. @response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
  16841. @html_document = nil
  16842. @url_options = nil
  16843. @controller = session.last_request.env['action_controller.instance']
  16844. return response.status
  16845. end
  16846. end
  16847. module Runner
  16848. include ActionDispatch::Assertions
  16849. def app
  16850. @app ||= nil
  16851. end
  16852. # Reset the current session. This is useful for testing multiple sessions
  16853. # in a single test case.
  16854. def reset!
  16855. @integration_session = Integration::Session.new(app)
  16856. end
  16857. %w(get post patch put head delete options cookies assigns
  16858. xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
  16859. define_method(method) do |*args|
  16860. reset! unless integration_session
  16861. # reset the html_document variable, but only for new get/post calls
  16862. @html_document = nil unless method == 'cookies' || method == 'assigns'
  16863. integration_session.__send__(method, *args).tap do
  16864. copy_session_variables!
  16865. end
  16866. end
  16867. end
  16868. # Open a new session instance. If a block is given, the new session is
  16869. # yielded to the block before being returned.
  16870. #
  16871. # session = open_session do |sess|
  16872. # sess.extend(CustomAssertions)
  16873. # end
  16874. #
  16875. # By default, a single session is automatically created for you, but you
  16876. # can use this method to open multiple sessions that ought to be tested
  16877. # simultaneously.
  16878. def open_session(app = nil)
  16879. dup.tap do |session|
  16880. yield session if block_given?
  16881. end
  16882. end
  16883. # Copy the instance variables from the current session instance into the
  16884. # test instance.
  16885. def copy_session_variables! #:nodoc:
  16886. return unless integration_session
  16887. %w(controller response request).each do |var|
  16888. instance_variable_set("@#{var}", @integration_session.__send__(var))
  16889. end
  16890. end
  16891. def default_url_options
  16892. reset! unless integration_session
  16893. integration_session.default_url_options
  16894. end
  16895. def default_url_options=(options)
  16896. reset! unless integration_session
  16897. integration_session.default_url_options = options
  16898. end
  16899. def respond_to?(method, include_private = false)
  16900. integration_session.respond_to?(method, include_private) || super
  16901. end
  16902. # Delegate unhandled messages to the current session instance.
  16903. def method_missing(sym, *args, &block)
  16904. reset! unless integration_session
  16905. if integration_session.respond_to?(sym)
  16906. integration_session.__send__(sym, *args, &block).tap do
  16907. copy_session_variables!
  16908. end
  16909. else
  16910. super
  16911. end
  16912. end
  16913. private
  16914. def integration_session
  16915. @integration_session ||= nil
  16916. end
  16917. end
  16918. end
  16919. # An integration test spans multiple controllers and actions,
  16920. # tying them all together to ensure they work together as expected. It tests
  16921. # more completely than either unit or functional tests do, exercising the
  16922. # entire stack, from the dispatcher to the database.
  16923. #
  16924. # At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
  16925. # using the get/post methods:
  16926. #
  16927. # require "test_helper"
  16928. #
  16929. # class ExampleTest < ActionDispatch::IntegrationTest
  16930. # fixtures :people
  16931. #
  16932. # def test_login
  16933. # # get the login page
  16934. # get "/login"
  16935. # assert_equal 200, status
  16936. #
  16937. # # post the login and follow through to the home page
  16938. # post "/login", username: people(:jamis).username,
  16939. # password: people(:jamis).password
  16940. # follow_redirect!
  16941. # assert_equal 200, status
  16942. # assert_equal "/home", path
  16943. # end
  16944. # end
  16945. #
  16946. # However, you can also have multiple session instances open per test, and
  16947. # even extend those instances with assertions and methods to create a very
  16948. # powerful testing DSL that is specific for your application. You can even
  16949. # reference any named routes you happen to have defined.
  16950. #
  16951. # require "test_helper"
  16952. #
  16953. # class AdvancedTest < ActionDispatch::IntegrationTest
  16954. # fixtures :people, :rooms
  16955. #
  16956. # def test_login_and_speak
  16957. # jamis, david = login(:jamis), login(:david)
  16958. # room = rooms(:office)
  16959. #
  16960. # jamis.enter(room)
  16961. # jamis.speak(room, "anybody home?")
  16962. #
  16963. # david.enter(room)
  16964. # david.speak(room, "hello!")
  16965. # end
  16966. #
  16967. # private
  16968. #
  16969. # module CustomAssertions
  16970. # def enter(room)
  16971. # # reference a named route, for maximum internal consistency!
  16972. # get(room_url(id: room.id))
  16973. # assert(...)
  16974. # ...
  16975. # end
  16976. #
  16977. # def speak(room, message)
  16978. # xml_http_request "/say/#{room.id}", message: message
  16979. # assert(...)
  16980. # ...
  16981. # end
  16982. # end
  16983. #
  16984. # def login(who)
  16985. # open_session do |sess|
  16986. # sess.extend(CustomAssertions)
  16987. # who = people(who)
  16988. # sess.post "/login", username: who.username,
  16989. # password: who.password
  16990. # assert(...)
  16991. # end
  16992. # end
  16993. # end
  16994. class IntegrationTest < ActiveSupport::TestCase
  16995. include Integration::Runner
  16996. include ActionController::TemplateAssertions
  16997. include ActionDispatch::Routing::UrlFor
  16998. @@app = nil
  16999. def self.app
  17000. if !@@app && !ActionDispatch.test_app
  17001. ActiveSupport::Deprecation.warn "Rails application fallback is deprecated and no longer works, please set ActionDispatch.test_app"
  17002. end
  17003. @@app || ActionDispatch.test_app
  17004. end
  17005. def self.app=(app)
  17006. @@app = app
  17007. end
  17008. def app
  17009. super || self.class.app
  17010. end
  17011. def url_options
  17012. reset! unless integration_session
  17013. integration_session.url_options
  17014. end
  17015. end
  17016. end
  17017. require 'action_dispatch/middleware/cookies'
  17018. require 'action_dispatch/middleware/flash'
  17019. require 'active_support/core_ext/hash/indifferent_access'
  17020. module ActionDispatch
  17021. module TestProcess
  17022. def assigns(key = nil)
  17023. assigns = {}.with_indifferent_access
  17024. @controller.view_assigns.each {|k, v| assigns.regular_writer(k, v)}
  17025. key.nil? ? assigns : assigns[key]
  17026. end
  17027. def session
  17028. @request.session
  17029. end
  17030. def flash
  17031. @request.flash
  17032. end
  17033. def cookies
  17034. @request.cookie_jar
  17035. end
  17036. def redirect_to_url
  17037. @response.redirect_url
  17038. end
  17039. # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionController::TestCase.fixture_path, path), type)</tt>:
  17040. #
  17041. # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png')
  17042. #
  17043. # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
  17044. # This will not affect other platforms:
  17045. #
  17046. # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png', :binary)
  17047. def fixture_file_upload(path, mime_type = nil, binary = false)
  17048. if self.class.respond_to?(:fixture_path) && self.class.fixture_path
  17049. path = File.join(self.class.fixture_path, path)
  17050. end
  17051. Rack::Test::UploadedFile.new(path, mime_type, binary)
  17052. end
  17053. end
  17054. end
  17055. require 'active_support/core_ext/hash/indifferent_access'
  17056. require 'rack/utils'
  17057. module ActionDispatch
  17058. class TestRequest < Request
  17059. DEFAULT_ENV = Rack::MockRequest.env_for('/')
  17060. def self.new(env = {})
  17061. super
  17062. end
  17063. def initialize(env = {})
  17064. env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
  17065. super(default_env.merge(env))
  17066. self.host = 'test.host'
  17067. self.remote_addr = '0.0.0.0'
  17068. self.user_agent = 'Rails Testing'
  17069. end
  17070. def request_method=(method)
  17071. @env['REQUEST_METHOD'] = method.to_s.upcase
  17072. end
  17073. def host=(host)
  17074. @env['HTTP_HOST'] = host
  17075. end
  17076. def port=(number)
  17077. @env['SERVER_PORT'] = number.to_i
  17078. end
  17079. def request_uri=(uri)
  17080. @env['REQUEST_URI'] = uri
  17081. end
  17082. def path=(path)
  17083. @env['PATH_INFO'] = path
  17084. end
  17085. def action=(action_name)
  17086. path_parameters["action"] = action_name.to_s
  17087. end
  17088. def if_modified_since=(last_modified)
  17089. @env['HTTP_IF_MODIFIED_SINCE'] = last_modified
  17090. end
  17091. def if_none_match=(etag)
  17092. @env['HTTP_IF_NONE_MATCH'] = etag
  17093. end
  17094. def remote_addr=(addr)
  17095. @env['REMOTE_ADDR'] = addr
  17096. end
  17097. def user_agent=(user_agent)
  17098. @env['HTTP_USER_AGENT'] = user_agent
  17099. end
  17100. def accept=(mime_types)
  17101. @env.delete('action_dispatch.request.accepts')
  17102. @env['HTTP_ACCEPT'] = Array(mime_types).collect { |mime_type| mime_type.to_s }.join(",")
  17103. end
  17104. alias :rack_cookies :cookies
  17105. def cookies
  17106. @cookies ||= {}.with_indifferent_access
  17107. end
  17108. private
  17109. def default_env
  17110. DEFAULT_ENV
  17111. end
  17112. end
  17113. end
  17114. module ActionDispatch
  17115. # Integration test methods such as ActionDispatch::Integration::Session#get
  17116. # and ActionDispatch::Integration::Session#post return objects of class
  17117. # TestResponse, which represent the HTTP response results of the requested
  17118. # controller actions.
  17119. #
  17120. # See Response for more information on controller response objects.
  17121. class TestResponse < Response
  17122. def self.from_response(response)
  17123. new.tap do |resp|
  17124. resp.status = response.status
  17125. resp.headers = response.headers
  17126. resp.body = response.body
  17127. end
  17128. end
  17129. # Was the response successful?
  17130. alias_method :success?, :successful?
  17131. # Was the URL not found?
  17132. alias_method :missing?, :not_found?
  17133. # Were we redirected?
  17134. alias_method :redirect?, :redirection?
  17135. # Was there a server-side error?
  17136. alias_method :error?, :server_error?
  17137. end
  17138. end
  17139. #--
  17140. # Copyright (c) 2004-2013 David Heinemeier Hansson
  17141. #
  17142. # Permission is hereby granted, free of charge, to any person obtaining
  17143. # a copy of this software and associated documentation files (the
  17144. # "Software"), to deal in the Software without restriction, including
  17145. # without limitation the rights to use, copy, modify, merge, publish,
  17146. # distribute, sublicense, and/or sell copies of the Software, and to
  17147. # permit persons to whom the Software is furnished to do so, subject to
  17148. # the following conditions:
  17149. #
  17150. # The above copyright notice and this permission notice shall be
  17151. # included in all copies or substantial portions of the Software.
  17152. #
  17153. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17154. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17155. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17156. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  17157. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17158. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  17159. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  17160. #++
  17161. require 'active_support'
  17162. require 'active_support/rails'
  17163. require 'active_support/core_ext/module/attribute_accessors'
  17164. require 'action_pack'
  17165. require 'rack'
  17166. module Rack
  17167. autoload :Test, 'rack/test'
  17168. end
  17169. module ActionDispatch
  17170. extend ActiveSupport::Autoload
  17171. class IllegalStateError < StandardError
  17172. end
  17173. eager_autoload do
  17174. autoload_under 'http' do
  17175. autoload :Request
  17176. autoload :Response
  17177. end
  17178. end
  17179. autoload_under 'middleware' do
  17180. autoload :RequestId
  17181. autoload :Callbacks
  17182. autoload :Cookies
  17183. autoload :DebugExceptions
  17184. autoload :ExceptionWrapper
  17185. autoload :Flash
  17186. autoload :Head
  17187. autoload :ParamsParser
  17188. autoload :PublicExceptions
  17189. autoload :Reloader
  17190. autoload :RemoteIp
  17191. autoload :ShowExceptions
  17192. autoload :SSL
  17193. autoload :Static
  17194. end
  17195. autoload :Journey
  17196. autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
  17197. autoload :Routing
  17198. module Http
  17199. extend ActiveSupport::Autoload
  17200. autoload :Cache
  17201. autoload :Headers
  17202. autoload :MimeNegotiation
  17203. autoload :Parameters
  17204. autoload :ParameterFilter
  17205. autoload :FilterParameters
  17206. autoload :FilterRedirect
  17207. autoload :Upload
  17208. autoload :UploadedFile, 'action_dispatch/http/upload'
  17209. autoload :URL
  17210. end
  17211. module Session
  17212. autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
  17213. autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
  17214. autoload :EncryptedCookieStore, 'action_dispatch/middleware/session/cookie_store'
  17215. autoload :UpgradeSignatureToEncryptionCookieStore, 'action_dispatch/middleware/session/cookie_store'
  17216. autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
  17217. autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
  17218. end
  17219. mattr_accessor :test_app
  17220. autoload_under 'testing' do
  17221. autoload :Assertions
  17222. autoload :Integration
  17223. autoload :IntegrationTest, 'action_dispatch/testing/integration'
  17224. autoload :TestProcess
  17225. autoload :TestRequest
  17226. autoload :TestResponse
  17227. end
  17228. end
  17229. autoload :Mime, 'action_dispatch/http/mime_type'
  17230. ActiveSupport.on_load(:action_view) do
  17231. ActionView::Base.default_formats ||= Mime::SET.symbols
  17232. ActionView::Template::Types.delegate_to Mime
  17233. end
  17234. module ActionPack
  17235. module VERSION #:nodoc:
  17236. MAJOR = 4
  17237. MINOR = 0
  17238. TINY = 0
  17239. PRE = "beta"
  17240. STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
  17241. end
  17242. end
  17243. #--
  17244. # Copyright (c) 2004-2013 David Heinemeier Hansson
  17245. #
  17246. # Permission is hereby granted, free of charge, to any person obtaining
  17247. # a copy of this software and associated documentation files (the
  17248. # "Software"), to deal in the Software without restriction, including
  17249. # without limitation the rights to use, copy, modify, merge, publish,
  17250. # distribute, sublicense, and/or sell copies of the Software, and to
  17251. # permit persons to whom the Software is furnished to do so, subject to
  17252. # the following conditions:
  17253. #
  17254. # The above copyright notice and this permission notice shall be
  17255. # included in all copies or substantial portions of the Software.
  17256. #
  17257. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17258. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17259. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17260. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  17261. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17262. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  17263. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  17264. #++
  17265. require 'action_pack/version'
  17266. require 'active_support/core_ext/module/attr_internal'
  17267. require 'active_support/core_ext/class/attribute_accessors'
  17268. require 'active_support/ordered_options'
  17269. require 'action_view/log_subscriber'
  17270. module ActionView #:nodoc:
  17271. # = Action View Base
  17272. #
  17273. # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERB
  17274. # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> extension then Jim Weirich's Builder::XmlMarkup library is used.
  17275. #
  17276. # == ERB
  17277. #
  17278. # You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
  17279. # following loop for names:
  17280. #
  17281. # <b>Names of all the people</b>
  17282. # <% @people.each do |person| %>
  17283. # Name: <%= person.name %><br/>
  17284. # <% end %>
  17285. #
  17286. # The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
  17287. # is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
  17288. #
  17289. # <%# WRONG %>
  17290. # Hi, Mr. <% puts "Frodo" %>
  17291. #
  17292. # If you absolutely must write from within a function use +concat+.
  17293. #
  17294. # <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
  17295. #
  17296. # === Using sub templates
  17297. #
  17298. # Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
  17299. # classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
  17300. #
  17301. # <%= render "shared/header" %>
  17302. # Something really specific and terrific
  17303. # <%= render "shared/footer" %>
  17304. #
  17305. # As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
  17306. # result of the rendering. The output embedding writes it to the current template.
  17307. #
  17308. # But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
  17309. # variables defined using the regular embedding tags. Like this:
  17310. #
  17311. # <% @page_title = "A Wonderful Hello" %>
  17312. # <%= render "shared/header" %>
  17313. #
  17314. # Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
  17315. #
  17316. # <title><%= @page_title %></title>
  17317. #
  17318. # === Passing local variables to sub templates
  17319. #
  17320. # You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
  17321. #
  17322. # <%= render "shared/header", { headline: "Welcome", person: person } %>
  17323. #
  17324. # These can now be accessed in <tt>shared/header</tt> with:
  17325. #
  17326. # Headline: <%= headline %>
  17327. # First name: <%= person.first_name %>
  17328. #
  17329. # If you need to find out whether a certain local variable has been assigned a value in a particular render call,
  17330. # you need to use the following pattern:
  17331. #
  17332. # <% if local_assigns.has_key? :headline %>
  17333. # Headline: <%= headline %>
  17334. # <% end %>
  17335. #
  17336. # Testing using <tt>defined? headline</tt> will not work. This is an implementation restriction.
  17337. #
  17338. # === Template caching
  17339. #
  17340. # By default, Rails will compile each template to a method in order to render it. When you alter a template,
  17341. # Rails will check the file's modification time and recompile it in development mode.
  17342. #
  17343. # == Builder
  17344. #
  17345. # Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object
  17346. # named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
  17347. #
  17348. # Here are some basic examples:
  17349. #
  17350. # xml.em("emphasized") # => <em>emphasized</em>
  17351. # xml.em { xml.b("emph & bold") } # => <em><b>emph &amp; bold</b></em>
  17352. # xml.a("A Link", "href" => "http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
  17353. # xml.target("name" => "compile", "option" => "fast") # => <target option="fast" name="compile"\>
  17354. # # NOTE: order of attributes is not specified.
  17355. #
  17356. # Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
  17357. #
  17358. # xml.div do
  17359. # xml.h1(@person.name)
  17360. # xml.p(@person.bio)
  17361. # end
  17362. #
  17363. # would produce something like:
  17364. #
  17365. # <div>
  17366. # <h1>David Heinemeier Hansson</h1>
  17367. # <p>A product of Danish Design during the Winter of '79...</p>
  17368. # </div>
  17369. #
  17370. # A full-length RSS example actually used on Basecamp:
  17371. #
  17372. # xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
  17373. # xml.channel do
  17374. # xml.title(@feed_title)
  17375. # xml.link(@url)
  17376. # xml.description "Basecamp: Recent items"
  17377. # xml.language "en-us"
  17378. # xml.ttl "40"
  17379. #
  17380. # @recent_items.each do |item|
  17381. # xml.item do
  17382. # xml.title(item_title(item))
  17383. # xml.description(item_description(item)) if item_description(item)
  17384. # xml.pubDate(item_pubDate(item))
  17385. # xml.guid(@person.firm.account.url + @recent_items.url(item))
  17386. # xml.link(@person.firm.account.url + @recent_items.url(item))
  17387. #
  17388. # xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
  17389. # end
  17390. # end
  17391. # end
  17392. # end
  17393. #
  17394. # More builder documentation can be found at http://builder.rubyforge.org.
  17395. class Base
  17396. include Helpers, ::ERB::Util, Context
  17397. # Specify the proc used to decorate input tags that refer to attributes with errors.
  17398. cattr_accessor :field_error_proc
  17399. @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
  17400. # How to complete the streaming when an exception occurs.
  17401. # This is our best guess: first try to close the attribute, then the tag.
  17402. cattr_accessor :streaming_completion_on_exception
  17403. @@streaming_completion_on_exception = %("><script>window.location = "/500.html"</script></html>)
  17404. # Specify whether rendering within namespaced controllers should prefix
  17405. # the partial paths for ActiveModel objects with the namespace.
  17406. # (e.g., an Admin::PostsController would render @post using /admin/posts/_post.erb)
  17407. cattr_accessor :prefix_partial_path_with_controller_namespace
  17408. @@prefix_partial_path_with_controller_namespace = true
  17409. # Specify default_formats that can be rendered.
  17410. cattr_accessor :default_formats
  17411. class_attribute :_routes
  17412. class_attribute :logger
  17413. class << self
  17414. delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
  17415. def cache_template_loading
  17416. ActionView::Resolver.caching?
  17417. end
  17418. def cache_template_loading=(value)
  17419. ActionView::Resolver.caching = value
  17420. end
  17421. def xss_safe? #:nodoc:
  17422. true
  17423. end
  17424. end
  17425. attr_accessor :view_renderer
  17426. attr_internal :config, :assigns
  17427. delegate :lookup_context, :to => :view_renderer
  17428. delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
  17429. def assign(new_assigns) # :nodoc:
  17430. @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
  17431. end
  17432. def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
  17433. @_config = ActiveSupport::InheritableOptions.new
  17434. if context.is_a?(ActionView::Renderer)
  17435. @view_renderer = context
  17436. else
  17437. lookup_context = context.is_a?(ActionView::LookupContext) ?
  17438. context : ActionView::LookupContext.new(context)
  17439. lookup_context.formats = formats if formats
  17440. lookup_context.prefixes = controller._prefixes if controller
  17441. @view_renderer = ActionView::Renderer.new(lookup_context)
  17442. end
  17443. assign(assigns)
  17444. assign_controller(controller)
  17445. _prepare_context
  17446. end
  17447. ActiveSupport.run_load_hooks(:action_view, self)
  17448. end
  17449. end
  17450. require 'active_support/core_ext/string/output_safety'
  17451. module ActionView
  17452. class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
  17453. def initialize(*)
  17454. super
  17455. encode!
  17456. end
  17457. def <<(value)
  17458. super(value.to_s)
  17459. end
  17460. alias :append= :<<
  17461. alias :safe_append= :safe_concat
  17462. end
  17463. class StreamingBuffer #:nodoc:
  17464. def initialize(block)
  17465. @block = block
  17466. end
  17467. def <<(value)
  17468. value = value.to_s
  17469. value = ERB::Util.h(value) unless value.html_safe?
  17470. @block.call(value)
  17471. end
  17472. alias :concat :<<
  17473. alias :append= :<<
  17474. def safe_concat(value)
  17475. @block.call(value.to_s)
  17476. end
  17477. alias :safe_append= :safe_concat
  17478. def html_safe?
  17479. true
  17480. end
  17481. def html_safe
  17482. self
  17483. end
  17484. end
  17485. end
  17486. module ActionView
  17487. module CompiledTemplates #:nodoc:
  17488. # holds compiled template code
  17489. end
  17490. # = Action View Context
  17491. #
  17492. # Action View contexts are supplied to Action Controller to render a template.
  17493. # The default Action View context is ActionView::Base.
  17494. #
  17495. # In order to work with ActionController, a Context must just include this module.
  17496. # The initialization of the variables used by the context (@output_buffer, @view_flow,
  17497. # and @virtual_path) is responsibility of the object that includes this module
  17498. # (although you can call _prepare_context defined below).
  17499. module Context
  17500. include CompiledTemplates
  17501. attr_accessor :output_buffer, :view_flow
  17502. # Prepares the context by setting the appropriate instance variables.
  17503. # :api: plugin
  17504. def _prepare_context
  17505. @view_flow = OutputFlow.new
  17506. @output_buffer = nil
  17507. @virtual_path = nil
  17508. end
  17509. # Encapsulates the interaction with the view flow so it
  17510. # returns the correct buffer on +yield+. This is usually
  17511. # overwritten by helpers to add more behavior.
  17512. # :api: plugin
  17513. def _layout_for(name=nil)
  17514. name ||= :layout
  17515. view_flow.get(name).html_safe
  17516. end
  17517. end
  17518. end
  17519. require 'thread_safe'
  17520. module ActionView
  17521. class Digestor
  17522. EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
  17523. # Matches:
  17524. # render partial: "comments/comment", collection: commentable.comments
  17525. # render "comments/comments"
  17526. # render 'comments/comments'
  17527. # render('comments/comments')
  17528. #
  17529. # render(@topic) => render("topics/topic")
  17530. # render(topics) => render("topics/topic")
  17531. # render(message.topics) => render("topics/topic")
  17532. RENDER_DEPENDENCY = /
  17533. render\s* # render, followed by optional whitespace
  17534. \(? # start an optional parenthesis for the render call
  17535. (partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture
  17536. ([@a-z"'][@a-z_\/\."']+) # the template name itself -- 2nd capture
  17537. /x
  17538. cattr_reader(:cache)
  17539. @@cache = ThreadSafe::Cache.new
  17540. def self.digest(name, format, finder, options = {})
  17541. cache_key = [name, format] + Array.wrap(options[:dependencies])
  17542. @@cache[cache_key.join('.')] ||= begin
  17543. klass = options[:partial] || name.include?("/_") ? PartialDigestor : Digestor
  17544. klass.new(name, format, finder, options).digest
  17545. end
  17546. end
  17547. attr_reader :name, :format, :finder, :options
  17548. def initialize(name, format, finder, options={})
  17549. @name, @format, @finder, @options = name, format, finder, options
  17550. end
  17551. def digest
  17552. Digest::MD5.hexdigest("#{source}-#{dependency_digest}").tap do |digest|
  17553. logger.try :info, "Cache digest for #{name}.#{format}: #{digest}"
  17554. end
  17555. rescue ActionView::MissingTemplate
  17556. logger.try :error, "Couldn't find template for digesting: #{name}.#{format}"
  17557. ''
  17558. end
  17559. def dependencies
  17560. render_dependencies + explicit_dependencies
  17561. rescue ActionView::MissingTemplate
  17562. [] # File doesn't exist, so no dependencies
  17563. end
  17564. def nested_dependencies
  17565. dependencies.collect do |dependency|
  17566. dependencies = PartialDigestor.new(dependency, format, finder).nested_dependencies
  17567. dependencies.any? ? { dependency => dependencies } : dependency
  17568. end
  17569. end
  17570. private
  17571. def logger
  17572. ActionView::Base.logger
  17573. end
  17574. def logical_name
  17575. name.gsub(%r|/_|, "/")
  17576. end
  17577. def directory
  17578. name.split("/")[0..-2].join("/")
  17579. end
  17580. def partial?
  17581. false
  17582. end
  17583. def source
  17584. @source ||= finder.find(logical_name, [], partial?, formats: [ format ]).source
  17585. end
  17586. def dependency_digest
  17587. template_digests = dependencies.collect do |template_name|
  17588. Digestor.digest(template_name, format, finder, partial: true)
  17589. end
  17590. (template_digests + injected_dependencies).join("-")
  17591. end
  17592. def render_dependencies
  17593. source.scan(RENDER_DEPENDENCY).
  17594. collect(&:second).uniq.
  17595. # render(@topic) => render("topics/topic")
  17596. # render(topics) => render("topics/topic")
  17597. # render(message.topics) => render("topics/topic")
  17598. collect { |name| name.sub(/\A@?([a-z]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }.
  17599. # render("headline") => render("message/headline")
  17600. collect { |name| name.include?("/") ? name : "#{directory}/#{name}" }.
  17601. # replace quotes from string renders
  17602. collect { |name| name.gsub(/["']/, "") }
  17603. end
  17604. def explicit_dependencies
  17605. source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
  17606. end
  17607. def injected_dependencies
  17608. Array.wrap(options[:dependencies])
  17609. end
  17610. end
  17611. class PartialDigestor < Digestor # :nodoc:
  17612. def partial?
  17613. true
  17614. end
  17615. end
  17616. end
  17617. require 'active_support/core_ext/string/output_safety'
  17618. module ActionView
  17619. class OutputFlow #:nodoc:
  17620. attr_reader :content
  17621. def initialize
  17622. @content = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
  17623. end
  17624. # Called by _layout_for to read stored values.
  17625. def get(key)
  17626. @content[key]
  17627. end
  17628. # Called by each renderer object to set the layout contents.
  17629. def set(key, value)
  17630. @content[key] = value
  17631. end
  17632. # Called by content_for
  17633. def append(key, value)
  17634. @content[key] << value
  17635. end
  17636. alias_method :append!, :append
  17637. end
  17638. class StreamingFlow < OutputFlow #:nodoc:
  17639. def initialize(view, fiber)
  17640. @view = view
  17641. @parent = nil
  17642. @child = view.output_buffer
  17643. @content = view.view_flow.content
  17644. @fiber = fiber
  17645. @root = Fiber.current.object_id
  17646. end
  17647. # Try to get an stored content. If the content
  17648. # is not available and we are inside the layout
  17649. # fiber, we set that we are waiting for the given
  17650. # key and yield.
  17651. def get(key)
  17652. return super if @content.key?(key)
  17653. if inside_fiber?
  17654. view = @view
  17655. begin
  17656. @waiting_for = key
  17657. view.output_buffer, @parent = @child, view.output_buffer
  17658. Fiber.yield
  17659. ensure
  17660. @waiting_for = nil
  17661. view.output_buffer, @child = @parent, view.output_buffer
  17662. end
  17663. end
  17664. super
  17665. end
  17666. # Appends the contents for the given key. This is called
  17667. # by provides and resumes back to the fiber if it is
  17668. # the key it is waiting for.
  17669. def append!(key, value)
  17670. super
  17671. @fiber.resume if @waiting_for == key
  17672. end
  17673. private
  17674. def inside_fiber?
  17675. Fiber.current.object_id != @root
  17676. end
  17677. end
  17678. endrequire 'active_support/core_ext/class/attribute_accessors'
  17679. require 'active_support/core_ext/enumerable'
  17680. module ActionView
  17681. # = Active Model Helpers
  17682. module Helpers
  17683. module ActiveModelHelper
  17684. end
  17685. module ActiveModelInstanceTag
  17686. def object
  17687. @active_model_object ||= begin
  17688. object = super
  17689. object.respond_to?(:to_model) ? object.to_model : object
  17690. end
  17691. end
  17692. def content_tag(*)
  17693. error_wrapping(super)
  17694. end
  17695. def tag(type, options, *)
  17696. tag_generate_errors?(options) ? error_wrapping(super) : super
  17697. end
  17698. def error_wrapping(html_tag)
  17699. if object_has_errors?
  17700. Base.field_error_proc.call(html_tag, self)
  17701. else
  17702. html_tag
  17703. end
  17704. end
  17705. def error_message
  17706. object.errors[@method_name]
  17707. end
  17708. private
  17709. def object_has_errors?
  17710. object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
  17711. end
  17712. def tag_generate_errors?(options)
  17713. options['type'] != 'hidden'
  17714. end
  17715. end
  17716. end
  17717. end
  17718. require 'active_support/core_ext/array/extract_options'
  17719. require 'active_support/core_ext/hash/keys'
  17720. require 'action_view/helpers/asset_url_helper'
  17721. require 'action_view/helpers/tag_helper'
  17722. module ActionView
  17723. # = Action View Asset Tag Helpers
  17724. module Helpers #:nodoc:
  17725. # This module provides methods for generating HTML that links views to assets such
  17726. # as images, javascripts, stylesheets, and feeds. These methods do not verify
  17727. # the assets exist before linking to them:
  17728. #
  17729. # image_tag("rails.png")
  17730. # # => <img alt="Rails" src="/assets/rails.png" />
  17731. # stylesheet_link_tag("application")
  17732. # # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
  17733. #
  17734. module AssetTagHelper
  17735. extend ActiveSupport::Concern
  17736. include AssetUrlHelper
  17737. include TagHelper
  17738. # Returns an HTML script tag for each of the +sources+ provided.
  17739. #
  17740. # Sources may be paths to JavaScript files. Relative paths are assumed to be relative
  17741. # to <tt>assets/javascripts</tt>, full paths are assumed to be relative to the document
  17742. # root. Relative paths are idiomatic, use absolute paths only when needed.
  17743. #
  17744. # When passing paths, the ".js" extension is optional.
  17745. #
  17746. # You can modify the HTML attributes of the script tag by passing a hash as the
  17747. # last argument.
  17748. #
  17749. # When the Asset Pipeline is enabled, you can pass the name of your manifest as
  17750. # source, and include other JavaScript or CoffeeScript files inside the manifest.
  17751. #
  17752. # javascript_include_tag "xmlhr"
  17753. # # => <script src="/assets/xmlhr.js?1284139606"></script>
  17754. #
  17755. # javascript_include_tag "xmlhr.js"
  17756. # # => <script src="/assets/xmlhr.js?1284139606"></script>
  17757. #
  17758. # javascript_include_tag "common.javascript", "/elsewhere/cools"
  17759. # # => <script src="/assets/common.javascript?1284139606"></script>
  17760. # # <script src="/elsewhere/cools.js?1423139606"></script>
  17761. #
  17762. # javascript_include_tag "http://www.example.com/xmlhr"
  17763. # # => <script src="http://www.example.com/xmlhr"></script>
  17764. #
  17765. # javascript_include_tag "http://www.example.com/xmlhr.js"
  17766. # # => <script src="http://www.example.com/xmlhr.js"></script>
  17767. #
  17768. def javascript_include_tag(*sources)
  17769. options = sources.extract_options!.stringify_keys
  17770. path_options = options.extract!('protocol').symbolize_keys
  17771. sources.uniq.map { |source|
  17772. tag_options = {
  17773. "src" => path_to_javascript(source, path_options)
  17774. }.merge(options)
  17775. content_tag(:script, "", tag_options)
  17776. }.join("\n").html_safe
  17777. end
  17778. # Returns a stylesheet link tag for the sources specified as arguments. If
  17779. # you don't specify an extension, <tt>.css</tt> will be appended automatically.
  17780. # You can modify the link attributes by passing a hash as the last argument.
  17781. # For historical reasons, the 'media' attribute will always be present and defaults
  17782. # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to
  17783. # apply to all media types.
  17784. #
  17785. # stylesheet_link_tag "style"
  17786. # # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
  17787. #
  17788. # stylesheet_link_tag "style.css"
  17789. # # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
  17790. #
  17791. # stylesheet_link_tag "http://www.example.com/style.css"
  17792. # # => <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
  17793. #
  17794. # stylesheet_link_tag "style", media: "all"
  17795. # # => <link href="/assets/style.css" media="all" rel="stylesheet" />
  17796. #
  17797. # stylesheet_link_tag "style", media: "print"
  17798. # # => <link href="/assets/style.css" media="print" rel="stylesheet" />
  17799. #
  17800. # stylesheet_link_tag "random.styles", "/css/stylish"
  17801. # # => <link href="/assets/random.styles" media="screen" rel="stylesheet" />
  17802. # # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
  17803. #
  17804. def stylesheet_link_tag(*sources)
  17805. options = sources.extract_options!.stringify_keys
  17806. path_options = options.extract!('protocol').symbolize_keys
  17807. sources.uniq.map { |source|
  17808. tag_options = {
  17809. "rel" => "stylesheet",
  17810. "media" => "screen",
  17811. "href" => path_to_stylesheet(source, path_options)
  17812. }.merge(options)
  17813. tag(:link, tag_options)
  17814. }.join("\n").html_safe
  17815. end
  17816. # Returns a link tag that browsers and news readers can use to auto-detect
  17817. # an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
  17818. # <tt>:atom</tt>. Control the link options in url_for format using the
  17819. # +url_options+. You can modify the LINK tag itself in +tag_options+.
  17820. #
  17821. # ==== Options
  17822. # * <tt>:rel</tt> - Specify the relation of this link, defaults to "alternate"
  17823. # * <tt>:type</tt> - Override the auto-generated mime type
  17824. # * <tt>:title</tt> - Specify the title of the link, defaults to the +type+
  17825. #
  17826. # auto_discovery_link_tag
  17827. # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
  17828. # auto_discovery_link_tag(:atom)
  17829. # # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
  17830. # auto_discovery_link_tag(:rss, {action: "feed"})
  17831. # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
  17832. # auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
  17833. # # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
  17834. # auto_discovery_link_tag(:rss, {controller: "news", action: "feed"})
  17835. # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
  17836. # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
  17837. # # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
  17838. def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
  17839. if !(type == :rss || type == :atom) && tag_options[:type].blank?
  17840. message = "You have passed type other than :rss or :atom to auto_discovery_link_tag and haven't supplied " +
  17841. "the :type option key. This behavior is deprecated and will be remove in Rails 4.1. You should pass " +
  17842. ":type option explicitly if you want to use other types, for example: " +
  17843. "auto_discovery_link_tag(:xml, '/feed.xml', :type => 'application/xml')"
  17844. ActiveSupport::Deprecation.warn message
  17845. end
  17846. tag(
  17847. "link",
  17848. "rel" => tag_options[:rel] || "alternate",
  17849. "type" => tag_options[:type] || Mime::Type.lookup_by_extension(type.to_s).to_s,
  17850. "title" => tag_options[:title] || type.to_s.upcase,
  17851. "href" => url_options.is_a?(Hash) ? url_for(url_options.merge(:only_path => false)) : url_options
  17852. )
  17853. end
  17854. # Returns a link loading a favicon file. You may specify a different file
  17855. # in the first argument. The helper accepts an additional options hash where
  17856. # you can override "rel" and "type".
  17857. #
  17858. # ==== Options
  17859. # * <tt>:rel</tt> - Specify the relation of this link, defaults to 'shortcut icon'
  17860. # * <tt>:type</tt> - Override the auto-generated mime type, defaults to 'image/vnd.microsoft.icon'
  17861. #
  17862. # favicon_link_tag '/myicon.ico'
  17863. # # => <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
  17864. #
  17865. # Mobile Safari looks for a different <link> tag, pointing to an image that
  17866. # will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
  17867. # The following call would generate such a tag:
  17868. #
  17869. # favicon_link_tag '/mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
  17870. # # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
  17871. #
  17872. def favicon_link_tag(source='favicon.ico', options={})
  17873. tag('link', {
  17874. :rel => 'shortcut icon',
  17875. :type => 'image/vnd.microsoft.icon',
  17876. :href => path_to_image(source)
  17877. }.merge(options.symbolize_keys))
  17878. end
  17879. # Returns an HTML image tag for the +source+. The +source+ can be a full
  17880. # path or a file.
  17881. #
  17882. # ==== Options
  17883. # You can add HTML attributes using the +options+. The +options+ supports
  17884. # three additional keys for convenience and conformance:
  17885. #
  17886. # * <tt>:alt</tt> - If no alt text is given, the file name part of the
  17887. # +source+ is used (capitalized and without the extension)
  17888. # * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
  17889. # width="30" and height="45", and "50" becomes width="50" and height="50".
  17890. # <tt>:size</tt> will be ignored if the value is not in the correct format.
  17891. #
  17892. # ==== Examples
  17893. #
  17894. # image_tag("icon")
  17895. # # => <img alt="Icon" src="/assets/icon" />
  17896. # image_tag("icon.png")
  17897. # # => <img alt="Icon" src="/assets/icon.png" />
  17898. # image_tag("icon.png", size: "16x10", alt: "Edit Entry")
  17899. # # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
  17900. # image_tag("/icons/icon.gif", size: "16")
  17901. # # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
  17902. # image_tag("/icons/icon.gif", height: '32', width: '32')
  17903. # # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
  17904. # image_tag("/icons/icon.gif", class: "menu_icon")
  17905. # # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
  17906. def image_tag(source, options={})
  17907. options = options.symbolize_keys
  17908. src = options[:src] = path_to_image(source)
  17909. unless src =~ /^(?:cid|data):/ || src.blank?
  17910. options[:alt] = options.fetch(:alt){ image_alt(src) }
  17911. end
  17912. if size = options.delete(:size)
  17913. options[:width], options[:height] = size.split("x") if size =~ %r{\A\d+x\d+\z}
  17914. options[:width] = options[:height] = size if size =~ %r{\A\d+\z}
  17915. end
  17916. tag("img", options)
  17917. end
  17918. # Returns a string suitable for an html image tag alt attribute.
  17919. # The +src+ argument is meant to be an image file path.
  17920. # The method removes the basename of the file path and the digest,
  17921. # if any. It also removes hyphens and underscores from file names and
  17922. # replaces them with spaces, returning a space-separated, titleized
  17923. # string.
  17924. #
  17925. # ==== Examples
  17926. #
  17927. # image_tag('rails.png')
  17928. # # => <img alt="Rails" src="/assets/rails.png" />
  17929. #
  17930. # image_tag('hyphenated-file-name.png')
  17931. # # => <img alt="Hyphenated file name" src="/assets/hyphenated-file-name.png" />
  17932. #
  17933. # image_tag('underscored_file_name.png')
  17934. # # => <img alt="Underscored file name" src="/assets/underscored_file_name.png" />
  17935. def image_alt(src)
  17936. File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').tr('-_', ' ').capitalize
  17937. end
  17938. # Returns an html video tag for the +sources+. If +sources+ is a string,
  17939. # a single video tag will be returned. If +sources+ is an array, a video
  17940. # tag with nested source tags for each source will be returned. The
  17941. # +sources+ can be full paths or files that exists in your public videos
  17942. # directory.
  17943. #
  17944. # ==== Options
  17945. # You can add HTML attributes using the +options+. The +options+ supports
  17946. # two additional keys for convenience and conformance:
  17947. #
  17948. # * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
  17949. # before the video loads. The path is calculated like the +src+ of +image_tag+.
  17950. # * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
  17951. # width="30" and height="45". <tt>:size</tt> will be ignored if the
  17952. # value is not in the correct format.
  17953. #
  17954. # video_tag("trailer")
  17955. # # => <video src="/videos/trailer" />
  17956. # video_tag("trailer.ogg")
  17957. # # => <video src="/videos/trailer.ogg" />
  17958. # video_tag("trailer.ogg", controls: true, autobuffer: true)
  17959. # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
  17960. # video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
  17961. # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
  17962. # video_tag("/trailers/hd.avi", size: "16x16")
  17963. # # => <video src="/trailers/hd.avi" width="16" height="16" />
  17964. # video_tag("/trailers/hd.avi", height: '32', width: '32')
  17965. # # => <video height="32" src="/trailers/hd.avi" width="32" />
  17966. # video_tag("trailer.ogg", "trailer.flv")
  17967. # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
  17968. # video_tag(["trailer.ogg", "trailer.flv"])
  17969. # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
  17970. # video_tag(["trailer.ogg", "trailer.flv"], size: "160x120")
  17971. # # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
  17972. def video_tag(*sources)
  17973. multiple_sources_tag('video', sources) do |options|
  17974. options[:poster] = path_to_image(options[:poster]) if options[:poster]
  17975. if size = options.delete(:size)
  17976. options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
  17977. end
  17978. end
  17979. end
  17980. # Returns an HTML audio tag for the +source+.
  17981. # The +source+ can be full path or file that exists in
  17982. # your public audios directory.
  17983. #
  17984. # audio_tag("sound")
  17985. # # => <audio src="/audios/sound" />
  17986. # audio_tag("sound.wav")
  17987. # # => <audio src="/audios/sound.wav" />
  17988. # audio_tag("sound.wav", autoplay: true, controls: true)
  17989. # # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
  17990. # audio_tag("sound.wav", "sound.mid")
  17991. # # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
  17992. def audio_tag(*sources)
  17993. multiple_sources_tag('audio', sources)
  17994. end
  17995. private
  17996. def multiple_sources_tag(type, sources)
  17997. options = sources.extract_options!.symbolize_keys
  17998. sources.flatten!
  17999. yield options if block_given?
  18000. if sources.size > 1
  18001. content_tag(type, options) do
  18002. safe_join sources.map { |source| tag("source", :src => send("path_to_#{type}", source)) }
  18003. end
  18004. else
  18005. options[:src] = send("path_to_#{type}", sources.first)
  18006. content_tag(type, nil, options)
  18007. end
  18008. end
  18009. end
  18010. end
  18011. end
  18012. require 'zlib'
  18013. module ActionView
  18014. # = Action View Asset URL Helpers
  18015. module Helpers
  18016. # This module provides methods for generating asset paths and
  18017. # urls.
  18018. #
  18019. # image_path("rails.png")
  18020. # # => "/assets/rails.png"
  18021. #
  18022. # image_url("rails.png")
  18023. # # => "http://www.example.com/assets/rails.png"
  18024. #
  18025. # === Using asset hosts
  18026. #
  18027. # By default, Rails links to these assets on the current host in the public
  18028. # folder, but you can direct Rails to link to assets from a dedicated asset
  18029. # server by setting <tt>ActionController::Base.asset_host</tt> in the application
  18030. # configuration, typically in <tt>config/environments/production.rb</tt>.
  18031. # For example, you'd define <tt>assets.example.com</tt> to be your asset
  18032. # host this way, inside the <tt>configure</tt> block of your environment-specific
  18033. # configuration files or <tt>config/application.rb</tt>:
  18034. #
  18035. # config.action_controller.asset_host = "assets.example.com"
  18036. #
  18037. # Helpers take that into account:
  18038. #
  18039. # image_tag("rails.png")
  18040. # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
  18041. # stylesheet_link_tag("application")
  18042. # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
  18043. #
  18044. # Browsers typically open at most two simultaneous connections to a single
  18045. # host, which means your assets often have to wait for other assets to finish
  18046. # downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
  18047. # +asset_host+. For example, "assets%d.example.com". If that wildcard is
  18048. # present Rails distributes asset requests among the corresponding four hosts
  18049. # "assets0.example.com", ..., "assets3.example.com". With this trick browsers
  18050. # will open eight simultaneous connections rather than two.
  18051. #
  18052. # image_tag("rails.png")
  18053. # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
  18054. # stylesheet_link_tag("application")
  18055. # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
  18056. #
  18057. # To do this, you can either setup four actual hosts, or you can use wildcard
  18058. # DNS to CNAME the wildcard to a single asset host. You can read more about
  18059. # setting up your DNS CNAME records from your ISP.
  18060. #
  18061. # Note: This is purely a browser performance optimization and is not meant
  18062. # for server load balancing. See http://www.die.net/musings/page_load_time/
  18063. # for background.
  18064. #
  18065. # Alternatively, you can exert more control over the asset host by setting
  18066. # +asset_host+ to a proc like this:
  18067. #
  18068. # ActionController::Base.asset_host = Proc.new { |source|
  18069. # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
  18070. # }
  18071. # image_tag("rails.png")
  18072. # # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
  18073. # stylesheet_link_tag("application")
  18074. # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
  18075. #
  18076. # The example above generates "http://assets1.example.com" and
  18077. # "http://assets2.example.com". This option is useful for example if
  18078. # you need fewer/more than four hosts, custom host names, etc.
  18079. #
  18080. # As you see the proc takes a +source+ parameter. That's a string with the
  18081. # absolute path of the asset, for example "/assets/rails.png".
  18082. #
  18083. # ActionController::Base.asset_host = Proc.new { |source|
  18084. # if source.ends_with?('.css')
  18085. # "http://stylesheets.example.com"
  18086. # else
  18087. # "http://assets.example.com"
  18088. # end
  18089. # }
  18090. # image_tag("rails.png")
  18091. # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
  18092. # stylesheet_link_tag("application")
  18093. # # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
  18094. #
  18095. # Alternatively you may ask for a second parameter +request+. That one is
  18096. # particularly useful for serving assets from an SSL-protected page. The
  18097. # example proc below disables asset hosting for HTTPS connections, while
  18098. # still sending assets for plain HTTP requests from asset hosts. If you don't
  18099. # have SSL certificates for each of the asset hosts this technique allows you
  18100. # to avoid warnings in the client about mixed media.
  18101. #
  18102. # config.action_controller.asset_host = Proc.new { |source, request|
  18103. # if request.ssl?
  18104. # "#{request.protocol}#{request.host_with_port}"
  18105. # else
  18106. # "#{request.protocol}assets.example.com"
  18107. # end
  18108. # }
  18109. #
  18110. # You can also implement a custom asset host object that responds to +call+
  18111. # and takes either one or two parameters just like the proc.
  18112. #
  18113. # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
  18114. # "http://asset%d.example.com", "https://asset1.example.com"
  18115. # )
  18116. #
  18117. module AssetUrlHelper
  18118. URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
  18119. # Computes the path to asset in public directory. If :type
  18120. # options is set, a file extension will be appended and scoped
  18121. # to the corresponding public directory.
  18122. #
  18123. # All other asset *_path helpers delegate through this method.
  18124. #
  18125. # asset_path "application.js" # => /application.js
  18126. # asset_path "application", type: :javascript # => /javascripts/application.js
  18127. # asset_path "application", type: :stylesheet # => /stylesheets/application.css
  18128. # asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
  18129. def asset_path(source, options = {})
  18130. source = source.to_s
  18131. return "" unless source.present?
  18132. return source if source =~ URI_REGEXP
  18133. tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
  18134. if extname = compute_asset_extname(source, options)
  18135. source = "#{source}#{extname}"
  18136. end
  18137. if source[0] != ?/
  18138. source = compute_asset_path(source, options)
  18139. end
  18140. relative_url_root = defined?(config.relative_url_root) && config.relative_url_root
  18141. if relative_url_root
  18142. source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/")
  18143. end
  18144. if host = compute_asset_host(source, options)
  18145. source = "#{host}#{source}"
  18146. end
  18147. "#{source}#{tail}"
  18148. end
  18149. alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route
  18150. # Computes the full URL to a asset in the public directory. This
  18151. # will use +asset_path+ internally, so most of their behaviors
  18152. # will be the same.
  18153. def asset_url(source, options = {})
  18154. path_to_asset(source, options.merge(:protocol => :request))
  18155. end
  18156. alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route
  18157. ASSET_EXTENSIONS = {
  18158. javascript: '.js',
  18159. stylesheet: '.css'
  18160. }
  18161. # Compute extname to append to asset path. Returns nil if
  18162. # nothing should be added.
  18163. def compute_asset_extname(source, options = {})
  18164. return if options[:extname] == false
  18165. extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
  18166. extname if extname && File.extname(source) != extname
  18167. end
  18168. # Maps asset types to public directory.
  18169. ASSET_PUBLIC_DIRECTORIES = {
  18170. audio: '/audios',
  18171. font: '/fonts',
  18172. image: '/images',
  18173. javascript: '/javascripts',
  18174. stylesheet: '/stylesheets',
  18175. video: '/videos'
  18176. }
  18177. # Computes asset path to public directory. Plugins and
  18178. # extensions can override this method to point to custom assets
  18179. # or generate digested paths or query strings.
  18180. def compute_asset_path(source, options = {})
  18181. dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
  18182. File.join(dir, source)
  18183. end
  18184. # Pick an asset host for this source. Returns +nil+ if no host is set,
  18185. # the host if no wildcard is set, the host interpolated with the
  18186. # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
  18187. # or the value returned from invoking call on an object responding to call
  18188. # (proc or otherwise).
  18189. def compute_asset_host(source = "", options = {})
  18190. request = self.request if respond_to?(:request)
  18191. host = config.asset_host if defined? config.asset_host
  18192. host ||= request.base_url if request && options[:protocol] == :request
  18193. return unless host
  18194. if host.respond_to?(:call)
  18195. arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
  18196. args = [source]
  18197. args << request if request && (arity > 1 || arity < 0)
  18198. host = host.call(*args)
  18199. elsif host =~ /%d/
  18200. host = host % (Zlib.crc32(source) % 4)
  18201. end
  18202. if host =~ URI_REGEXP
  18203. host
  18204. else
  18205. protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
  18206. case protocol
  18207. when :relative
  18208. "//#{host}"
  18209. when :request
  18210. "#{request.protocol}#{host}"
  18211. else
  18212. "#{protocol}://#{host}"
  18213. end
  18214. end
  18215. end
  18216. # Computes the path to a javascript asset in the public javascripts directory.
  18217. # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
  18218. # Full paths from the document root will be passed through.
  18219. # Used internally by javascript_include_tag to build the script path.
  18220. #
  18221. # javascript_path "xmlhr" # => /javascripts/xmlhr.js
  18222. # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
  18223. # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
  18224. # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
  18225. # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
  18226. def javascript_path(source, options = {})
  18227. path_to_asset(source, {type: :javascript}.merge!(options))
  18228. end
  18229. alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
  18230. # Computes the full URL to a javascript asset in the public javascripts directory.
  18231. # This will use +javascript_path+ internally, so most of their behaviors will be the same.
  18232. def javascript_url(source, options = {})
  18233. url_to_asset(source, {type: :javascript}.merge!(options))
  18234. end
  18235. alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
  18236. # Computes the path to a stylesheet asset in the public stylesheets directory.
  18237. # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
  18238. # Full paths from the document root will be passed through.
  18239. # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
  18240. #
  18241. # stylesheet_path "style" # => /stylesheets/style.css
  18242. # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
  18243. # stylesheet_path "/dir/style.css" # => /dir/style.css
  18244. # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
  18245. # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
  18246. def stylesheet_path(source, options = {})
  18247. path_to_asset(source, {type: :stylesheet}.merge!(options))
  18248. end
  18249. alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
  18250. # Computes the full URL to a stylesheet asset in the public stylesheets directory.
  18251. # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
  18252. def stylesheet_url(source, options = {})
  18253. url_to_asset(source, {type: :stylesheet}.merge!(options))
  18254. end
  18255. alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
  18256. # Computes the path to an image asset.
  18257. # Full paths from the document root will be passed through.
  18258. # Used internally by +image_tag+ to build the image path:
  18259. #
  18260. # image_path("edit") # => "/assets/edit"
  18261. # image_path("edit.png") # => "/assets/edit.png"
  18262. # image_path("icons/edit.png") # => "/assets/icons/edit.png"
  18263. # image_path("/icons/edit.png") # => "/icons/edit.png"
  18264. # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
  18265. #
  18266. # If you have images as application resources this method may conflict with their named routes.
  18267. # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
  18268. # plugin authors are encouraged to do so.
  18269. def image_path(source, options = {})
  18270. path_to_asset(source, {type: :image}.merge!(options))
  18271. end
  18272. alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
  18273. # Computes the full URL to an image asset.
  18274. # This will use +image_path+ internally, so most of their behaviors will be the same.
  18275. def image_url(source, options = {})
  18276. url_to_asset(source, {type: :image}.merge!(options))
  18277. end
  18278. alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
  18279. # Computes the path to a video asset in the public videos directory.
  18280. # Full paths from the document root will be passed through.
  18281. # Used internally by +video_tag+ to build the video path.
  18282. #
  18283. # video_path("hd") # => /videos/hd
  18284. # video_path("hd.avi") # => /videos/hd.avi
  18285. # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
  18286. # video_path("/trailers/hd.avi") # => /trailers/hd.avi
  18287. # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
  18288. def video_path(source, options = {})
  18289. path_to_asset(source, {type: :video}.merge!(options))
  18290. end
  18291. alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
  18292. # Computes the full URL to a video asset in the public videos directory.
  18293. # This will use +video_path+ internally, so most of their behaviors will be the same.
  18294. def video_url(source, options = {})
  18295. url_to_asset(source, {type: :video}.merge!(options))
  18296. end
  18297. alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
  18298. # Computes the path to an audio asset in the public audios directory.
  18299. # Full paths from the document root will be passed through.
  18300. # Used internally by +audio_tag+ to build the audio path.
  18301. #
  18302. # audio_path("horse") # => /audios/horse
  18303. # audio_path("horse.wav") # => /audios/horse.wav
  18304. # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
  18305. # audio_path("/sounds/horse.wav") # => /sounds/horse.wav
  18306. # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
  18307. def audio_path(source, options = {})
  18308. path_to_asset(source, {type: :audio}.merge!(options))
  18309. end
  18310. alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
  18311. # Computes the full URL to an audio asset in the public audios directory.
  18312. # This will use +audio_path+ internally, so most of their behaviors will be the same.
  18313. def audio_url(source, options = {})
  18314. url_to_asset(source, {type: :audio}.merge!(options))
  18315. end
  18316. alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
  18317. # Computes the path to a font asset.
  18318. # Full paths from the document root will be passed through.
  18319. #
  18320. # font_path("font") # => /assets/font
  18321. # font_path("font.ttf") # => /assets/font.ttf
  18322. # font_path("dir/font.ttf") # => /assets/dir/font.ttf
  18323. # font_path("/dir/font.ttf") # => /dir/font.ttf
  18324. # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
  18325. def font_path(source, options = {})
  18326. path_to_asset(source, {type: :font}.merge!(options))
  18327. end
  18328. alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
  18329. # Computes the full URL to a font asset.
  18330. # This will use +font_path+ internally, so most of their behaviors will be the same.
  18331. def font_url(source, options = {})
  18332. url_to_asset(source, {type: :font}.merge!(options))
  18333. end
  18334. alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
  18335. end
  18336. end
  18337. end
  18338. require 'set'
  18339. module ActionView
  18340. # = Action View Atom Feed Helpers
  18341. module Helpers
  18342. module AtomFeedHelper
  18343. # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
  18344. # template languages).
  18345. #
  18346. # Full usage example:
  18347. #
  18348. # config/routes.rb:
  18349. # Basecamp::Application.routes.draw do
  18350. # resources :posts
  18351. # root to: "posts#index"
  18352. # end
  18353. #
  18354. # app/controllers/posts_controller.rb:
  18355. # class PostsController < ApplicationController::Base
  18356. # # GET /posts.html
  18357. # # GET /posts.atom
  18358. # def index
  18359. # @posts = Post.all
  18360. #
  18361. # respond_to do |format|
  18362. # format.html
  18363. # format.atom
  18364. # end
  18365. # end
  18366. # end
  18367. #
  18368. # app/views/posts/index.atom.builder:
  18369. # atom_feed do |feed|
  18370. # feed.title("My great blog!")
  18371. # feed.updated(@posts[0].created_at) if @posts.length > 0
  18372. #
  18373. # @posts.each do |post|
  18374. # feed.entry(post) do |entry|
  18375. # entry.title(post.title)
  18376. # entry.content(post.body, type: 'html')
  18377. #
  18378. # entry.author do |author|
  18379. # author.name("DHH")
  18380. # end
  18381. # end
  18382. # end
  18383. # end
  18384. #
  18385. # The options for atom_feed are:
  18386. #
  18387. # * <tt>:language</tt>: Defaults to "en-US".
  18388. # * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
  18389. # * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
  18390. # * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}"
  18391. # * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
  18392. # created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
  18393. # 2005 is used (as an "I don't care" value).
  18394. # * <tt>:instruct</tt>: Hash of XML processing instructions in the form {target => {attribute => value, }} or {target => [{attribute => value, }, ]}
  18395. #
  18396. # Other namespaces can be added to the root element:
  18397. #
  18398. # app/views/posts/index.atom.builder:
  18399. # atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
  18400. # 'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
  18401. # feed.title("My great blog!")
  18402. # feed.updated((@posts.first.created_at))
  18403. # feed.tag!(openSearch:totalResults, 10)
  18404. #
  18405. # @posts.each do |post|
  18406. # feed.entry(post) do |entry|
  18407. # entry.title(post.title)
  18408. # entry.content(post.body, type: 'html')
  18409. # entry.tag!('app:edited', Time.now)
  18410. #
  18411. # entry.author do |author|
  18412. # author.name("DHH")
  18413. # end
  18414. # end
  18415. # end
  18416. # end
  18417. #
  18418. # The Atom spec defines five elements (content rights title subtitle
  18419. # summary) which may directly contain xhtml content if type: 'xhtml'
  18420. # is specified as an attribute. If so, this helper will take care of
  18421. # the enclosing div and xhtml namespace declaration. Example usage:
  18422. #
  18423. # entry.summary type: 'xhtml' do |xhtml|
  18424. # xhtml.p pluralize(order.line_items.count, "line item")
  18425. # xhtml.p "Shipped to #{order.address}"
  18426. # xhtml.p "Paid by #{order.pay_type}"
  18427. # end
  18428. #
  18429. #
  18430. # <tt>atom_feed</tt> yields an +AtomFeedBuilder+ instance. Nested elements yield
  18431. # an +AtomBuilder+ instance.
  18432. def atom_feed(options = {}, &block)
  18433. if options[:schema_date]
  18434. options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
  18435. else
  18436. options[:schema_date] = "2005" # The Atom spec copyright date
  18437. end
  18438. xml = options.delete(:xml) || eval("xml", block.binding)
  18439. xml.instruct!
  18440. if options[:instruct]
  18441. options[:instruct].each do |target,attrs|
  18442. if attrs.respond_to?(:keys)
  18443. xml.instruct!(target, attrs)
  18444. elsif attrs.respond_to?(:each)
  18445. attrs.each { |attr_group| xml.instruct!(target, attr_group) }
  18446. end
  18447. end
  18448. end
  18449. feed_opts = {"xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom'}
  18450. feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
  18451. xml.feed(feed_opts) do
  18452. xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
  18453. xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
  18454. xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
  18455. yield AtomFeedBuilder.new(xml, self, options)
  18456. end
  18457. end
  18458. class AtomBuilder #:nodoc:
  18459. XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
  18460. def initialize(xml)
  18461. @xml = xml
  18462. end
  18463. private
  18464. # Delegate to xml builder, first wrapping the element in a xhtml
  18465. # namespaced div element if the method and arguments indicate
  18466. # that an xhtml_block? is desired.
  18467. def method_missing(method, *arguments, &block)
  18468. if xhtml_block?(method, arguments)
  18469. @xml.__send__(method, *arguments) do
  18470. @xml.div(:xmlns => 'http://www.w3.org/1999/xhtml') do |xhtml|
  18471. block.call(xhtml)
  18472. end
  18473. end
  18474. else
  18475. @xml.__send__(method, *arguments, &block)
  18476. end
  18477. end
  18478. # True if the method name matches one of the five elements defined
  18479. # in the Atom spec as potentially containing XHTML content and
  18480. # if type: 'xhtml' is, in fact, specified.
  18481. def xhtml_block?(method, arguments)
  18482. if XHTML_TAG_NAMES.include?(method.to_s)
  18483. last = arguments.last
  18484. last.is_a?(Hash) && last[:type].to_s == 'xhtml'
  18485. end
  18486. end
  18487. end
  18488. class AtomFeedBuilder < AtomBuilder #:nodoc:
  18489. def initialize(xml, view, feed_options = {})
  18490. @xml, @view, @feed_options = xml, view, feed_options
  18491. end
  18492. # Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
  18493. def updated(date_or_time = nil)
  18494. @xml.updated((date_or_time || Time.now.utc).xmlschema)
  18495. end
  18496. # Creates an entry tag for a specific record and prefills the id using class and id.
  18497. #
  18498. # Options:
  18499. #
  18500. # * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
  18501. # * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
  18502. # * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
  18503. # * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
  18504. # * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
  18505. def entry(record, options = {})
  18506. @xml.entry do
  18507. @xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
  18508. if options[:published] || (record.respond_to?(:created_at) && record.created_at)
  18509. @xml.published((options[:published] || record.created_at).xmlschema)
  18510. end
  18511. if options[:updated] || (record.respond_to?(:updated_at) && record.updated_at)
  18512. @xml.updated((options[:updated] || record.updated_at).xmlschema)
  18513. end
  18514. type = options.fetch(:type, 'text/html')
  18515. @xml.link(:rel => 'alternate', :type => type, :href => options[:url] || @view.polymorphic_url(record))
  18516. yield AtomBuilder.new(@xml)
  18517. end
  18518. end
  18519. end
  18520. end
  18521. end
  18522. end
  18523. module ActionView
  18524. # = Action View Cache Helper
  18525. module Helpers
  18526. module CacheHelper
  18527. # This helper exposes a method for caching fragments of a view
  18528. # rather than an entire action or page. This technique is useful
  18529. # caching pieces like menus, lists of newstopics, static HTML
  18530. # fragments, and so on. This method takes a block that contains
  18531. # the content you wish to cache.
  18532. #
  18533. # The best way to use this is by doing key-based cache expiration
  18534. # on top of a cache store like Memcached that'll automatically
  18535. # kick out old entries. For more on key-based expiration, see:
  18536. # http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
  18537. #
  18538. # When using this method, you list the cache dependency as the name of the cache, like so:
  18539. #
  18540. # <% cache project do %>
  18541. # <b>All the topics on this project</b>
  18542. # <%= render project.topics %>
  18543. # <% end %>
  18544. #
  18545. # This approach will assume that when a new topic is added, you'll touch
  18546. # the project. The cache key generated from this call will be something like:
  18547. #
  18548. # views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
  18549. # ^class ^id ^updated_at ^template tree digest
  18550. #
  18551. # The cache is thus automatically bumped whenever the project updated_at is touched.
  18552. #
  18553. # If your template cache depends on multiple sources (try to avoid this to keep things simple),
  18554. # you can name all these dependencies as part of an array:
  18555. #
  18556. # <% cache [ project, current_user ] do %>
  18557. # <b>All the topics on this project</b>
  18558. # <%= render project.topics %>
  18559. # <% end %>
  18560. #
  18561. # This will include both records as part of the cache key and updating either of them will
  18562. # expire the cache.
  18563. #
  18564. # ==== Template digest
  18565. #
  18566. # The template digest that's added to the cache key is computed by taking an md5 of the
  18567. # contents of the entire template file. This ensures that your caches will automatically
  18568. # expire when you change the template file.
  18569. #
  18570. # Note that the md5 is taken of the entire template file, not just what's within the
  18571. # cache do/end call. So it's possible that changing something outside of that call will
  18572. # still expire the cache.
  18573. #
  18574. # Additionally, the digestor will automatically look through your template file for
  18575. # explicit and implicit dependencies, and include those as part of the digest.
  18576. #
  18577. # The digestor can be bypassed by passing skip_digest: true as an option to the cache call:
  18578. #
  18579. # <% cache project, skip_digest: true do %>
  18580. # <b>All the topics on this project</b>
  18581. # <%= render project.topics %>
  18582. # <% end %>
  18583. #
  18584. # ==== Implicit dependencies
  18585. #
  18586. # Most template dependencies can be derived from calls to render in the template itself.
  18587. # Here are some examples of render calls that Cache Digests knows how to decode:
  18588. #
  18589. # render partial: "comments/comment", collection: commentable.comments
  18590. # render "comments/comments"
  18591. # render 'comments/comments'
  18592. # render('comments/comments')
  18593. #
  18594. # render "header" => render("comments/header")
  18595. #
  18596. # render(@topic) => render("topics/topic")
  18597. # render(topics) => render("topics/topic")
  18598. # render(message.topics) => render("topics/topic")
  18599. #
  18600. # It's not possible to derive all render calls like that, though. Here are a few examples of things that can't be derived:
  18601. #
  18602. # render group_of_attachments
  18603. # render @project.documents.where(published: true).order('created_at')
  18604. #
  18605. # You will have to rewrite those to the explicit form:
  18606. #
  18607. # render partial: 'attachments/attachment', collection: group_of_attachments
  18608. # render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
  18609. #
  18610. # === Explicit dependencies
  18611. #
  18612. # Some times you'll have template dependencies that can't be derived at all. This is typically
  18613. # the case when you have template rendering that happens in helpers. Here's an example:
  18614. #
  18615. # <%= render_sortable_todolists @project.todolists %>
  18616. #
  18617. # You'll need to use a special comment format to call those out:
  18618. #
  18619. # <%# Template Dependency: todolists/todolist %>
  18620. # <%= render_sortable_todolists @project.todolists %>
  18621. #
  18622. # The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
  18623. # You can only declare one template dependency per line.
  18624. #
  18625. # === External dependencies
  18626. #
  18627. # If you use a helper method, for example, inside of a cached block and you then update that helper,
  18628. # you'll have to bump the cache as well. It doesn't really matter how you do it, but the md5 of the template file
  18629. # must change. One recommendation is to simply be explicit in a comment, like:
  18630. #
  18631. # <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
  18632. # <%= some_helper_method(person) %>
  18633. #
  18634. # Now all you'll have to do is change that timestamp when the helper method changes.
  18635. def cache(name = {}, options = nil, &block)
  18636. if controller.perform_caching
  18637. safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
  18638. else
  18639. yield
  18640. end
  18641. nil
  18642. end
  18643. # Cache fragments of a view if +condition+ is true
  18644. #
  18645. # <%= cache_if admin?, project do %>
  18646. # <b>All the topics on this project</b>
  18647. # <%= render project.topics %>
  18648. # <% end %>
  18649. def cache_if(condition, name = {}, options = nil, &block)
  18650. if condition
  18651. cache(name, options, &block)
  18652. else
  18653. yield
  18654. end
  18655. nil
  18656. end
  18657. # Cache fragments of a view unless +condition+ is true
  18658. #
  18659. # <%= cache_unless admin?, project do %>
  18660. # <b>All the topics on this project</b>
  18661. # <%= render project.topics %>
  18662. # <% end %>
  18663. def cache_unless(condition, name = {}, options = nil, &block)
  18664. cache_if !condition, name, options, &block
  18665. end
  18666. # This helper returns the name of a cache key for a given fragment cache
  18667. # call. By supplying skip_digest: true to cache, the digestion of cache
  18668. # fragments can be manually bypassed. This is useful when cache fragments
  18669. # cannot be manually expired unless you know the exact key which is the
  18670. # case when using memcached.
  18671. def cache_fragment_name(name = {}, options = nil)
  18672. skip_digest = options && options[:skip_digest]
  18673. if skip_digest
  18674. name
  18675. else
  18676. fragment_name_with_digest(name)
  18677. end
  18678. end
  18679. private
  18680. def fragment_name_with_digest(name) #:nodoc:
  18681. if @virtual_path
  18682. [
  18683. *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
  18684. Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context, dependencies: view_cache_dependencies)
  18685. ]
  18686. else
  18687. name
  18688. end
  18689. end
  18690. # TODO: Create an object that has caching read/write on it
  18691. def fragment_for(name = {}, options = nil, &block) #:nodoc:
  18692. if fragment = controller.read_fragment(name, options)
  18693. fragment
  18694. else
  18695. # VIEW TODO: Make #capture usable outside of ERB
  18696. # This dance is needed because Builder can't use capture
  18697. pos = output_buffer.length
  18698. yield
  18699. output_safe = output_buffer.html_safe?
  18700. fragment = output_buffer.slice!(pos..-1)
  18701. if output_safe
  18702. self.output_buffer = output_buffer.class.new(output_buffer)
  18703. end
  18704. controller.write_fragment(name, fragment, options)
  18705. end
  18706. end
  18707. end
  18708. end
  18709. end
  18710. require 'active_support/core_ext/string/output_safety'
  18711. module ActionView
  18712. # = Action View Capture Helper
  18713. module Helpers
  18714. # CaptureHelper exposes methods to let you extract generated markup which
  18715. # can be used in other parts of a template or layout file.
  18716. #
  18717. # It provides a method to capture blocks into variables through capture and
  18718. # a way to capture a block of markup for use in a layout through content_for.
  18719. module CaptureHelper
  18720. # The capture method allows you to extract part of a template into a
  18721. # variable. You can then use this variable anywhere in your templates or layout.
  18722. #
  18723. # The capture method can be used in ERB templates...
  18724. #
  18725. # <% @greeting = capture do %>
  18726. # Welcome to my shiny new web page! The date and time is
  18727. # <%= Time.now %>
  18728. # <% end %>
  18729. #
  18730. # ...and Builder (RXML) templates.
  18731. #
  18732. # @timestamp = capture do
  18733. # "The current timestamp is #{Time.now}."
  18734. # end
  18735. #
  18736. # You can then use that variable anywhere else. For example:
  18737. #
  18738. # <html>
  18739. # <head><title><%= @greeting %></title></head>
  18740. # <body>
  18741. # <b><%= @greeting %></b>
  18742. # </body></html>
  18743. #
  18744. def capture(*args)
  18745. value = nil
  18746. buffer = with_output_buffer { value = yield(*args) }
  18747. if string = buffer.presence || value and string.is_a?(String)
  18748. ERB::Util.html_escape string
  18749. end
  18750. end
  18751. # Calling content_for stores a block of markup in an identifier for later use.
  18752. # In order to access this stored content in other templates, helper modules
  18753. # or the layout, you would pass the identifier as an argument to <tt>content_for</tt>.
  18754. #
  18755. # Note: <tt>yield</tt> can still be used to retrieve the stored content, but calling
  18756. # <tt>yield</tt> doesn't work in helper modules, while <tt>content_for</tt> does.
  18757. #
  18758. # <% content_for :not_authorized do %>
  18759. # alert('You are not authorized to do that!')
  18760. # <% end %>
  18761. #
  18762. # You can then use <tt>content_for :not_authorized</tt> anywhere in your templates.
  18763. #
  18764. # <%= content_for :not_authorized if current_user.nil? %>
  18765. #
  18766. # This is equivalent to:
  18767. #
  18768. # <%= yield :not_authorized if current_user.nil? %>
  18769. #
  18770. # <tt>content_for</tt>, however, can also be used in helper modules.
  18771. #
  18772. # module StorageHelper
  18773. # def stored_content
  18774. # content_for(:storage) || "Your storage is empty"
  18775. # end
  18776. # end
  18777. #
  18778. # This helper works just like normal helpers.
  18779. #
  18780. # <%= stored_content %>
  18781. #
  18782. # You can also use the <tt>yield</tt> syntax alongside an existing call to
  18783. # <tt>yield</tt> in a layout. For example:
  18784. #
  18785. # <%# This is the layout %>
  18786. # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  18787. # <head>
  18788. # <title>My Website</title>
  18789. # <%= yield :script %>
  18790. # </head>
  18791. # <body>
  18792. # <%= yield %>
  18793. # </body>
  18794. # </html>
  18795. #
  18796. # And now, we'll create a view that has a <tt>content_for</tt> call that
  18797. # creates the <tt>script</tt> identifier.
  18798. #
  18799. # <%# This is our view %>
  18800. # Please login!
  18801. #
  18802. # <% content_for :script do %>
  18803. # <script>alert('You are not authorized to view this page!')</script>
  18804. # <% end %>
  18805. #
  18806. # Then, in another view, you could to do something like this:
  18807. #
  18808. # <%= link_to 'Logout', action: 'logout', remote: true %>
  18809. #
  18810. # <% content_for :script do %>
  18811. # <%= javascript_include_tag :defaults %>
  18812. # <% end %>
  18813. #
  18814. # That will place +script+ tags for your default set of JavaScript files on the page;
  18815. # this technique is useful if you'll only be using these scripts in a few views.
  18816. #
  18817. # Note that content_for concatenates (default) the blocks it is given for a particular
  18818. # identifier in order. For example:
  18819. #
  18820. # <% content_for :navigation do %>
  18821. # <li><%= link_to 'Home', action: 'index' %></li>
  18822. # <% end %>
  18823. #
  18824. # And in other place:
  18825. #
  18826. # <% content_for :navigation do %>
  18827. # <li><%= link_to 'Login', action: 'login' %></li>
  18828. # <% end %>
  18829. #
  18830. # Then, in another template or layout, this code would render both links in order:
  18831. #
  18832. # <ul><%= content_for :navigation %></ul>
  18833. #
  18834. # If the flush parameter is true content_for replaces the blocks it is given. For example:
  18835. #
  18836. # <% content_for :navigation do %>
  18837. # <li><%= link_to 'Home', action: 'index' %></li>
  18838. # <% end %>
  18839. #
  18840. # <%# Add some other content, or use a different template: %>
  18841. #
  18842. # <% content_for :navigation, flush: true do %>
  18843. # <li><%= link_to 'Login', action: 'login' %></li>
  18844. # <% end %>
  18845. #
  18846. # Then, in another template or layout, this code would render only the last link:
  18847. #
  18848. # <ul><%= content_for :navigation %></ul>
  18849. #
  18850. # Lastly, simple content can be passed as a parameter:
  18851. #
  18852. # <% content_for :script, javascript_include_tag(:defaults) %>
  18853. #
  18854. # WARNING: content_for is ignored in caches. So you shouldn't use it for elements that will be fragment cached.
  18855. def content_for(name, content = nil, options = {}, &block)
  18856. if content || block_given?
  18857. if block_given?
  18858. options = content if content
  18859. content = capture(&block)
  18860. end
  18861. if content
  18862. options[:flush] ? @view_flow.set(name, content) : @view_flow.append(name, content)
  18863. end
  18864. nil
  18865. else
  18866. @view_flow.get(name)
  18867. end
  18868. end
  18869. # The same as +content_for+ but when used with streaming flushes
  18870. # straight back to the layout. In other words, if you want to
  18871. # concatenate several times to the same buffer when rendering a given
  18872. # template, you should use +content_for+, if not, use +provide+ to tell
  18873. # the layout to stop looking for more contents.
  18874. def provide(name, content = nil, &block)
  18875. content = capture(&block) if block_given?
  18876. result = @view_flow.append!(name, content) if content
  18877. result unless content
  18878. end
  18879. # content_for? checks whether any content has been captured yet using `content_for`.
  18880. # Useful to render parts of your layout differently based on what is in your views.
  18881. #
  18882. # <%# This is the layout %>
  18883. # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  18884. # <head>
  18885. # <title>My Website</title>
  18886. # <%= yield :script %>
  18887. # </head>
  18888. # <body class="<%= content_for?(:right_col) ? 'one-column' : 'two-column' %>">
  18889. # <%= yield %>
  18890. # <%= yield :right_col %>
  18891. # </body>
  18892. # </html>
  18893. def content_for?(name)
  18894. @view_flow.get(name).present?
  18895. end
  18896. # Use an alternate output buffer for the duration of the block.
  18897. # Defaults to a new empty string.
  18898. def with_output_buffer(buf = nil) #:nodoc:
  18899. unless buf
  18900. buf = ActionView::OutputBuffer.new
  18901. buf.force_encoding(output_buffer.encoding) if output_buffer
  18902. end
  18903. self.output_buffer, old_buffer = buf, output_buffer
  18904. yield
  18905. output_buffer
  18906. ensure
  18907. self.output_buffer = old_buffer
  18908. end
  18909. # Add the output buffer to the response body and start a new one.
  18910. def flush_output_buffer #:nodoc:
  18911. if output_buffer && !output_buffer.empty?
  18912. response.stream.write output_buffer
  18913. self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0]
  18914. nil
  18915. end
  18916. end
  18917. end
  18918. end
  18919. end
  18920. require 'active_support/core_ext/module/attr_internal'
  18921. module ActionView
  18922. module Helpers
  18923. # This module keeps all methods and behavior in ActionView
  18924. # that simply delegates to the controller.
  18925. module ControllerHelper #:nodoc:
  18926. attr_internal :controller, :request
  18927. delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
  18928. :flash, :action_name, :controller_name, :controller_path, :to => :controller
  18929. def assign_controller(controller)
  18930. if @_controller = controller
  18931. @_request = controller.request if controller.respond_to?(:request)
  18932. @_config = controller.config.inheritable_copy if controller.respond_to?(:config)
  18933. end
  18934. end
  18935. def logger
  18936. controller.logger if controller.respond_to?(:logger)
  18937. end
  18938. end
  18939. end
  18940. end
  18941. module ActionView
  18942. # = Action View CSRF Helper
  18943. module Helpers
  18944. module CsrfHelper
  18945. # Returns meta tags "csrf-param" and "csrf-token" with the name of the cross-site
  18946. # request forgery protection parameter and token, respectively.
  18947. #
  18948. # <head>
  18949. # <%= csrf_meta_tags %>
  18950. # </head>
  18951. #
  18952. # These are used to generate the dynamic forms that implement non-remote links with
  18953. # <tt>:method</tt>.
  18954. #
  18955. # Note that regular forms generate hidden fields, and that Ajax calls are whitelisted,
  18956. # so they do not use these tags.
  18957. def csrf_meta_tags
  18958. if protect_against_forgery?
  18959. [
  18960. tag('meta', :name => 'csrf-param', :content => request_forgery_protection_token),
  18961. tag('meta', :name => 'csrf-token', :content => form_authenticity_token)
  18962. ].join("\n").html_safe
  18963. end
  18964. end
  18965. # For backwards compatibility.
  18966. alias csrf_meta_tag csrf_meta_tags
  18967. end
  18968. end
  18969. end
  18970. require 'date'
  18971. require 'action_view/helpers/tag_helper'
  18972. require 'active_support/core_ext/array/extract_options'
  18973. require 'active_support/core_ext/date/conversions'
  18974. require 'active_support/core_ext/hash/slice'
  18975. require 'active_support/core_ext/object/with_options'
  18976. module ActionView
  18977. module Helpers
  18978. # = Action View Date Helpers
  18979. #
  18980. # The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
  18981. # elements. All of the select-type methods share a number of common options that are as follows:
  18982. #
  18983. # * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
  18984. # would give \birthday[month] instead of \date[month] if passed to the <tt>select_month</tt> method.
  18985. # * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
  18986. # * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
  18987. # the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
  18988. # of \date[month].
  18989. module DateHelper
  18990. # Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
  18991. # Pass <tt>include_seconds: true</tt> if you want more detailed approximations when distance < 1 min, 29 secs.
  18992. # Distances are reported based on the following table:
  18993. #
  18994. # 0 <-> 29 secs # => less than a minute
  18995. # 30 secs <-> 1 min, 29 secs # => 1 minute
  18996. # 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
  18997. # 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
  18998. # 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
  18999. # 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
  19000. # 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
  19001. # 29 days, 23 hrs, 59 mins, 30 secs <-> 44 days, 23 hrs, 59 mins, 29 secs # => about 1 month
  19002. # 44 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 2 months
  19003. # 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
  19004. # 1 yr <-> 1 yr, 3 months # => about 1 year
  19005. # 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
  19006. # 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
  19007. # 2 yrs <-> max time or date # => (same rules as 1 yr)
  19008. #
  19009. # With <tt>include_seconds: true</tt> and the difference < 1 minute 29 seconds:
  19010. # 0-4 secs # => less than 5 seconds
  19011. # 5-9 secs # => less than 10 seconds
  19012. # 10-19 secs # => less than 20 seconds
  19013. # 20-39 secs # => half a minute
  19014. # 40-59 secs # => less than a minute
  19015. # 60-89 secs # => 1 minute
  19016. #
  19017. # from_time = Time.now
  19018. # distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
  19019. # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
  19020. # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
  19021. # distance_of_time_in_words(from_time, from_time + 15.seconds, include_seconds: true) # => less than 20 seconds
  19022. # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
  19023. # distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
  19024. # distance_of_time_in_words(from_time, from_time + 45.seconds, include_seconds: true) # => less than a minute
  19025. # distance_of_time_in_words(from_time, from_time - 45.seconds, include_seconds: true) # => less than a minute
  19026. # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
  19027. # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
  19028. # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
  19029. # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
  19030. #
  19031. # to_time = Time.now + 6.years + 19.days
  19032. # distance_of_time_in_words(from_time, to_time, include_seconds: true) # => about 6 years
  19033. # distance_of_time_in_words(to_time, from_time, include_seconds: true) # => about 6 years
  19034. # distance_of_time_in_words(Time.now, Time.now) # => less than a minute
  19035. def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {})
  19036. if include_seconds_or_options.is_a?(Hash)
  19037. options = include_seconds_or_options
  19038. else
  19039. ActiveSupport::Deprecation.warn "distance_of_time_in_words and time_ago_in_words now accept :include_seconds " +
  19040. "as a part of options hash, not a boolean argument"
  19041. options[:include_seconds] ||= !!include_seconds_or_options
  19042. end
  19043. options = {
  19044. scope: :'datetime.distance_in_words'
  19045. }.merge!(options)
  19046. from_time = from_time.to_time if from_time.respond_to?(:to_time)
  19047. to_time = to_time.to_time if to_time.respond_to?(:to_time)
  19048. from_time, to_time = to_time, from_time if from_time > to_time
  19049. distance_in_minutes = ((to_time - from_time)/60.0).round
  19050. distance_in_seconds = (to_time - from_time).round
  19051. I18n.with_options :locale => options[:locale], :scope => options[:scope] do |locale|
  19052. case distance_in_minutes
  19053. when 0..1
  19054. return distance_in_minutes == 0 ?
  19055. locale.t(:less_than_x_minutes, :count => 1) :
  19056. locale.t(:x_minutes, :count => distance_in_minutes) unless options[:include_seconds]
  19057. case distance_in_seconds
  19058. when 0..4 then locale.t :less_than_x_seconds, :count => 5
  19059. when 5..9 then locale.t :less_than_x_seconds, :count => 10
  19060. when 10..19 then locale.t :less_than_x_seconds, :count => 20
  19061. when 20..39 then locale.t :half_a_minute
  19062. when 40..59 then locale.t :less_than_x_minutes, :count => 1
  19063. else locale.t :x_minutes, :count => 1
  19064. end
  19065. when 2...45 then locale.t :x_minutes, :count => distance_in_minutes
  19066. when 45...90 then locale.t :about_x_hours, :count => 1
  19067. # 90 mins up to 24 hours
  19068. when 90...1440 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
  19069. # 24 hours up to 42 hours
  19070. when 1440...2520 then locale.t :x_days, :count => 1
  19071. # 42 hours up to 30 days
  19072. when 2520...43200 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
  19073. # 30 days up to 60 days
  19074. when 43200...86400 then locale.t :about_x_months, :count => (distance_in_minutes.to_f / 43200.0).round
  19075. # 60 days up to 365 days
  19076. when 86400...525600 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
  19077. else
  19078. if from_time.acts_like?(:time) && to_time.acts_like?(:time)
  19079. fyear = from_time.year
  19080. fyear += 1 if from_time.month >= 3
  19081. tyear = to_time.year
  19082. tyear -= 1 if to_time.month < 3
  19083. leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
  19084. minute_offset_for_leap_year = leap_years * 1440
  19085. # Discount the leap year days when calculating year distance.
  19086. # e.g. if there are 20 leap year days between 2 dates having the same day
  19087. # and month then the based on 365 days calculation
  19088. # the distance in years will come out to over 80 years when in written
  19089. # english it would read better as about 80 years.
  19090. minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
  19091. else
  19092. minutes_with_offset = distance_in_minutes
  19093. end
  19094. remainder = (minutes_with_offset % 525600)
  19095. distance_in_years = (minutes_with_offset.div 525600)
  19096. if remainder < 131400
  19097. locale.t(:about_x_years, :count => distance_in_years)
  19098. elsif remainder < 394200
  19099. locale.t(:over_x_years, :count => distance_in_years)
  19100. else
  19101. locale.t(:almost_x_years, :count => distance_in_years + 1)
  19102. end
  19103. end
  19104. end
  19105. end
  19106. # Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
  19107. #
  19108. # time_ago_in_words(3.minutes.from_now) # => 3 minutes
  19109. # time_ago_in_words(3.minutes.ago) # => 3 minutes
  19110. # time_ago_in_words(Time.now - 15.hours) # => about 15 hours
  19111. # time_ago_in_words(Time.now) # => less than a minute
  19112. # time_ago_in_words(Time.now, include_seconds: true) # => less than 5 seconds
  19113. #
  19114. # from_time = Time.now - 3.days - 14.minutes - 25.seconds
  19115. # time_ago_in_words(from_time) # => 3 days
  19116. #
  19117. # from_time = (3.days + 14.minutes + 25.seconds).ago
  19118. # time_ago_in_words(from_time) # => 3 days
  19119. #
  19120. # Note that you cannot pass a <tt>Numeric</tt> value to <tt>time_ago_in_words</tt>.
  19121. #
  19122. def time_ago_in_words(from_time, include_seconds_or_options = {})
  19123. distance_of_time_in_words(from_time, Time.now, include_seconds_or_options)
  19124. end
  19125. alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
  19126. # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
  19127. # attribute (identified by +method+) on an object assigned to the template (identified by +object+).
  19128. #
  19129. # ==== Options
  19130. # * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
  19131. # "2" instead of "February").
  19132. # * <tt>:use_two_digit_numbers</tt> - Set to true if you want to display two digit month and day numbers (e.g.
  19133. # "02" instead of "February" and "08" instead of "8").
  19134. # * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
  19135. # month names (e.g. "Feb" instead of "February").
  19136. # * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
  19137. # "2 - February" instead of "February").
  19138. # * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
  19139. # Note: You can also use Rails' i18n functionality for this.
  19140. # * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
  19141. # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
  19142. # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
  19143. # * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
  19144. # as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
  19145. # first of the given month in order to not create invalid dates like 31 February.
  19146. # * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
  19147. # as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
  19148. # * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
  19149. # as a hidden field instead of showing a select field.
  19150. # * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to
  19151. # customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
  19152. # select will not be shown (like when you set <tt>discard_xxx: true</tt>. Defaults to the order defined in
  19153. # the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
  19154. # * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
  19155. # dates.
  19156. # * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
  19157. # * <tt>:selected</tt> - Set a date that overrides the actual value.
  19158. # * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
  19159. # * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
  19160. # for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
  19161. # Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
  19162. # or the given prompt string.
  19163. # * <tt>:with_css_classes</tt> - Set to true if you want assign different styles for 'select' tags. This option
  19164. # automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second' for your 'select' tags.
  19165. #
  19166. # If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
  19167. #
  19168. # NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
  19169. #
  19170. # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute.
  19171. # date_select("article", "written_on")
  19172. #
  19173. # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
  19174. # # with the year in the year drop down box starting at 1995.
  19175. # date_select("article", "written_on", start_year: 1995)
  19176. #
  19177. # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
  19178. # # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
  19179. # # and without a day select box.
  19180. # date_select("article", "written_on", start_year: 1995, use_month_numbers: true,
  19181. # discard_day: true, include_blank: true)
  19182. #
  19183. # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
  19184. # # with two digit numbers used for months and days.
  19185. # date_select("article", "written_on", use_two_digit_numbers: true)
  19186. #
  19187. # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
  19188. # # with the fields ordered as day, month, year rather than month, day, year.
  19189. # date_select("article", "written_on", order: [:day, :month, :year])
  19190. #
  19191. # # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
  19192. # # lacking a year field.
  19193. # date_select("user", "birthday", order: [:month, :day])
  19194. #
  19195. # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
  19196. # # which is initially set to the date 3 days from the current date
  19197. # date_select("article", "written_on", default: 3.days.from_now)
  19198. #
  19199. # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
  19200. # # which is set in the form with todays date, regardless of the value in the Active Record object.
  19201. # date_select("article", "written_on", selected: Date.today)
  19202. #
  19203. # # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
  19204. # # that will have a default day of 20.
  19205. # date_select("credit_card", "bill_due", default: { day: 20 })
  19206. #
  19207. # # Generates a date select with custom prompts.
  19208. # date_select("article", "written_on", prompt: { day: 'Select day', month: 'Select month', year: 'Select year' })
  19209. #
  19210. # The selects are prepared for multi-parameter assignment to an Active Record object.
  19211. #
  19212. # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
  19213. # all month choices are valid.
  19214. def date_select(object_name, method, options = {}, html_options = {})
  19215. Tags::DateSelect.new(object_name, method, self, options, html_options).render
  19216. end
  19217. # Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
  19218. # specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
  19219. # +object+). You can include the seconds with <tt>:include_seconds</tt>. You can get hours in the AM/PM format
  19220. # with <tt>:ampm</tt> option.
  19221. #
  19222. # This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
  19223. # <tt>:ignore_date</tt> is set to +true+. If you set the <tt>:ignore_date</tt> to +true+, you must have a
  19224. # +date_select+ on the same method within the form otherwise an exception will be raised.
  19225. #
  19226. # If anything is passed in the html_options hash it will be applied to every select tag in the set.
  19227. #
  19228. # # Creates a time select tag that, when POSTed, will be stored in the article variable in the sunrise attribute.
  19229. # time_select("article", "sunrise")
  19230. #
  19231. # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the article variables in
  19232. # # the sunrise attribute.
  19233. # time_select("article", "start_time", include_seconds: true)
  19234. #
  19235. # # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
  19236. # time_select 'game', 'game_time', {minute_step: 15}
  19237. #
  19238. # # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
  19239. # time_select("article", "written_on", prompt: {hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds'})
  19240. # time_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
  19241. # time_select("article", "written_on", prompt: true) # generic prompts for all
  19242. #
  19243. # # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
  19244. # time_select 'game', 'game_time', {ampm: true}
  19245. #
  19246. # The selects are prepared for multi-parameter assignment to an Active Record object.
  19247. #
  19248. # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
  19249. # all month choices are valid.
  19250. def time_select(object_name, method, options = {}, html_options = {})
  19251. Tags::TimeSelect.new(object_name, method, self, options, html_options).render
  19252. end
  19253. # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
  19254. # specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
  19255. # by +object+).
  19256. #
  19257. # If anything is passed in the html_options hash it will be applied to every select tag in the set.
  19258. #
  19259. # # Generates a datetime select that, when POSTed, will be stored in the article variable in the written_on
  19260. # # attribute.
  19261. # datetime_select("article", "written_on")
  19262. #
  19263. # # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
  19264. # # article variable in the written_on attribute.
  19265. # datetime_select("article", "written_on", start_year: 1995)
  19266. #
  19267. # # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
  19268. # # be stored in the trip variable in the departing attribute.
  19269. # datetime_select("trip", "departing", default: 3.days.from_now)
  19270. #
  19271. # # Generate a datetime select with hours in the AM/PM format
  19272. # datetime_select("article", "written_on", ampm: true)
  19273. #
  19274. # # Generates a datetime select that discards the type that, when POSTed, will be stored in the article variable
  19275. # # as the written_on attribute.
  19276. # datetime_select("article", "written_on", discard_type: true)
  19277. #
  19278. # # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
  19279. # datetime_select("article", "written_on", prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
  19280. # datetime_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
  19281. # datetime_select("article", "written_on", prompt: true) # generic prompts for all
  19282. #
  19283. # The selects are prepared for multi-parameter assignment to an Active Record object.
  19284. def datetime_select(object_name, method, options = {}, html_options = {})
  19285. Tags::DatetimeSelect.new(object_name, method, self, options, html_options).render
  19286. end
  19287. # Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
  19288. # +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
  19289. # an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
  19290. # supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
  19291. # <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
  19292. # control visual display of the elements.
  19293. #
  19294. # If anything is passed in the html_options hash it will be applied to every select tag in the set.
  19295. #
  19296. # my_date_time = Time.now + 4.days
  19297. #
  19298. # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today).
  19299. # select_datetime(my_date_time)
  19300. #
  19301. # # Generates a datetime select that defaults to today (no specified datetime)
  19302. # select_datetime()
  19303. #
  19304. # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
  19305. # # with the fields ordered year, month, day rather than month, day, year.
  19306. # select_datetime(my_date_time, order: [:year, :month, :day])
  19307. #
  19308. # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
  19309. # # with a '/' between each date field.
  19310. # select_datetime(my_date_time, date_separator: '/')
  19311. #
  19312. # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
  19313. # # with a date fields separated by '/', time fields separated by '' and the date and time fields
  19314. # # separated by a comma (',').
  19315. # select_datetime(my_date_time, date_separator: '/', time_separator: '', datetime_separator: ',')
  19316. #
  19317. # # Generates a datetime select that discards the type of the field and defaults to the datetime in
  19318. # # my_date_time (four days after today)
  19319. # select_datetime(my_date_time, discard_type: true)
  19320. #
  19321. # # Generate a datetime field with hours in the AM/PM format
  19322. # select_datetime(my_date_time, ampm: true)
  19323. #
  19324. # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
  19325. # # prefixed with 'payday' rather than 'date'
  19326. # select_datetime(my_date_time, prefix: 'payday')
  19327. #
  19328. # # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
  19329. # select_datetime(my_date_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
  19330. # select_datetime(my_date_time, prompt: {hour: true}) # generic prompt for hours
  19331. # select_datetime(my_date_time, prompt: true) # generic prompts for all
  19332. def select_datetime(datetime = Time.current, options = {}, html_options = {})
  19333. DateTimeSelector.new(datetime, options, html_options).select_datetime
  19334. end
  19335. # Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
  19336. # It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
  19337. # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order.
  19338. # If the array passed to the <tt>:order</tt> option does not contain all the three symbols, all tags will be hidden.
  19339. #
  19340. # If anything is passed in the html_options hash it will be applied to every select tag in the set.
  19341. #
  19342. # my_date = Time.now + 6.days
  19343. #
  19344. # # Generates a date select that defaults to the date in my_date (six days after today).
  19345. # select_date(my_date)
  19346. #
  19347. # # Generates a date select that defaults to today (no specified date).
  19348. # select_date()
  19349. #
  19350. # # Generates a date select that defaults to the date in my_date (six days after today)
  19351. # # with the fields ordered year, month, day rather than month, day, year.
  19352. # select_date(my_date, order: [:year, :month, :day])
  19353. #
  19354. # # Generates a date select that discards the type of the field and defaults to the date in
  19355. # # my_date (six days after today).
  19356. # select_date(my_date, discard_type: true)
  19357. #
  19358. # # Generates a date select that defaults to the date in my_date,
  19359. # # which has fields separated by '/'.
  19360. # select_date(my_date, date_separator: '/')
  19361. #
  19362. # # Generates a date select that defaults to the datetime in my_date (six days after today)
  19363. # # prefixed with 'payday' rather than 'date'.
  19364. # select_date(my_date, prefix: 'payday')
  19365. #
  19366. # # Generates a date select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
  19367. # select_date(my_date, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
  19368. # select_date(my_date, prompt: {hour: true}) # generic prompt for hours
  19369. # select_date(my_date, prompt: true) # generic prompts for all
  19370. def select_date(date = Date.current, options = {}, html_options = {})
  19371. DateTimeSelector.new(date, options, html_options).select_date
  19372. end
  19373. # Returns a set of html select-tags (one for hour and minute).
  19374. # You can set <tt>:time_separator</tt> key to format the output, and
  19375. # the <tt>:include_seconds</tt> option to include an input for seconds.
  19376. #
  19377. # If anything is passed in the html_options hash it will be applied to every select tag in the set.
  19378. #
  19379. # my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds
  19380. #
  19381. # # Generates a time select that defaults to the time in my_time.
  19382. # select_time(my_time)
  19383. #
  19384. # # Generates a time select that defaults to the current time (no specified time).
  19385. # select_time()
  19386. #
  19387. # # Generates a time select that defaults to the time in my_time,
  19388. # # which has fields separated by ':'.
  19389. # select_time(my_time, time_separator: ':')
  19390. #
  19391. # # Generates a time select that defaults to the time in my_time,
  19392. # # that also includes an input for seconds.
  19393. # select_time(my_time, include_seconds: true)
  19394. #
  19395. # # Generates a time select that defaults to the time in my_time, that has fields
  19396. # # separated by ':' and includes an input for seconds.
  19397. # select_time(my_time, time_separator: ':', include_seconds: true)
  19398. #
  19399. # # Generate a time select field with hours in the AM/PM format
  19400. # select_time(my_time, ampm: true)
  19401. #
  19402. # # Generates a time select field with hours that range from 2 to 14
  19403. # select_time(my_time, start_hour: 2, end_hour: 14)
  19404. #
  19405. # # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
  19406. # select_time(my_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
  19407. # select_time(my_time, prompt: {hour: true}) # generic prompt for hours
  19408. # select_time(my_time, prompt: true) # generic prompts for all
  19409. def select_time(datetime = Time.current, options = {}, html_options = {})
  19410. DateTimeSelector.new(datetime, options, html_options).select_time
  19411. end
  19412. # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
  19413. # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
  19414. # Override the field name using the <tt>:field_name</tt> option, 'second' by default.
  19415. #
  19416. # my_time = Time.now + 16.minutes
  19417. #
  19418. # # Generates a select field for seconds that defaults to the seconds for the time in my_time.
  19419. # select_second(my_time)
  19420. #
  19421. # # Generates a select field for seconds that defaults to the number given.
  19422. # select_second(33)
  19423. #
  19424. # # Generates a select field for seconds that defaults to the seconds for the time in my_time
  19425. # # that is named 'interval' rather than 'second'.
  19426. # select_second(my_time, field_name: 'interval')
  19427. #
  19428. # # Generates a select field for seconds with a custom prompt. Use <tt>prompt: true</tt> for a
  19429. # # generic prompt.
  19430. # select_second(14, prompt: 'Choose seconds')
  19431. def select_second(datetime, options = {}, html_options = {})
  19432. DateTimeSelector.new(datetime, options, html_options).select_second
  19433. end
  19434. # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
  19435. # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
  19436. # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
  19437. # Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
  19438. #
  19439. # my_time = Time.now + 6.hours
  19440. #
  19441. # # Generates a select field for minutes that defaults to the minutes for the time in my_time.
  19442. # select_minute(my_time)
  19443. #
  19444. # # Generates a select field for minutes that defaults to the number given.
  19445. # select_minute(14)
  19446. #
  19447. # # Generates a select field for minutes that defaults to the minutes for the time in my_time
  19448. # # that is named 'moment' rather than 'minute'.
  19449. # select_minute(my_time, field_name: 'moment')
  19450. #
  19451. # # Generates a select field for minutes with a custom prompt. Use <tt>prompt: true</tt> for a
  19452. # # generic prompt.
  19453. # select_minute(14, prompt: 'Choose minutes')
  19454. def select_minute(datetime, options = {}, html_options = {})
  19455. DateTimeSelector.new(datetime, options, html_options).select_minute
  19456. end
  19457. # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
  19458. # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
  19459. # Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
  19460. #
  19461. # my_time = Time.now + 6.hours
  19462. #
  19463. # # Generates a select field for hours that defaults to the hour for the time in my_time.
  19464. # select_hour(my_time)
  19465. #
  19466. # # Generates a select field for hours that defaults to the number given.
  19467. # select_hour(13)
  19468. #
  19469. # # Generates a select field for hours that defaults to the hour for the time in my_time
  19470. # # that is named 'stride' rather than 'hour'.
  19471. # select_hour(my_time, field_name: 'stride')
  19472. #
  19473. # # Generates a select field for hours with a custom prompt. Use <tt>prompt: true</tt> for a
  19474. # # generic prompt.
  19475. # select_hour(13, prompt: 'Choose hour')
  19476. #
  19477. # # Generate a select field for hours in the AM/PM format
  19478. # select_hour(my_time, ampm: true)
  19479. #
  19480. # # Generates a select field that includes options for hours from 2 to 14.
  19481. # select_hour(my_time, start_hour: 2, end_hour: 14)
  19482. def select_hour(datetime, options = {}, html_options = {})
  19483. DateTimeSelector.new(datetime, options, html_options).select_hour
  19484. end
  19485. # Returns a select tag with options for each of the days 1 through 31 with the current day selected.
  19486. # The <tt>date</tt> can also be substituted for a day number.
  19487. # If you want to display days with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
  19488. # Override the field name using the <tt>:field_name</tt> option, 'day' by default.
  19489. #
  19490. # my_date = Time.now + 2.days
  19491. #
  19492. # # Generates a select field for days that defaults to the day for the date in my_date.
  19493. # select_day(my_time)
  19494. #
  19495. # # Generates a select field for days that defaults to the number given.
  19496. # select_day(5)
  19497. #
  19498. # # Generates a select field for days that defaults to the number given, but displays it with two digits.
  19499. # select_day(5, use_two_digit_numbers: true)
  19500. #
  19501. # # Generates a select field for days that defaults to the day for the date in my_date
  19502. # # that is named 'due' rather than 'day'.
  19503. # select_day(my_time, field_name: 'due')
  19504. #
  19505. # # Generates a select field for days with a custom prompt. Use <tt>prompt: true</tt> for a
  19506. # # generic prompt.
  19507. # select_day(5, prompt: 'Choose day')
  19508. def select_day(date, options = {}, html_options = {})
  19509. DateTimeSelector.new(date, options, html_options).select_day
  19510. end
  19511. # Returns a select tag with options for each of the months January through December with the current month
  19512. # selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
  19513. # used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
  19514. # instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
  19515. # want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
  19516. # to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
  19517. # to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
  19518. # If you want to display months with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
  19519. # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
  19520. #
  19521. # # Generates a select field for months that defaults to the current month that
  19522. # # will use keys like "January", "March".
  19523. # select_month(Date.today)
  19524. #
  19525. # # Generates a select field for months that defaults to the current month that
  19526. # # is named "start" rather than "month".
  19527. # select_month(Date.today, field_name: 'start')
  19528. #
  19529. # # Generates a select field for months that defaults to the current month that
  19530. # # will use keys like "1", "3".
  19531. # select_month(Date.today, use_month_numbers: true)
  19532. #
  19533. # # Generates a select field for months that defaults to the current month that
  19534. # # will use keys like "1 - January", "3 - March".
  19535. # select_month(Date.today, add_month_numbers: true)
  19536. #
  19537. # # Generates a select field for months that defaults to the current month that
  19538. # # will use keys like "Jan", "Mar".
  19539. # select_month(Date.today, use_short_month: true)
  19540. #
  19541. # # Generates a select field for months that defaults to the current month that
  19542. # # will use keys like "Januar", "Marts."
  19543. # select_month(Date.today, use_month_names: %w(Januar Februar Marts ...))
  19544. #
  19545. # # Generates a select field for months that defaults to the current month that
  19546. # # will use keys with two digit numbers like "01", "03".
  19547. # select_month(Date.today, use_two_digit_numbers: true)
  19548. #
  19549. # # Generates a select field for months with a custom prompt. Use <tt>prompt: true</tt> for a
  19550. # # generic prompt.
  19551. # select_month(14, prompt: 'Choose month')
  19552. def select_month(date, options = {}, html_options = {})
  19553. DateTimeSelector.new(date, options, html_options).select_month
  19554. end
  19555. # Returns a select tag with options for each of the five years on each side of the current, which is selected.
  19556. # The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
  19557. # +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
  19558. # greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
  19559. # Override the field name using the <tt>:field_name</tt> option, 'year' by default.
  19560. #
  19561. # # Generates a select field for years that defaults to the current year that
  19562. # # has ascending year values.
  19563. # select_year(Date.today, start_year: 1992, end_year: 2007)
  19564. #
  19565. # # Generates a select field for years that defaults to the current year that
  19566. # # is named 'birth' rather than 'year'.
  19567. # select_year(Date.today, field_name: 'birth')
  19568. #
  19569. # # Generates a select field for years that defaults to the current year that
  19570. # # has descending year values.
  19571. # select_year(Date.today, start_year: 2005, end_year: 1900)
  19572. #
  19573. # # Generates a select field for years that defaults to the year 2006 that
  19574. # # has ascending year values.
  19575. # select_year(2006, start_year: 2000, end_year: 2010)
  19576. #
  19577. # # Generates a select field for years with a custom prompt. Use <tt>prompt: true</tt> for a
  19578. # # generic prompt.
  19579. # select_year(14, prompt: 'Choose year')
  19580. def select_year(date, options = {}, html_options = {})
  19581. DateTimeSelector.new(date, options, html_options).select_year
  19582. end
  19583. # Returns an html time tag for the given date or time.
  19584. #
  19585. # time_tag Date.today # =>
  19586. # <time datetime="2010-11-04">November 04, 2010</time>
  19587. # time_tag Time.now # =>
  19588. # <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
  19589. # time_tag Date.yesterday, 'Yesterday' # =>
  19590. # <time datetime="2010-11-03">Yesterday</time>
  19591. # time_tag Date.today, pubdate: true # =>
  19592. # <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
  19593. #
  19594. # <%= time_tag Time.now do %>
  19595. # <span>Right now</span>
  19596. # <% end %>
  19597. # # => <time datetime="2010-11-04T17:55:45+01:00"><span>Right now</span></time>
  19598. def time_tag(date_or_time, *args, &block)
  19599. options = args.extract_options!
  19600. format = options.delete(:format) || :long
  19601. content = args.first || I18n.l(date_or_time, :format => format)
  19602. datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.rfc3339
  19603. content_tag(:time, content, options.reverse_merge(:datetime => datetime), &block)
  19604. end
  19605. end
  19606. class DateTimeSelector #:nodoc:
  19607. include ActionView::Helpers::TagHelper
  19608. DEFAULT_PREFIX = 'date'.freeze
  19609. POSITION = {
  19610. :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
  19611. }.freeze
  19612. AMPM_TRANSLATION = Hash[
  19613. [[0, "12 AM"], [1, "01 AM"], [2, "02 AM"], [3, "03 AM"],
  19614. [4, "04 AM"], [5, "05 AM"], [6, "06 AM"], [7, "07 AM"],
  19615. [8, "08 AM"], [9, "09 AM"], [10, "10 AM"], [11, "11 AM"],
  19616. [12, "12 PM"], [13, "01 PM"], [14, "02 PM"], [15, "03 PM"],
  19617. [16, "04 PM"], [17, "05 PM"], [18, "06 PM"], [19, "07 PM"],
  19618. [20, "08 PM"], [21, "09 PM"], [22, "10 PM"], [23, "11 PM"]]
  19619. ].freeze
  19620. def initialize(datetime, options = {}, html_options = {})
  19621. @options = options.dup
  19622. @html_options = html_options.dup
  19623. @datetime = datetime
  19624. @options[:datetime_separator] ||= ' &mdash; '
  19625. @options[:time_separator] ||= ' : '
  19626. end
  19627. def select_datetime
  19628. order = date_order.dup
  19629. order -= [:hour, :minute, :second]
  19630. @options[:discard_year] ||= true unless order.include?(:year)
  19631. @options[:discard_month] ||= true unless order.include?(:month)
  19632. @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
  19633. @options[:discard_minute] ||= true if @options[:discard_hour]
  19634. @options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
  19635. set_day_if_discarded
  19636. if @options[:tag] && @options[:ignore_date]
  19637. select_time
  19638. else
  19639. [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
  19640. order += [:hour, :minute, :second] unless @options[:discard_hour]
  19641. build_selects_from_types(order)
  19642. end
  19643. end
  19644. def select_date
  19645. order = date_order.dup
  19646. @options[:discard_hour] = true
  19647. @options[:discard_minute] = true
  19648. @options[:discard_second] = true
  19649. @options[:discard_year] ||= true unless order.include?(:year)
  19650. @options[:discard_month] ||= true unless order.include?(:month)
  19651. @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
  19652. set_day_if_discarded
  19653. [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
  19654. build_selects_from_types(order)
  19655. end
  19656. def select_time
  19657. order = []
  19658. @options[:discard_month] = true
  19659. @options[:discard_year] = true
  19660. @options[:discard_day] = true
  19661. @options[:discard_second] ||= true unless @options[:include_seconds]
  19662. order += [:year, :month, :day] unless @options[:ignore_date]
  19663. order += [:hour, :minute]
  19664. order << :second if @options[:include_seconds]
  19665. build_selects_from_types(order)
  19666. end
  19667. def select_second
  19668. if @options[:use_hidden] || @options[:discard_second]
  19669. build_hidden(:second, sec) if @options[:include_seconds]
  19670. else
  19671. build_options_and_select(:second, sec)
  19672. end
  19673. end
  19674. def select_minute
  19675. if @options[:use_hidden] || @options[:discard_minute]
  19676. build_hidden(:minute, min)
  19677. else
  19678. build_options_and_select(:minute, min, :step => @options[:minute_step])
  19679. end
  19680. end
  19681. def select_hour
  19682. if @options[:use_hidden] || @options[:discard_hour]
  19683. build_hidden(:hour, hour)
  19684. else
  19685. options = {}
  19686. options[:ampm] = @options[:ampm] || false
  19687. options[:start] = @options[:start_hour] || 0
  19688. options[:end] = @options[:end_hour] || 23
  19689. build_options_and_select(:hour, hour, options)
  19690. end
  19691. end
  19692. def select_day
  19693. if @options[:use_hidden] || @options[:discard_day]
  19694. build_hidden(:day, day || 1)
  19695. else
  19696. build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false, :use_two_digit_numbers => @options[:use_two_digit_numbers])
  19697. end
  19698. end
  19699. def select_month
  19700. if @options[:use_hidden] || @options[:discard_month]
  19701. build_hidden(:month, month || 1)
  19702. else
  19703. month_options = []
  19704. 1.upto(12) do |month_number|
  19705. options = { :value => month_number }
  19706. options[:selected] = "selected" if month == month_number
  19707. month_options << content_tag(:option, month_name(month_number), options) + "\n"
  19708. end
  19709. build_select(:month, month_options.join)
  19710. end
  19711. end
  19712. def select_year
  19713. if !@datetime || @datetime == 0
  19714. val = '1'
  19715. middle_year = Date.today.year
  19716. else
  19717. val = middle_year = year
  19718. end
  19719. if @options[:use_hidden] || @options[:discard_year]
  19720. build_hidden(:year, val)
  19721. else
  19722. options = {}
  19723. options[:start] = @options[:start_year] || middle_year - 5
  19724. options[:end] = @options[:end_year] || middle_year + 5
  19725. options[:step] = options[:start] < options[:end] ? 1 : -1
  19726. options[:leading_zeros] = false
  19727. options[:max_years_allowed] = @options[:max_years_allowed] || 1000
  19728. if (options[:end] - options[:start]).abs > options[:max_years_allowed]
  19729. raise ArgumentError, "There're too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter"
  19730. end
  19731. build_options_and_select(:year, val, options)
  19732. end
  19733. end
  19734. private
  19735. %w( sec min hour day month year ).each do |method|
  19736. define_method(method) do
  19737. @datetime.kind_of?(Numeric) ? @datetime : @datetime.send(method) if @datetime
  19738. end
  19739. end
  19740. # If the day is hidden, the day should be set to the 1st so all month and year choices are
  19741. # valid. Otherwise, February 31st or February 29th, 2011 can be selected, which are invalid.
  19742. def set_day_if_discarded
  19743. if @datetime && @options[:discard_day]
  19744. @datetime = @datetime.change(:day => 1)
  19745. end
  19746. end
  19747. # Returns translated month names, but also ensures that a custom month
  19748. # name array has a leading nil element.
  19749. def month_names
  19750. @month_names ||= begin
  19751. month_names = @options[:use_month_names] || translated_month_names
  19752. month_names.unshift(nil) if month_names.size < 13
  19753. month_names
  19754. end
  19755. end
  19756. # Returns translated month names.
  19757. # => [nil, "January", "February", "March",
  19758. # "April", "May", "June", "July",
  19759. # "August", "September", "October",
  19760. # "November", "December"]
  19761. #
  19762. # If <tt>:use_short_month</tt> option is set
  19763. # => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  19764. # "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  19765. def translated_month_names
  19766. key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
  19767. I18n.translate(key, :locale => @options[:locale])
  19768. end
  19769. # Lookup month name for number.
  19770. # month_name(1) => "January"
  19771. #
  19772. # If <tt>:use_month_numbers</tt> option is passed
  19773. # month_name(1) => 1
  19774. #
  19775. # If <tt>:use_two_month_numbers</tt> option is passed
  19776. # month_name(1) => '01'
  19777. #
  19778. # If <tt>:add_month_numbers</tt> option is passed
  19779. # month_name(1) => "1 - January"
  19780. def month_name(number)
  19781. if @options[:use_month_numbers]
  19782. number
  19783. elsif @options[:use_two_digit_numbers]
  19784. sprintf "%02d", number
  19785. elsif @options[:add_month_numbers]
  19786. "#{number} - #{month_names[number]}"
  19787. else
  19788. month_names[number]
  19789. end
  19790. end
  19791. def date_order
  19792. @date_order ||= @options[:order] || translated_date_order
  19793. end
  19794. def translated_date_order
  19795. date_order = I18n.translate(:'date.order', :locale => @options[:locale], :default => [])
  19796. date_order = date_order.map { |element| element.to_sym }
  19797. forbidden_elements = date_order - [:year, :month, :day]
  19798. if forbidden_elements.any?
  19799. raise StandardError,
  19800. "#{@options[:locale]}.date.order only accepts :year, :month and :day"
  19801. end
  19802. date_order
  19803. end
  19804. # Build full select tag from date type and options.
  19805. def build_options_and_select(type, selected, options = {})
  19806. build_select(type, build_options(selected, options))
  19807. end
  19808. # Build select option html from date value and options.
  19809. # build_options(15, start: 1, end: 31)
  19810. # => "<option value="1">1</option>
  19811. # <option value="2">2</option>
  19812. # <option value="3">3</option>..."
  19813. #
  19814. # If <tt>use_two_digit_numbers: true</tt> option is passed
  19815. # build_options(15, start: 1, end: 31, use_two_digit_numbers: true)
  19816. # => "<option value="1">01</option>
  19817. # <option value="2">02</option>
  19818. # <option value="3">03</option>..."
  19819. #
  19820. # If <tt>:step</tt> options is passed
  19821. # build_options(15, start: 1, end: 31, step: 2)
  19822. # => "<option value="1">1</option>
  19823. # <option value="3">3</option>
  19824. # <option value="5">5</option>..."
  19825. def build_options(selected, options = {})
  19826. options = {
  19827. leading_zeros: true, ampm: false, use_two_digit_numbers: false
  19828. }.merge!(options)
  19829. start = options.delete(:start) || 0
  19830. stop = options.delete(:end) || 59
  19831. step = options.delete(:step) || 1
  19832. leading_zeros = options.delete(:leading_zeros)
  19833. select_options = []
  19834. start.step(stop, step) do |i|
  19835. value = leading_zeros ? sprintf("%02d", i) : i
  19836. tag_options = { :value => value }
  19837. tag_options[:selected] = "selected" if selected == i
  19838. text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
  19839. text = options[:ampm] ? AMPM_TRANSLATION[i] : text
  19840. select_options << content_tag(:option, text, tag_options)
  19841. end
  19842. (select_options.join("\n") + "\n").html_safe
  19843. end
  19844. # Builds select tag from date type and html select options.
  19845. # build_select(:month, "<option value="1">January</option>...")
  19846. # => "<select id="post_written_on_2i" name="post[written_on(2i)]">
  19847. # <option value="1">January</option>...
  19848. # </select>"
  19849. def build_select(type, select_options_as_html)
  19850. select_options = {
  19851. :id => input_id_from_type(type),
  19852. :name => input_name_from_type(type)
  19853. }.merge!(@html_options)
  19854. select_options[:disabled] = 'disabled' if @options[:disabled]
  19855. select_options[:class] = type if @options[:with_css_classes]
  19856. select_html = "\n"
  19857. select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
  19858. select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
  19859. select_html << select_options_as_html
  19860. (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
  19861. end
  19862. # Builds a prompt option tag with supplied options or from default options.
  19863. # prompt_option_tag(:month, prompt: 'Select month')
  19864. # => "<option value="">Select month</option>"
  19865. def prompt_option_tag(type, options)
  19866. prompt = case options
  19867. when Hash
  19868. default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
  19869. default_options.merge!(options)[type.to_sym]
  19870. when String
  19871. options
  19872. else
  19873. I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
  19874. end
  19875. prompt ? content_tag(:option, prompt, :value => '') : ''
  19876. end
  19877. # Builds hidden input tag for date part and value.
  19878. # build_hidden(:year, 2008)
  19879. # => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
  19880. def build_hidden(type, value)
  19881. select_options = {
  19882. :type => "hidden",
  19883. :id => input_id_from_type(type),
  19884. :name => input_name_from_type(type),
  19885. :value => value
  19886. }.merge!(@html_options.slice(:disabled))
  19887. select_options[:disabled] = 'disabled' if @options[:disabled]
  19888. tag(:input, select_options) + "\n".html_safe
  19889. end
  19890. # Returns the name attribute for the input tag.
  19891. # => post[written_on(1i)]
  19892. def input_name_from_type(type)
  19893. prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
  19894. prefix += "[#{@options[:index]}]" if @options.has_key?(:index)
  19895. field_name = @options[:field_name] || type
  19896. if @options[:include_position]
  19897. field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
  19898. end
  19899. @options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
  19900. end
  19901. # Returns the id attribute for the input tag.
  19902. # => "post_written_on_1i"
  19903. def input_id_from_type(type)
  19904. id = input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
  19905. id = @options[:namespace] + '_' + id if @options[:namespace]
  19906. id
  19907. end
  19908. # Given an ordering of datetime components, create the selection HTML
  19909. # and join them with their appropriate separators.
  19910. def build_selects_from_types(order)
  19911. select = ''
  19912. first_visible = order.find { |type| !@options[:"discard_#{type}"] }
  19913. order.reverse.each do |type|
  19914. separator = separator(type) unless type == first_visible # don't add before first visible field
  19915. select.insert(0, separator.to_s + send("select_#{type}").to_s)
  19916. end
  19917. select.html_safe
  19918. end
  19919. # Returns the separator for a given datetime component.
  19920. def separator(type)
  19921. return "" if @options[:use_hidden]
  19922. case type
  19923. when :year, :month, :day
  19924. @options[:"discard_#{type}"] ? "" : @options[:date_separator]
  19925. when :hour
  19926. (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
  19927. when :minute, :second
  19928. @options[:"discard_#{type}"] ? "" : @options[:time_separator]
  19929. end
  19930. end
  19931. end
  19932. class FormBuilder
  19933. # Wraps ActionView::Helpers::DateHelper#date_select for form builders:
  19934. #
  19935. # <%= form_for @person do |f| %>
  19936. # <%= f.date_select :birth_date %>
  19937. # <%= f.submit %>
  19938. # <% end %>
  19939. #
  19940. # Please refer to the documentation of the base helper for details.
  19941. def date_select(method, options = {}, html_options = {})
  19942. @template.date_select(@object_name, method, objectify_options(options), html_options)
  19943. end
  19944. # Wraps ActionView::Helpers::DateHelper#time_select for form builders:
  19945. #
  19946. # <%= form_for @race do |f| %>
  19947. # <%= f.time_select :average_lap %>
  19948. # <%= f.submit %>
  19949. # <% end %>
  19950. #
  19951. # Please refer to the documentation of the base helper for details.
  19952. def time_select(method, options = {}, html_options = {})
  19953. @template.time_select(@object_name, method, objectify_options(options), html_options)
  19954. end
  19955. # Wraps ActionView::Helpers::DateHelper#datetime_select for form builders:
  19956. #
  19957. # <%= form_for @person do |f| %>
  19958. # <%= f.time_select :last_request_at %>
  19959. # <%= f.submit %>
  19960. # <% end %>
  19961. #
  19962. # Please refer to the documentation of the base helper for details.
  19963. def datetime_select(method, options = {}, html_options = {})
  19964. @template.datetime_select(@object_name, method, objectify_options(options), html_options)
  19965. end
  19966. end
  19967. end
  19968. end
  19969. module ActionView
  19970. # = Action View Debug Helper
  19971. #
  19972. # Provides a set of methods for making it easier to debug Rails objects.
  19973. module Helpers
  19974. module DebugHelper
  19975. include TagHelper
  19976. # Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
  19977. # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
  19978. # Useful for inspecting an object at the time of rendering.
  19979. #
  19980. # @user = User.new({ username: 'testing', password: 'xyz', age: 42}) %>
  19981. # debug(@user)
  19982. # # =>
  19983. # <pre class='debug_dump'>--- !ruby/object:User
  19984. # attributes:
  19985. # &nbsp; updated_at:
  19986. # &nbsp; username: testing
  19987. #
  19988. # &nbsp; age: 42
  19989. # &nbsp; password: xyz
  19990. # &nbsp; created_at:
  19991. # attributes_cache: {}
  19992. #
  19993. # new_record: true
  19994. # </pre>
  19995. def debug(object)
  19996. Marshal::dump(object)
  19997. object = ERB::Util.html_escape(object.to_yaml).gsub(" ", "&nbsp; ").html_safe
  19998. content_tag(:pre, object, :class => "debug_dump")
  19999. rescue Exception # errors from Marshal or YAML
  20000. # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
  20001. content_tag(:code, object.to_yaml, :class => "debug_dump")
  20002. end
  20003. end
  20004. end
  20005. end
  20006. require 'cgi'
  20007. require 'action_view/helpers/date_helper'
  20008. require 'action_view/helpers/tag_helper'
  20009. require 'action_view/helpers/form_tag_helper'
  20010. require 'action_view/helpers/active_model_helper'
  20011. require 'action_view/helpers/tags'
  20012. require 'action_view/model_naming'
  20013. require 'active_support/core_ext/class/attribute_accessors'
  20014. require 'active_support/core_ext/hash/slice'
  20015. require 'active_support/core_ext/string/output_safety'
  20016. require 'active_support/core_ext/string/inflections'
  20017. module ActionView
  20018. # = Action View Form Helpers
  20019. module Helpers
  20020. # Form helpers are designed to make working with resources much easier
  20021. # compared to using vanilla HTML.
  20022. #
  20023. # Typically, a form designed to create or update a resource reflects the
  20024. # identity of the resource in several ways: (i) the url that the form is
  20025. # sent to (the form element's +action+ attribute) should result in a request
  20026. # being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
  20027. # parameter in the case of an existing resource), (ii) input fields should
  20028. # be named in such a way that in the controller their values appear in the
  20029. # appropriate places within the +params+ hash, and (iii) for an existing record,
  20030. # when the form is initially displayed, input fields corresponding to attributes
  20031. # of the resource should show the current values of those attributes.
  20032. #
  20033. # In Rails, this is usually achieved by creating the form using +form_for+ and
  20034. # a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
  20035. # tag and yields a form builder object that knows the model the form is about.
  20036. # Input fields are created by calling methods defined on the form builder, which
  20037. # means they are able to generate the appropriate names and default values
  20038. # corresponding to the model attributes, as well as convenient IDs, etc.
  20039. # Conventions in the generated field names allow controllers to receive form data
  20040. # nicely structured in +params+ with no effort on your side.
  20041. #
  20042. # For example, to create a new person you typically set up a new instance of
  20043. # +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
  20044. # in the view template pass that object to +form_for+:
  20045. #
  20046. # <%= form_for @person do |f| %>
  20047. # <%= f.label :first_name %>:
  20048. # <%= f.text_field :first_name %><br />
  20049. #
  20050. # <%= f.label :last_name %>:
  20051. # <%= f.text_field :last_name %><br />
  20052. #
  20053. # <%= f.submit %>
  20054. # <% end %>
  20055. #
  20056. # The HTML generated for this would be (modulus formatting):
  20057. #
  20058. # <form action="/people" class="new_person" id="new_person" method="post">
  20059. # <div style="margin:0;padding:0;display:inline">
  20060. # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
  20061. # </div>
  20062. # <label for="person_first_name">First name</label>:
  20063. # <input id="person_first_name" name="person[first_name]" type="text" /><br />
  20064. #
  20065. # <label for="person_last_name">Last name</label>:
  20066. # <input id="person_last_name" name="person[last_name]" type="text" /><br />
  20067. #
  20068. # <input name="commit" type="submit" value="Create Person" />
  20069. # </form>
  20070. #
  20071. # As you see, the HTML reflects knowledge about the resource in several spots,
  20072. # like the path the form should be submitted to, or the names of the input fields.
  20073. #
  20074. # In particular, thanks to the conventions followed in the generated field names, the
  20075. # controller gets a nested hash <tt>params[:person]</tt> with the person attributes
  20076. # set in the form. That hash is ready to be passed to <tt>Person.create</tt>:
  20077. #
  20078. # if @person = Person.create(params[:person])
  20079. # # success
  20080. # else
  20081. # # error handling
  20082. # end
  20083. #
  20084. # Interestingly, the exact same view code in the previous example can be used to edit
  20085. # a person. If <tt>@person</tt> is an existing record with name "John Smith" and ID 256,
  20086. # the code above as is would yield instead:
  20087. #
  20088. # <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
  20089. # <div style="margin:0;padding:0;display:inline">
  20090. # <input name="_method" type="hidden" value="patch" />
  20091. # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
  20092. # </div>
  20093. # <label for="person_first_name">First name</label>:
  20094. # <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br />
  20095. #
  20096. # <label for="person_last_name">Last name</label>:
  20097. # <input id="person_last_name" name="person[last_name]" type="text" value="Smith" /><br />
  20098. #
  20099. # <input name="commit" type="submit" value="Update Person" />
  20100. # </form>
  20101. #
  20102. # Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>.
  20103. # That works that way because the involved helpers know whether the resource is a new record or not,
  20104. # and generate HTML accordingly.
  20105. #
  20106. # The controller would receive the form data again in <tt>params[:person]</tt>, ready to be
  20107. # passed to <tt>Person#update</tt>:
  20108. #
  20109. # if @person.update(params[:person])
  20110. # # success
  20111. # else
  20112. # # error handling
  20113. # end
  20114. #
  20115. # That's how you typically work with resources.
  20116. module FormHelper
  20117. extend ActiveSupport::Concern
  20118. include FormTagHelper
  20119. include UrlHelper
  20120. include ModelNaming
  20121. # Creates a form that allows the user to create or update the attributes
  20122. # of a specific model object.
  20123. #
  20124. # The method can be used in several slightly different ways, depending on
  20125. # how much you wish to rely on Rails to infer automatically from the model
  20126. # how the form should be constructed. For a generic model object, a form
  20127. # can be created by passing +form_for+ a string or symbol representing
  20128. # the object we are concerned with:
  20129. #
  20130. # <%= form_for :person do |f| %>
  20131. # First name: <%= f.text_field :first_name %><br />
  20132. # Last name : <%= f.text_field :last_name %><br />
  20133. # Biography : <%= f.text_area :biography %><br />
  20134. # Admin? : <%= f.check_box :admin %><br />
  20135. # <%= f.submit %>
  20136. # <% end %>
  20137. #
  20138. # The variable +f+ yielded to the block is a FormBuilder object that
  20139. # incorporates the knowledge about the model object represented by
  20140. # <tt>:person</tt> passed to +form_for+. Methods defined on the FormBuilder
  20141. # are used to generate fields bound to this model. Thus, for example,
  20142. #
  20143. # <%= f.text_field :first_name %>
  20144. #
  20145. # will get expanded to
  20146. #
  20147. # <%= text_field :person, :first_name %>
  20148. # which results in an html <tt><input></tt> tag whose +name+ attribute is
  20149. # <tt>person[first_name]</tt>. This means that when the form is submitted,
  20150. # the value entered by the user will be available in the controller as
  20151. # <tt>params[:person][:first_name]</tt>.
  20152. #
  20153. # For fields generated in this way using the FormBuilder,
  20154. # if <tt>:person</tt> also happens to be the name of an instance variable
  20155. # <tt>@person</tt>, the default value of the field shown when the form is
  20156. # initially displayed (e.g. in the situation where you are editing an
  20157. # existing record) will be the value of the corresponding attribute of
  20158. # <tt>@person</tt>.
  20159. #
  20160. # The rightmost argument to +form_for+ is an
  20161. # optional hash of options -
  20162. #
  20163. # * <tt>:url</tt> - The URL the form is to be submitted to. This may be
  20164. # represented in the same way as values passed to +url_for+ or +link_to+.
  20165. # So for example you may use a named route directly. When the model is
  20166. # represented by a string or symbol, as in the example above, if the
  20167. # <tt>:url</tt> option is not specified, by default the form will be
  20168. # sent back to the current url (We will describe below an alternative
  20169. # resource-oriented usage of +form_for+ in which the URL does not need
  20170. # to be specified explicitly).
  20171. # * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
  20172. # id attributes on form elements. The namespace attribute will be prefixed
  20173. # with underscore on the generated HTML id.
  20174. # * <tt>:html</tt> - Optional HTML attributes for the form tag.
  20175. #
  20176. # Also note that +form_for+ doesn't create an exclusive scope. It's still
  20177. # possible to use both the stand-alone FormHelper methods and methods
  20178. # from FormTagHelper. For example:
  20179. #
  20180. # <%= form_for :person do |f| %>
  20181. # First name: <%= f.text_field :first_name %>
  20182. # Last name : <%= f.text_field :last_name %>
  20183. # Biography : <%= text_area :person, :biography %>
  20184. # Admin? : <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
  20185. # <%= f.submit %>
  20186. # <% end %>
  20187. #
  20188. # This also works for the methods in FormOptionHelper and DateHelper that
  20189. # are designed to work with an object as base, like
  20190. # FormOptionHelper#collection_select and DateHelper#datetime_select.
  20191. #
  20192. # === #form_for with a model object
  20193. #
  20194. # In the examples above, the object to be created or edited was
  20195. # represented by a symbol passed to +form_for+, and we noted that
  20196. # a string can also be used equivalently. It is also possible, however,
  20197. # to pass a model object itself to +form_for+. For example, if <tt>@post</tt>
  20198. # is an existing record you wish to edit, you can create the form using
  20199. #
  20200. # <%= form_for @post do |f| %>
  20201. # ...
  20202. # <% end %>
  20203. #
  20204. # This behaves in almost the same way as outlined previously, with a
  20205. # couple of small exceptions. First, the prefix used to name the input
  20206. # elements within the form (hence the key that denotes them in the +params+
  20207. # hash) is actually derived from the object's _class_, e.g. <tt>params[:post]</tt>
  20208. # if the object's class is +Post+. However, this can be overwritten using
  20209. # the <tt>:as</tt> option, e.g. -
  20210. #
  20211. # <%= form_for(@person, as: :client) do |f| %>
  20212. # ...
  20213. # <% end %>
  20214. #
  20215. # would result in <tt>params[:client]</tt>.
  20216. #
  20217. # Secondly, the field values shown when the form is initially displayed
  20218. # are taken from the attributes of the object passed to +form_for+,
  20219. # regardless of whether the object is an instance
  20220. # variable. So, for example, if we had a _local_ variable +post+
  20221. # representing an existing record,
  20222. #
  20223. # <%= form_for post do |f| %>
  20224. # ...
  20225. # <% end %>
  20226. #
  20227. # would produce a form with fields whose initial state reflect the current
  20228. # values of the attributes of +post+.
  20229. #
  20230. # === Resource-oriented style
  20231. #
  20232. # In the examples just shown, although not indicated explicitly, we still
  20233. # need to use the <tt>:url</tt> option in order to specify where the
  20234. # form is going to be sent. However, further simplification is possible
  20235. # if the record passed to +form_for+ is a _resource_, i.e. it corresponds
  20236. # to a set of RESTful routes, e.g. defined using the +resources+ method
  20237. # in <tt>config/routes.rb</tt>. In this case Rails will simply infer the
  20238. # appropriate URL from the record itself. For example,
  20239. #
  20240. # <%= form_for @post do |f| %>
  20241. # ...
  20242. # <% end %>
  20243. #
  20244. # is then equivalent to something like:
  20245. #
  20246. # <%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
  20247. # ...
  20248. # <% end %>
  20249. #
  20250. # And for a new record
  20251. #
  20252. # <%= form_for(Post.new) do |f| %>
  20253. # ...
  20254. # <% end %>
  20255. #
  20256. # is equivalent to something like:
  20257. #
  20258. # <%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
  20259. # ...
  20260. # <% end %>
  20261. #
  20262. # However you can still overwrite individual conventions, such as:
  20263. #
  20264. # <%= form_for(@post, url: super_posts_path) do |f| %>
  20265. # ...
  20266. # <% end %>
  20267. #
  20268. # You can also set the answer format, like this:
  20269. #
  20270. # <%= form_for(@post, format: :json) do |f| %>
  20271. # ...
  20272. # <% end %>
  20273. #
  20274. # For namespaced routes, like +admin_post_url+:
  20275. #
  20276. # <%= form_for([:admin, @post]) do |f| %>
  20277. # ...
  20278. # <% end %>
  20279. #
  20280. # If your resource has associations defined, for example, you want to add comments
  20281. # to the document given that the routes are set correctly:
  20282. #
  20283. # <%= form_for([@document, @comment]) do |f| %>
  20284. # ...
  20285. # <% end %>
  20286. #
  20287. # Where <tt>@document = Document.find(params[:id])</tt> and
  20288. # <tt>@comment = Comment.new</tt>.
  20289. #
  20290. # === Setting the method
  20291. #
  20292. # You can force the form to use the full array of HTTP verbs by setting
  20293. #
  20294. # method: (:get|:post|:patch|:put|:delete)
  20295. #
  20296. # in the options hash. If the verb is not GET or POST, which are natively
  20297. # supported by HTML forms, the form will be set to POST and a hidden input
  20298. # called _method will carry the intended verb for the server to interpret.
  20299. #
  20300. # === Unobtrusive JavaScript
  20301. #
  20302. # Specifying:
  20303. #
  20304. # remote: true
  20305. #
  20306. # in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
  20307. # behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular
  20308. # POST arrangement, but ultimately the behavior is the choice of the JavaScript driver implementor.
  20309. # Even though it's using JavaScript to serialize the form elements, the form submission will work just like
  20310. # a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>).
  20311. #
  20312. # Example:
  20313. #
  20314. # <%= form_for(@post, remote: true) do |f| %>
  20315. # ...
  20316. # <% end %>
  20317. #
  20318. # The HTML generated for this would be:
  20319. #
  20320. # <form action='http://www.example.com' method='post' data-remote='true'>
  20321. # <div style='margin:0;padding:0;display:inline'>
  20322. # <input name='_method' type='hidden' value='patch' />
  20323. # </div>
  20324. # ...
  20325. # </form>
  20326. #
  20327. # === Setting HTML options
  20328. #
  20329. # You can set data attributes directly by passing in a data hash, but all other HTML options must be wrapped in
  20330. # the HTML key. Example:
  20331. #
  20332. # <%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
  20333. # ...
  20334. # <% end %>
  20335. #
  20336. # The HTML generated for this would be:
  20337. #
  20338. # <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
  20339. # <div style='margin:0;padding:0;display:inline'>
  20340. # <input name='_method' type='hidden' value='patch' />
  20341. # </div>
  20342. # ...
  20343. # </form>
  20344. #
  20345. # === Removing hidden model id's
  20346. #
  20347. # The form_for method automatically includes the model id as a hidden field in the form.
  20348. # This is used to maintain the correlation between the form data and its associated model.
  20349. # Some ORM systems do not use IDs on nested models so in this case you want to be able
  20350. # to disable the hidden id.
  20351. #
  20352. # In the following example the Post model has many Comments stored within it in a NoSQL database,
  20353. # thus there is no primary key for comments.
  20354. #
  20355. # Example:
  20356. #
  20357. # <%= form_for(@post) do |f| %>
  20358. # <%= f.fields_for(:comments, include_id: false) do |cf| %>
  20359. # ...
  20360. # <% end %>
  20361. # <% end %>
  20362. #
  20363. # === Customized form builders
  20364. #
  20365. # You can also build forms using a customized FormBuilder class. Subclass
  20366. # FormBuilder and override or define some more helpers, then use your
  20367. # custom builder. For example, let's say you made a helper to
  20368. # automatically add labels to form inputs.
  20369. #
  20370. # <%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
  20371. # <%= f.text_field :first_name %>
  20372. # <%= f.text_field :last_name %>
  20373. # <%= f.text_area :biography %>
  20374. # <%= f.check_box :admin %>
  20375. # <%= f.submit %>
  20376. # <% end %>
  20377. #
  20378. # In this case, if you use this:
  20379. #
  20380. # <%= render f %>
  20381. #
  20382. # The rendered template is <tt>people/_labelling_form</tt> and the local
  20383. # variable referencing the form builder is called
  20384. # <tt>labelling_form</tt>.
  20385. #
  20386. # The custom FormBuilder class is automatically merged with the options
  20387. # of a nested fields_for call, unless it's explicitly set.
  20388. #
  20389. # In many cases you will want to wrap the above in another helper, so you
  20390. # could do something like the following:
  20391. #
  20392. # def labelled_form_for(record_or_name_or_array, *args, &block)
  20393. # options = args.extract_options!
  20394. # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &block)
  20395. # end
  20396. #
  20397. # If you don't need to attach a form to a model instance, then check out
  20398. # FormTagHelper#form_tag.
  20399. #
  20400. # === Form to external resources
  20401. #
  20402. # When you build forms to external resources sometimes you need to set an authenticity token or just render a form
  20403. # without it, for example when you submit data to a payment gateway number and types of fields could be limited.
  20404. #
  20405. # To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
  20406. #
  20407. # <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
  20408. # ...
  20409. # <% end %>
  20410. #
  20411. # If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
  20412. #
  20413. # <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
  20414. # ...
  20415. # <% end %>
  20416. def form_for(record, options = {}, &block)
  20417. raise ArgumentError, "Missing block" unless block_given?
  20418. html_options = options[:html] ||= {}
  20419. case record
  20420. when String, Symbol
  20421. object_name = record
  20422. object = nil
  20423. else
  20424. object = record.is_a?(Array) ? record.last : record
  20425. raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
  20426. object_name = options[:as] || model_name_from_record_or_class(object).param_key
  20427. apply_form_for_options!(record, object, options)
  20428. end
  20429. html_options[:data] = options.delete(:data) if options.has_key?(:data)
  20430. html_options[:remote] = options.delete(:remote) if options.has_key?(:remote)
  20431. html_options[:method] = options.delete(:method) if options.has_key?(:method)
  20432. html_options[:authenticity_token] = options.delete(:authenticity_token)
  20433. builder = instantiate_builder(object_name, object, options)
  20434. output = capture(builder, &block)
  20435. html_options[:multipart] = builder.multipart?
  20436. form_tag(options[:url] || {}, html_options) { output }
  20437. end
  20438. def apply_form_for_options!(record, object, options) #:nodoc:
  20439. object = convert_to_model(object)
  20440. as = options[:as]
  20441. action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
  20442. options[:html].reverse_merge!(
  20443. class: as ? "#{action}_#{as}" : dom_class(object, action),
  20444. id: as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
  20445. method: method
  20446. )
  20447. options[:url] ||= polymorphic_path(record, format: options.delete(:format))
  20448. end
  20449. private :apply_form_for_options!
  20450. # Creates a scope around a specific model object like form_for, but
  20451. # doesn't create the form tags themselves. This makes fields_for suitable
  20452. # for specifying additional model objects in the same form.
  20453. #
  20454. # Although the usage and purpose of +field_for+ is similar to +form_for+'s,
  20455. # its method signature is slightly different. Like +form_for+, it yields
  20456. # a FormBuilder object associated with a particular model object to a block,
  20457. # and within the block allows methods to be called on the builder to
  20458. # generate fields associated with the model object. Fields may reflect
  20459. # a model object in two ways - how they are named (hence how submitted
  20460. # values appear within the +params+ hash in the controller) and what
  20461. # default values are shown when the form the fields appear in is first
  20462. # displayed. In order for both of these features to be specified independently,
  20463. # both an object name (represented by either a symbol or string) and the
  20464. # object itself can be passed to the method separately -
  20465. #
  20466. # <%= form_for @person do |person_form| %>
  20467. # First name: <%= person_form.text_field :first_name %>
  20468. # Last name : <%= person_form.text_field :last_name %>
  20469. #
  20470. # <%= fields_for :permission, @person.permission do |permission_fields| %>
  20471. # Admin? : <%= permission_fields.check_box :admin %>
  20472. # <% end %>
  20473. #
  20474. # <%= f.submit %>
  20475. # <% end %>
  20476. #
  20477. # In this case, the checkbox field will be represented by an HTML +input+
  20478. # tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
  20479. # value will appear in the controller as <tt>params[:permission][:admin]</tt>.
  20480. # If <tt>@person.permission</tt> is an existing record with an attribute
  20481. # +admin+, the initial state of the checkbox when first displayed will
  20482. # reflect the value of <tt>@person.permission.admin</tt>.
  20483. #
  20484. # Often this can be simplified by passing just the name of the model
  20485. # object to +fields_for+ -
  20486. #
  20487. # <%= fields_for :permission do |permission_fields| %>
  20488. # Admin?: <%= permission_fields.check_box :admin %>
  20489. # <% end %>
  20490. #
  20491. # ...in which case, if <tt>:permission</tt> also happens to be the name of an
  20492. # instance variable <tt>@permission</tt>, the initial state of the input
  20493. # field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
  20494. #
  20495. # Alternatively, you can pass just the model object itself (if the first
  20496. # argument isn't a string or symbol +fields_for+ will realize that the
  20497. # name has been omitted) -
  20498. #
  20499. # <%= fields_for @person.permission do |permission_fields| %>
  20500. # Admin?: <%= permission_fields.check_box :admin %>
  20501. # <% end %>
  20502. #
  20503. # and +fields_for+ will derive the required name of the field from the
  20504. # _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
  20505. # of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
  20506. #
  20507. # Note: This also works for the methods in FormOptionHelper and
  20508. # DateHelper that are designed to work with an object as base, like
  20509. # FormOptionHelper#collection_select and DateHelper#datetime_select.
  20510. #
  20511. # === Nested Attributes Examples
  20512. #
  20513. # When the object belonging to the current scope has a nested attribute
  20514. # writer for a certain attribute, fields_for will yield a new scope
  20515. # for that attribute. This allows you to create forms that set or change
  20516. # the attributes of a parent object and its associations in one go.
  20517. #
  20518. # Nested attribute writers are normal setter methods named after an
  20519. # association. The most common way of defining these writers is either
  20520. # with +accepts_nested_attributes_for+ in a model definition or by
  20521. # defining a method with the proper name. For example: the attribute
  20522. # writer for the association <tt>:address</tt> is called
  20523. # <tt>address_attributes=</tt>.
  20524. #
  20525. # Whether a one-to-one or one-to-many style form builder will be yielded
  20526. # depends on whether the normal reader method returns a _single_ object
  20527. # or an _array_ of objects.
  20528. #
  20529. # ==== One-to-one
  20530. #
  20531. # Consider a Person class which returns a _single_ Address from the
  20532. # <tt>address</tt> reader method and responds to the
  20533. # <tt>address_attributes=</tt> writer method:
  20534. #
  20535. # class Person
  20536. # def address
  20537. # @address
  20538. # end
  20539. #
  20540. # def address_attributes=(attributes)
  20541. # # Process the attributes hash
  20542. # end
  20543. # end
  20544. #
  20545. # This model can now be used with a nested fields_for, like so:
  20546. #
  20547. # <%= form_for @person do |person_form| %>
  20548. # ...
  20549. # <%= person_form.fields_for :address do |address_fields| %>
  20550. # Street : <%= address_fields.text_field :street %>
  20551. # Zip code: <%= address_fields.text_field :zip_code %>
  20552. # <% end %>
  20553. # ...
  20554. # <% end %>
  20555. #
  20556. # When address is already an association on a Person you can use
  20557. # +accepts_nested_attributes_for+ to define the writer method for you:
  20558. #
  20559. # class Person < ActiveRecord::Base
  20560. # has_one :address
  20561. # accepts_nested_attributes_for :address
  20562. # end
  20563. #
  20564. # If you want to destroy the associated model through the form, you have
  20565. # to enable it first using the <tt>:allow_destroy</tt> option for
  20566. # +accepts_nested_attributes_for+:
  20567. #
  20568. # class Person < ActiveRecord::Base
  20569. # has_one :address
  20570. # accepts_nested_attributes_for :address, allow_destroy: true
  20571. # end
  20572. #
  20573. # Now, when you use a form element with the <tt>_destroy</tt> parameter,
  20574. # with a value that evaluates to +true+, you will destroy the associated
  20575. # model (eg. 1, '1', true, or 'true'):
  20576. #
  20577. # <%= form_for @person do |person_form| %>
  20578. # ...
  20579. # <%= person_form.fields_for :address do |address_fields| %>
  20580. # ...
  20581. # Delete: <%= address_fields.check_box :_destroy %>
  20582. # <% end %>
  20583. # ...
  20584. # <% end %>
  20585. #
  20586. # ==== One-to-many
  20587. #
  20588. # Consider a Person class which returns an _array_ of Project instances
  20589. # from the <tt>projects</tt> reader method and responds to the
  20590. # <tt>projects_attributes=</tt> writer method:
  20591. #
  20592. # class Person
  20593. # def projects
  20594. # [@project1, @project2]
  20595. # end
  20596. #
  20597. # def projects_attributes=(attributes)
  20598. # # Process the attributes hash
  20599. # end
  20600. # end
  20601. #
  20602. # Note that the <tt>projects_attributes=</tt> writer method is in fact
  20603. # required for fields_for to correctly identify <tt>:projects</tt> as a
  20604. # collection, and the correct indices to be set in the form markup.
  20605. #
  20606. # When projects is already an association on Person you can use
  20607. # +accepts_nested_attributes_for+ to define the writer method for you:
  20608. #
  20609. # class Person < ActiveRecord::Base
  20610. # has_many :projects
  20611. # accepts_nested_attributes_for :projects
  20612. # end
  20613. #
  20614. # This model can now be used with a nested fields_for. The block given to
  20615. # the nested fields_for call will be repeated for each instance in the
  20616. # collection:
  20617. #
  20618. # <%= form_for @person do |person_form| %>
  20619. # ...
  20620. # <%= person_form.fields_for :projects do |project_fields| %>
  20621. # <% if project_fields.object.active? %>
  20622. # Name: <%= project_fields.text_field :name %>
  20623. # <% end %>
  20624. # <% end %>
  20625. # ...
  20626. # <% end %>
  20627. #
  20628. # It's also possible to specify the instance to be used:
  20629. #
  20630. # <%= form_for @person do |person_form| %>
  20631. # ...
  20632. # <% @person.projects.each do |project| %>
  20633. # <% if project.active? %>
  20634. # <%= person_form.fields_for :projects, project do |project_fields| %>
  20635. # Name: <%= project_fields.text_field :name %>
  20636. # <% end %>
  20637. # <% end %>
  20638. # <% end %>
  20639. # ...
  20640. # <% end %>
  20641. #
  20642. # Or a collection to be used:
  20643. #
  20644. # <%= form_for @person do |person_form| %>
  20645. # ...
  20646. # <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
  20647. # Name: <%= project_fields.text_field :name %>
  20648. # <% end %>
  20649. # ...
  20650. # <% end %>
  20651. #
  20652. # When projects is already an association on Person you can use
  20653. # +accepts_nested_attributes_for+ to define the writer method for you:
  20654. #
  20655. # class Person < ActiveRecord::Base
  20656. # has_many :projects
  20657. # accepts_nested_attributes_for :projects
  20658. # end
  20659. #
  20660. # If you want to destroy any of the associated models through the
  20661. # form, you have to enable it first using the <tt>:allow_destroy</tt>
  20662. # option for +accepts_nested_attributes_for+:
  20663. #
  20664. # class Person < ActiveRecord::Base
  20665. # has_many :projects
  20666. # accepts_nested_attributes_for :projects, allow_destroy: true
  20667. # end
  20668. #
  20669. # This will allow you to specify which models to destroy in the
  20670. # attributes hash by adding a form element for the <tt>_destroy</tt>
  20671. # parameter with a value that evaluates to +true+
  20672. # (eg. 1, '1', true, or 'true'):
  20673. #
  20674. # <%= form_for @person do |person_form| %>
  20675. # ...
  20676. # <%= person_form.fields_for :projects do |project_fields| %>
  20677. # Delete: <%= project_fields.check_box :_destroy %>
  20678. # <% end %>
  20679. # ...
  20680. # <% end %>
  20681. #
  20682. # When a collection is used you might want to know the index of each
  20683. # object into the array. For this purpose, the <tt>index</tt> method
  20684. # is available in the FormBuilder object.
  20685. #
  20686. # <%= form_for @person do |person_form| %>
  20687. # ...
  20688. # <%= person_form.fields_for :projects do |project_fields| %>
  20689. # Project #<%= project_fields.index %>
  20690. # ...
  20691. # <% end %>
  20692. # ...
  20693. # <% end %>
  20694. #
  20695. # Note that fields_for will automatically generate a hidden field
  20696. # to store the ID of the record. There are circumstances where this
  20697. # hidden field is not needed and you can pass <tt>hidden_field_id: false</tt>
  20698. # to prevent fields_for from rendering it automatically.
  20699. def fields_for(record_name, record_object = nil, options = {}, &block)
  20700. builder = instantiate_builder(record_name, record_object, options)
  20701. capture(builder, &block)
  20702. end
  20703. # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
  20704. # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
  20705. # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
  20706. # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
  20707. # onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
  20708. # target labels for radio_button tags (where the value is used in the ID of the input tag).
  20709. #
  20710. # ==== Examples
  20711. # label(:post, :title)
  20712. # # => <label for="post_title">Title</label>
  20713. #
  20714. # You can localize your labels based on model and attribute names.
  20715. # For example you can define the following in your locale (e.g. en.yml)
  20716. #
  20717. # helpers:
  20718. # label:
  20719. # post:
  20720. # body: "Write your entire text here"
  20721. #
  20722. # Which then will result in
  20723. #
  20724. # label(:post, :body)
  20725. # # => <label for="post_body">Write your entire text here</label>
  20726. #
  20727. # Localization can also be based purely on the translation of the attribute-name
  20728. # (if you are using ActiveRecord):
  20729. #
  20730. # activerecord:
  20731. # attributes:
  20732. # post:
  20733. # cost: "Total cost"
  20734. #
  20735. # label(:post, :cost)
  20736. # # => <label for="post_cost">Total cost</label>
  20737. #
  20738. # label(:post, :title, "A short title")
  20739. # # => <label for="post_title">A short title</label>
  20740. #
  20741. # label(:post, :title, "A short title", class: "title_label")
  20742. # # => <label for="post_title" class="title_label">A short title</label>
  20743. #
  20744. # label(:post, :privacy, "Public Post", value: "public")
  20745. # # => <label for="post_privacy_public">Public Post</label>
  20746. #
  20747. # label(:post, :terms) do
  20748. # 'Accept <a href="/terms">Terms</a>.'.html_safe
  20749. # end
  20750. def label(object_name, method, content_or_options = nil, options = nil, &block)
  20751. Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
  20752. end
  20753. # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
  20754. # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
  20755. # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
  20756. # shown.
  20757. #
  20758. # ==== Examples
  20759. # text_field(:post, :title, size: 20)
  20760. # # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
  20761. #
  20762. # text_field(:post, :title, class: "create_input")
  20763. # # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
  20764. #
  20765. # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }")
  20766. # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }"/>
  20767. #
  20768. # text_field(:snippet, :code, size: 20, class: 'code_input')
  20769. # # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
  20770. def text_field(object_name, method, options = {})
  20771. Tags::TextField.new(object_name, method, self, options).render
  20772. end
  20773. # Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
  20774. # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
  20775. # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
  20776. # shown. For security reasons this field is blank by default; pass in a value via +options+ if this is not desired.
  20777. #
  20778. # ==== Examples
  20779. # password_field(:login, :pass, size: 20)
  20780. # # => <input type="password" id="login_pass" name="login[pass]" size="20" />
  20781. #
  20782. # password_field(:account, :secret, class: "form_input", value: @account.secret)
  20783. # # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
  20784. #
  20785. # password_field(:user, :password, onchange: "if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }")
  20786. # # => <input type="password" id="user_password" name="user[password]" onchange="if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }"/>
  20787. #
  20788. # password_field(:account, :pin, size: 20, class: 'form_input')
  20789. # # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
  20790. def password_field(object_name, method, options = {})
  20791. Tags::PasswordField.new(object_name, method, self, options).render
  20792. end
  20793. # Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
  20794. # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
  20795. # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
  20796. # shown.
  20797. #
  20798. # ==== Examples
  20799. # hidden_field(:signup, :pass_confirm)
  20800. # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
  20801. #
  20802. # hidden_field(:post, :tag_list)
  20803. # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
  20804. #
  20805. # hidden_field(:user, :token)
  20806. # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
  20807. def hidden_field(object_name, method, options = {})
  20808. Tags::HiddenField.new(object_name, method, self, options).render
  20809. end
  20810. # Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
  20811. # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
  20812. # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
  20813. # shown.
  20814. #
  20815. # Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
  20816. #
  20817. # ==== Options
  20818. # * Creates standard HTML attributes for the tag.
  20819. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  20820. # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
  20821. # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
  20822. #
  20823. # ==== Examples
  20824. # file_field(:user, :avatar)
  20825. # # => <input type="file" id="user_avatar" name="user[avatar]" />
  20826. #
  20827. # file_field(:post, :image, :multiple => true)
  20828. # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
  20829. #
  20830. # file_field(:post, :attached, accept: 'text/html')
  20831. # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
  20832. #
  20833. # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
  20834. # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
  20835. #
  20836. # file_field(:attachment, :file, class: 'file_input')
  20837. # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
  20838. def file_field(object_name, method, options = {})
  20839. Tags::FileField.new(object_name, method, self, options).render
  20840. end
  20841. # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
  20842. # on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
  20843. # hash with +options+.
  20844. #
  20845. # ==== Examples
  20846. # text_area(:post, :body, cols: 20, rows: 40)
  20847. # # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
  20848. # # #{@post.body}
  20849. # # </textarea>
  20850. #
  20851. # text_area(:comment, :text, size: "20x30")
  20852. # # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
  20853. # # #{@comment.text}
  20854. # # </textarea>
  20855. #
  20856. # text_area(:application, :notes, cols: 40, rows: 15, class: 'app_input')
  20857. # # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
  20858. # # #{@application.notes}
  20859. # # </textarea>
  20860. #
  20861. # text_area(:entry, :body, size: "20x20", disabled: 'disabled')
  20862. # # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
  20863. # # #{@entry.body}
  20864. # # </textarea>
  20865. def text_area(object_name, method, options = {})
  20866. Tags::TextArea.new(object_name, method, self, options).render
  20867. end
  20868. # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
  20869. # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
  20870. # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
  20871. # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
  20872. # while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
  20873. #
  20874. # ==== Gotcha
  20875. #
  20876. # The HTML specification says unchecked check boxes are not successful, and
  20877. # thus web browsers do not send them. Unfortunately this introduces a gotcha:
  20878. # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
  20879. # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
  20880. # any mass-assignment idiom like
  20881. #
  20882. # @invoice.update(params[:invoice])
  20883. #
  20884. # wouldn't update the flag.
  20885. #
  20886. # To prevent this the helper generates an auxiliary hidden field before
  20887. # the very check box. The hidden field has the same name and its
  20888. # attributes mimic an unchecked check box.
  20889. #
  20890. # This way, the client either sends only the hidden field (representing
  20891. # the check box is unchecked), or both fields. Since the HTML specification
  20892. # says key/value pairs have to be sent in the same order they appear in the
  20893. # form, and parameters extraction gets the last occurrence of any repeated
  20894. # key in the query string, that works for ordinary forms.
  20895. #
  20896. # Unfortunately that workaround does not work when the check box goes
  20897. # within an array-like parameter, as in
  20898. #
  20899. # <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
  20900. # <%= form.check_box :paid %>
  20901. # ...
  20902. # <% end %>
  20903. #
  20904. # because parameter name repetition is precisely what Rails seeks to distinguish
  20905. # the elements of the array. For each item with a checked check box you
  20906. # get an extra ghost item with only that attribute, assigned to "0".
  20907. #
  20908. # In that case it is preferable to either use +check_box_tag+ or to use
  20909. # hashes instead of arrays.
  20910. #
  20911. # # Let's say that @post.validated? is 1:
  20912. # check_box("post", "validated")
  20913. # # => <input name="post[validated]" type="hidden" value="0" />
  20914. # # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
  20915. #
  20916. # # Let's say that @puppy.gooddog is "no":
  20917. # check_box("puppy", "gooddog", {}, "yes", "no")
  20918. # # => <input name="puppy[gooddog]" type="hidden" value="no" />
  20919. # # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
  20920. #
  20921. # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
  20922. # # => <input name="eula[accepted]" type="hidden" value="no" />
  20923. # # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
  20924. def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
  20925. Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
  20926. end
  20927. # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
  20928. # assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
  20929. # radio button will be checked.
  20930. #
  20931. # To force the radio button to be checked pass <tt>checked: true</tt> in the
  20932. # +options+ hash. You may pass HTML options there as well.
  20933. #
  20934. # # Let's say that @post.category returns "rails":
  20935. # radio_button("post", "category", "rails")
  20936. # radio_button("post", "category", "java")
  20937. # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
  20938. # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
  20939. #
  20940. # radio_button("user", "receive_newsletter", "yes")
  20941. # radio_button("user", "receive_newsletter", "no")
  20942. # # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
  20943. # # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
  20944. def radio_button(object_name, method, tag_value, options = {})
  20945. Tags::RadioButton.new(object_name, method, self, tag_value, options).render
  20946. end
  20947. # Returns a text_field of type "color".
  20948. #
  20949. # color_field("car", "color")
  20950. # # => <input id="car_color" name="car[color]" type="color" value="#000000" />
  20951. def color_field(object_name, method, options = {})
  20952. Tags::ColorField.new(object_name, method, self, options).render
  20953. end
  20954. # Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
  20955. # assigned to the template (identified by +object_name+). Inputs of type "search" may be styled differently by
  20956. # some browsers.
  20957. #
  20958. # search_field(:user, :name)
  20959. # # => <input id="user_name" name="user[name]" type="search" />
  20960. # search_field(:user, :name, autosave: false)
  20961. # # => <input autosave="false" id="user_name" name="user[name]" type="search" />
  20962. # search_field(:user, :name, results: 3)
  20963. # # => <input id="user_name" name="user[name]" results="3" type="search" />
  20964. # # Assume request.host returns "www.example.com"
  20965. # search_field(:user, :name, autosave: true)
  20966. # # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" />
  20967. # search_field(:user, :name, onsearch: true)
  20968. # # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
  20969. # search_field(:user, :name, autosave: false, onsearch: true)
  20970. # # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
  20971. # search_field(:user, :name, autosave: true, onsearch: true)
  20972. # # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" type="search" />
  20973. def search_field(object_name, method, options = {})
  20974. Tags::SearchField.new(object_name, method, self, options).render
  20975. end
  20976. # Returns a text_field of type "tel".
  20977. #
  20978. # telephone_field("user", "phone")
  20979. # # => <input id="user_phone" name="user[phone]" type="tel" />
  20980. #
  20981. def telephone_field(object_name, method, options = {})
  20982. Tags::TelField.new(object_name, method, self, options).render
  20983. end
  20984. # aliases telephone_field
  20985. alias phone_field telephone_field
  20986. # Returns a text_field of type "date".
  20987. #
  20988. # date_field("user", "born_on")
  20989. # # => <input id="user_born_on" name="user[born_on]" type="date" />
  20990. #
  20991. # The default value is generated by trying to call "to_date"
  20992. # on the object's value, which makes it behave as expected for instances
  20993. # of DateTime and ActiveSupport::TimeWithZone. You can still override that
  20994. # by passing the "value" option explicitly, e.g.
  20995. #
  20996. # @user.born_on = Date.new(1984, 1, 27)
  20997. # date_field("user", "born_on", value: "1984-05-12")
  20998. # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-05-12" />
  20999. #
  21000. def date_field(object_name, method, options = {})
  21001. Tags::DateField.new(object_name, method, self, options).render
  21002. end
  21003. # Returns a text_field of type "time".
  21004. #
  21005. # The default value is generated by trying to call +strftime+ with "%T.%L"
  21006. # on the objects's value. It is still possible to override that
  21007. # by passing the "value" option.
  21008. #
  21009. # === Options
  21010. # * Accepts same options as time_field_tag
  21011. #
  21012. # === Example
  21013. # time_field("task", "started_at")
  21014. # # => <input id="task_started_at" name="task[started_at]" type="time" />
  21015. #
  21016. def time_field(object_name, method, options = {})
  21017. Tags::TimeField.new(object_name, method, self, options).render
  21018. end
  21019. # Returns a text_field of type "datetime".
  21020. #
  21021. # datetime_field("user", "born_on")
  21022. # # => <input id="user_born_on" name="user[born_on]" type="datetime" />
  21023. #
  21024. # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z"
  21025. # on the object's value, which makes it behave as expected for instances
  21026. # of DateTime and ActiveSupport::TimeWithZone.
  21027. #
  21028. # @user.born_on = Date.new(1984, 1, 12)
  21029. # datetime_field("user", "born_on")
  21030. # # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />
  21031. #
  21032. def datetime_field(object_name, method, options = {})
  21033. Tags::DatetimeField.new(object_name, method, self, options).render
  21034. end
  21035. # Returns a text_field of type "datetime-local".
  21036. #
  21037. # datetime_local_field("user", "born_on")
  21038. # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" />
  21039. #
  21040. # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T"
  21041. # on the object's value, which makes it behave as expected for instances
  21042. # of DateTime and ActiveSupport::TimeWithZone.
  21043. #
  21044. # @user.born_on = Date.new(1984, 1, 12)
  21045. # datetime_local_field("user", "born_on")
  21046. # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />
  21047. #
  21048. def datetime_local_field(object_name, method, options = {})
  21049. Tags::DatetimeLocalField.new(object_name, method, self, options).render
  21050. end
  21051. # Returns a text_field of type "month".
  21052. #
  21053. # month_field("user", "born_on")
  21054. # # => <input id="user_born_on" name="user[born_on]" type="month" />
  21055. #
  21056. # The default value is generated by trying to call +strftime+ with "%Y-%m"
  21057. # on the object's value, which makes it behave as expected for instances
  21058. # of DateTime and ActiveSupport::TimeWithZone.
  21059. #
  21060. # @user.born_on = Date.new(1984, 1, 27)
  21061. # month_field("user", "born_on")
  21062. # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-01" />
  21063. #
  21064. def month_field(object_name, method, options = {})
  21065. Tags::MonthField.new(object_name, method, self, options).render
  21066. end
  21067. # Returns a text_field of type "week".
  21068. #
  21069. # week_field("user", "born_on")
  21070. # # => <input id="user_born_on" name="user[born_on]" type="week" />
  21071. #
  21072. # The default value is generated by trying to call +strftime+ with "%Y-W%W"
  21073. # on the object's value, which makes it behave as expected for instances
  21074. # of DateTime and ActiveSupport::TimeWithZone.
  21075. #
  21076. # @user.born_on = Date.new(1984, 5, 12)
  21077. # week_field("user", "born_on")
  21078. # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-W19" />
  21079. #
  21080. def week_field(object_name, method, options = {})
  21081. Tags::WeekField.new(object_name, method, self, options).render
  21082. end
  21083. # Returns a text_field of type "url".
  21084. #
  21085. # url_field("user", "homepage")
  21086. # # => <input id="user_homepage" name="user[homepage]" type="url" />
  21087. #
  21088. def url_field(object_name, method, options = {})
  21089. Tags::UrlField.new(object_name, method, self, options).render
  21090. end
  21091. # Returns a text_field of type "email".
  21092. #
  21093. # email_field("user", "address")
  21094. # # => <input id="user_address" name="user[address]" type="email" />
  21095. #
  21096. def email_field(object_name, method, options = {})
  21097. Tags::EmailField.new(object_name, method, self, options).render
  21098. end
  21099. # Returns an input tag of type "number".
  21100. #
  21101. # ==== Options
  21102. # * Accepts same options as number_field_tag
  21103. def number_field(object_name, method, options = {})
  21104. Tags::NumberField.new(object_name, method, self, options).render
  21105. end
  21106. # Returns an input tag of type "range".
  21107. #
  21108. # ==== Options
  21109. # * Accepts same options as range_field_tag
  21110. def range_field(object_name, method, options = {})
  21111. Tags::RangeField.new(object_name, method, self, options).render
  21112. end
  21113. private
  21114. def instantiate_builder(record_name, record_object, options)
  21115. case record_name
  21116. when String, Symbol
  21117. object = record_object
  21118. object_name = record_name
  21119. else
  21120. object = record_name
  21121. object_name = model_name_from_record_or_class(object).param_key
  21122. end
  21123. builder = options[:builder] || default_form_builder
  21124. builder.new(object_name, object, self, options)
  21125. end
  21126. def default_form_builder
  21127. builder = ActionView::Base.default_form_builder
  21128. builder.respond_to?(:constantize) ? builder.constantize : builder
  21129. end
  21130. end
  21131. class FormBuilder
  21132. include ModelNaming
  21133. # The methods which wrap a form helper call.
  21134. class_attribute :field_helpers
  21135. self.field_helpers = FormHelper.instance_methods - [:form_for, :convert_to_model, :model_name_from_record_or_class]
  21136. attr_accessor :object_name, :object, :options
  21137. attr_reader :multipart, :index
  21138. alias :multipart? :multipart
  21139. def multipart=(multipart)
  21140. @multipart = multipart
  21141. if parent_builder = @options[:parent_builder]
  21142. parent_builder.multipart = multipart
  21143. end
  21144. end
  21145. def self._to_partial_path
  21146. @_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, '')
  21147. end
  21148. def to_partial_path
  21149. self.class._to_partial_path
  21150. end
  21151. def to_model
  21152. self
  21153. end
  21154. def initialize(object_name, object, template, options, block=nil)
  21155. if block
  21156. ActiveSupport::Deprecation.warn "Giving a block to FormBuilder is deprecated and has no effect anymore."
  21157. end
  21158. @nested_child_index = {}
  21159. @object_name, @object, @template, @options = object_name, object, template, options
  21160. @default_options = @options ? @options.slice(:index, :namespace) : {}
  21161. if @object_name.to_s.match(/\[\]$/)
  21162. if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
  21163. @auto_index = object.to_param
  21164. else
  21165. raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
  21166. end
  21167. end
  21168. @multipart = nil
  21169. @index = options[:index] || options[:child_index]
  21170. end
  21171. (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
  21172. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  21173. def #{selector}(method, options = {}) # def text_field(method, options = {})
  21174. @template.send( # @template.send(
  21175. #{selector.inspect}, # "text_field",
  21176. @object_name, # @object_name,
  21177. method, # method,
  21178. objectify_options(options)) # objectify_options(options))
  21179. end # end
  21180. RUBY_EVAL
  21181. end
  21182. # Creates a scope around a specific model object like form_for, but
  21183. # doesn't create the form tags themselves. This makes fields_for suitable
  21184. # for specifying additional model objects in the same form.
  21185. #
  21186. # Although the usage and purpose of +field_for+ is similar to +form_for+'s,
  21187. # its method signature is slightly different. Like +form_for+, it yields
  21188. # a FormBuilder object associated with a particular model object to a block,
  21189. # and within the block allows methods to be called on the builder to
  21190. # generate fields associated with the model object. Fields may reflect
  21191. # a model object in two ways - how they are named (hence how submitted
  21192. # values appear within the +params+ hash in the controller) and what
  21193. # default values are shown when the form the fields appear in is first
  21194. # displayed. In order for both of these features to be specified independently,
  21195. # both an object name (represented by either a symbol or string) and the
  21196. # object itself can be passed to the method separately -
  21197. #
  21198. # <%= form_for @person do |person_form| %>
  21199. # First name: <%= person_form.text_field :first_name %>
  21200. # Last name : <%= person_form.text_field :last_name %>
  21201. #
  21202. # <%= fields_for :permission, @person.permission do |permission_fields| %>
  21203. # Admin? : <%= permission_fields.check_box :admin %>
  21204. # <% end %>
  21205. #
  21206. # <%= f.submit %>
  21207. # <% end %>
  21208. #
  21209. # In this case, the checkbox field will be represented by an HTML +input+
  21210. # tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
  21211. # value will appear in the controller as <tt>params[:permission][:admin]</tt>.
  21212. # If <tt>@person.permission</tt> is an existing record with an attribute
  21213. # +admin+, the initial state of the checkbox when first displayed will
  21214. # reflect the value of <tt>@person.permission.admin</tt>.
  21215. #
  21216. # Often this can be simplified by passing just the name of the model
  21217. # object to +fields_for+ -
  21218. #
  21219. # <%= fields_for :permission do |permission_fields| %>
  21220. # Admin?: <%= permission_fields.check_box :admin %>
  21221. # <% end %>
  21222. #
  21223. # ...in which case, if <tt>:permission</tt> also happens to be the name of an
  21224. # instance variable <tt>@permission</tt>, the initial state of the input
  21225. # field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
  21226. #
  21227. # Alternatively, you can pass just the model object itself (if the first
  21228. # argument isn't a string or symbol +fields_for+ will realize that the
  21229. # name has been omitted) -
  21230. #
  21231. # <%= fields_for @person.permission do |permission_fields| %>
  21232. # Admin?: <%= permission_fields.check_box :admin %>
  21233. # <% end %>
  21234. #
  21235. # and +fields_for+ will derive the required name of the field from the
  21236. # _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
  21237. # of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
  21238. #
  21239. # Note: This also works for the methods in FormOptionHelper and
  21240. # DateHelper that are designed to work with an object as base, like
  21241. # FormOptionHelper#collection_select and DateHelper#datetime_select.
  21242. #
  21243. # === Nested Attributes Examples
  21244. #
  21245. # When the object belonging to the current scope has a nested attribute
  21246. # writer for a certain attribute, fields_for will yield a new scope
  21247. # for that attribute. This allows you to create forms that set or change
  21248. # the attributes of a parent object and its associations in one go.
  21249. #
  21250. # Nested attribute writers are normal setter methods named after an
  21251. # association. The most common way of defining these writers is either
  21252. # with +accepts_nested_attributes_for+ in a model definition or by
  21253. # defining a method with the proper name. For example: the attribute
  21254. # writer for the association <tt>:address</tt> is called
  21255. # <tt>address_attributes=</tt>.
  21256. #
  21257. # Whether a one-to-one or one-to-many style form builder will be yielded
  21258. # depends on whether the normal reader method returns a _single_ object
  21259. # or an _array_ of objects.
  21260. #
  21261. # ==== One-to-one
  21262. #
  21263. # Consider a Person class which returns a _single_ Address from the
  21264. # <tt>address</tt> reader method and responds to the
  21265. # <tt>address_attributes=</tt> writer method:
  21266. #
  21267. # class Person
  21268. # def address
  21269. # @address
  21270. # end
  21271. #
  21272. # def address_attributes=(attributes)
  21273. # # Process the attributes hash
  21274. # end
  21275. # end
  21276. #
  21277. # This model can now be used with a nested fields_for, like so:
  21278. #
  21279. # <%= form_for @person do |person_form| %>
  21280. # ...
  21281. # <%= person_form.fields_for :address do |address_fields| %>
  21282. # Street : <%= address_fields.text_field :street %>
  21283. # Zip code: <%= address_fields.text_field :zip_code %>
  21284. # <% end %>
  21285. # ...
  21286. # <% end %>
  21287. #
  21288. # When address is already an association on a Person you can use
  21289. # +accepts_nested_attributes_for+ to define the writer method for you:
  21290. #
  21291. # class Person < ActiveRecord::Base
  21292. # has_one :address
  21293. # accepts_nested_attributes_for :address
  21294. # end
  21295. #
  21296. # If you want to destroy the associated model through the form, you have
  21297. # to enable it first using the <tt>:allow_destroy</tt> option for
  21298. # +accepts_nested_attributes_for+:
  21299. #
  21300. # class Person < ActiveRecord::Base
  21301. # has_one :address
  21302. # accepts_nested_attributes_for :address, allow_destroy: true
  21303. # end
  21304. #
  21305. # Now, when you use a form element with the <tt>_destroy</tt> parameter,
  21306. # with a value that evaluates to +true+, you will destroy the associated
  21307. # model (eg. 1, '1', true, or 'true'):
  21308. #
  21309. # <%= form_for @person do |person_form| %>
  21310. # ...
  21311. # <%= person_form.fields_for :address do |address_fields| %>
  21312. # ...
  21313. # Delete: <%= address_fields.check_box :_destroy %>
  21314. # <% end %>
  21315. # ...
  21316. # <% end %>
  21317. #
  21318. # ==== One-to-many
  21319. #
  21320. # Consider a Person class which returns an _array_ of Project instances
  21321. # from the <tt>projects</tt> reader method and responds to the
  21322. # <tt>projects_attributes=</tt> writer method:
  21323. #
  21324. # class Person
  21325. # def projects
  21326. # [@project1, @project2]
  21327. # end
  21328. #
  21329. # def projects_attributes=(attributes)
  21330. # # Process the attributes hash
  21331. # end
  21332. # end
  21333. #
  21334. # Note that the <tt>projects_attributes=</tt> writer method is in fact
  21335. # required for fields_for to correctly identify <tt>:projects</tt> as a
  21336. # collection, and the correct indices to be set in the form markup.
  21337. #
  21338. # When projects is already an association on Person you can use
  21339. # +accepts_nested_attributes_for+ to define the writer method for you:
  21340. #
  21341. # class Person < ActiveRecord::Base
  21342. # has_many :projects
  21343. # accepts_nested_attributes_for :projects
  21344. # end
  21345. #
  21346. # This model can now be used with a nested fields_for. The block given to
  21347. # the nested fields_for call will be repeated for each instance in the
  21348. # collection:
  21349. #
  21350. # <%= form_for @person do |person_form| %>
  21351. # ...
  21352. # <%= person_form.fields_for :projects do |project_fields| %>
  21353. # <% if project_fields.object.active? %>
  21354. # Name: <%= project_fields.text_field :name %>
  21355. # <% end %>
  21356. # <% end %>
  21357. # ...
  21358. # <% end %>
  21359. #
  21360. # It's also possible to specify the instance to be used:
  21361. #
  21362. # <%= form_for @person do |person_form| %>
  21363. # ...
  21364. # <% @person.projects.each do |project| %>
  21365. # <% if project.active? %>
  21366. # <%= person_form.fields_for :projects, project do |project_fields| %>
  21367. # Name: <%= project_fields.text_field :name %>
  21368. # <% end %>
  21369. # <% end %>
  21370. # <% end %>
  21371. # ...
  21372. # <% end %>
  21373. #
  21374. # Or a collection to be used:
  21375. #
  21376. # <%= form_for @person do |person_form| %>
  21377. # ...
  21378. # <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
  21379. # Name: <%= project_fields.text_field :name %>
  21380. # <% end %>
  21381. # ...
  21382. # <% end %>
  21383. #
  21384. # When projects is already an association on Person you can use
  21385. # +accepts_nested_attributes_for+ to define the writer method for you:
  21386. #
  21387. # class Person < ActiveRecord::Base
  21388. # has_many :projects
  21389. # accepts_nested_attributes_for :projects
  21390. # end
  21391. #
  21392. # If you want to destroy any of the associated models through the
  21393. # form, you have to enable it first using the <tt>:allow_destroy</tt>
  21394. # option for +accepts_nested_attributes_for+:
  21395. #
  21396. # class Person < ActiveRecord::Base
  21397. # has_many :projects
  21398. # accepts_nested_attributes_for :projects, allow_destroy: true
  21399. # end
  21400. #
  21401. # This will allow you to specify which models to destroy in the
  21402. # attributes hash by adding a form element for the <tt>_destroy</tt>
  21403. # parameter with a value that evaluates to +true+
  21404. # (eg. 1, '1', true, or 'true'):
  21405. #
  21406. # <%= form_for @person do |person_form| %>
  21407. # ...
  21408. # <%= person_form.fields_for :projects do |project_fields| %>
  21409. # Delete: <%= project_fields.check_box :_destroy %>
  21410. # <% end %>
  21411. # ...
  21412. # <% end %>
  21413. #
  21414. # When a collection is used you might want to know the index of each
  21415. # object into the array. For this purpose, the <tt>index</tt> method
  21416. # is available in the FormBuilder object.
  21417. #
  21418. # <%= form_for @person do |person_form| %>
  21419. # ...
  21420. # <%= person_form.fields_for :projects do |project_fields| %>
  21421. # Project #<%= project_fields.index %>
  21422. # ...
  21423. # <% end %>
  21424. # ...
  21425. # <% end %>
  21426. #
  21427. # Note that fields_for will automatically generate a hidden field
  21428. # to store the ID of the record. There are circumstances where this
  21429. # hidden field is not needed and you can pass <tt>hidden_field_id: false</tt>
  21430. # to prevent fields_for from rendering it automatically.
  21431. def fields_for(record_name, record_object = nil, fields_options = {}, &block)
  21432. fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
  21433. fields_options[:builder] ||= options[:builder]
  21434. fields_options[:namespace] = options[:namespace]
  21435. fields_options[:parent_builder] = self
  21436. case record_name
  21437. when String, Symbol
  21438. if nested_attributes_association?(record_name)
  21439. return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
  21440. end
  21441. else
  21442. record_object = record_name.is_a?(Array) ? record_name.last : record_name
  21443. record_name = model_name_from_record_or_class(record_object).param_key
  21444. end
  21445. index = if options.has_key?(:index)
  21446. options[:index]
  21447. elsif defined?(@auto_index)
  21448. self.object_name = @object_name.to_s.sub(/\[\]$/,"")
  21449. @auto_index
  21450. end
  21451. record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
  21452. fields_options[:child_index] = index
  21453. @template.fields_for(record_name, record_object, fields_options, &block)
  21454. end
  21455. # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
  21456. # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
  21457. # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
  21458. # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
  21459. # onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
  21460. # target labels for radio_button tags (where the value is used in the ID of the input tag).
  21461. #
  21462. # ==== Examples
  21463. # label(:post, :title)
  21464. # # => <label for="post_title">Title</label>
  21465. #
  21466. # You can localize your labels based on model and attribute names.
  21467. # For example you can define the following in your locale (e.g. en.yml)
  21468. #
  21469. # helpers:
  21470. # label:
  21471. # post:
  21472. # body: "Write your entire text here"
  21473. #
  21474. # Which then will result in
  21475. #
  21476. # label(:post, :body)
  21477. # # => <label for="post_body">Write your entire text here</label>
  21478. #
  21479. # Localization can also be based purely on the translation of the attribute-name
  21480. # (if you are using ActiveRecord):
  21481. #
  21482. # activerecord:
  21483. # attributes:
  21484. # post:
  21485. # cost: "Total cost"
  21486. #
  21487. # label(:post, :cost)
  21488. # # => <label for="post_cost">Total cost</label>
  21489. #
  21490. # label(:post, :title, "A short title")
  21491. # # => <label for="post_title">A short title</label>
  21492. #
  21493. # label(:post, :title, "A short title", class: "title_label")
  21494. # # => <label for="post_title" class="title_label">A short title</label>
  21495. #
  21496. # label(:post, :privacy, "Public Post", value: "public")
  21497. # # => <label for="post_privacy_public">Public Post</label>
  21498. #
  21499. # label(:post, :terms) do
  21500. # 'Accept <a href="/terms">Terms</a>.'.html_safe
  21501. # end
  21502. def label(method, text = nil, options = {}, &block)
  21503. @template.label(@object_name, method, text, objectify_options(options), &block)
  21504. end
  21505. # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
  21506. # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
  21507. # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
  21508. # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
  21509. # while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
  21510. #
  21511. # ==== Gotcha
  21512. #
  21513. # The HTML specification says unchecked check boxes are not successful, and
  21514. # thus web browsers do not send them. Unfortunately this introduces a gotcha:
  21515. # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
  21516. # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
  21517. # any mass-assignment idiom like
  21518. #
  21519. # @invoice.update(params[:invoice])
  21520. #
  21521. # wouldn't update the flag.
  21522. #
  21523. # To prevent this the helper generates an auxiliary hidden field before
  21524. # the very check box. The hidden field has the same name and its
  21525. # attributes mimic an unchecked check box.
  21526. #
  21527. # This way, the client either sends only the hidden field (representing
  21528. # the check box is unchecked), or both fields. Since the HTML specification
  21529. # says key/value pairs have to be sent in the same order they appear in the
  21530. # form, and parameters extraction gets the last occurrence of any repeated
  21531. # key in the query string, that works for ordinary forms.
  21532. #
  21533. # Unfortunately that workaround does not work when the check box goes
  21534. # within an array-like parameter, as in
  21535. #
  21536. # <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
  21537. # <%= form.check_box :paid %>
  21538. # ...
  21539. # <% end %>
  21540. #
  21541. # because parameter name repetition is precisely what Rails seeks to distinguish
  21542. # the elements of the array. For each item with a checked check box you
  21543. # get an extra ghost item with only that attribute, assigned to "0".
  21544. #
  21545. # In that case it is preferable to either use +check_box_tag+ or to use
  21546. # hashes instead of arrays.
  21547. #
  21548. # # Let's say that @post.validated? is 1:
  21549. # check_box("post", "validated")
  21550. # # => <input name="post[validated]" type="hidden" value="0" />
  21551. # # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
  21552. #
  21553. # # Let's say that @puppy.gooddog is "no":
  21554. # check_box("puppy", "gooddog", {}, "yes", "no")
  21555. # # => <input name="puppy[gooddog]" type="hidden" value="no" />
  21556. # # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
  21557. #
  21558. # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
  21559. # # => <input name="eula[accepted]" type="hidden" value="no" />
  21560. # # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
  21561. def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
  21562. @template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
  21563. end
  21564. # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
  21565. # assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
  21566. # radio button will be checked.
  21567. #
  21568. # To force the radio button to be checked pass <tt>checked: true</tt> in the
  21569. # +options+ hash. You may pass HTML options there as well.
  21570. #
  21571. # # Let's say that @post.category returns "rails":
  21572. # radio_button("post", "category", "rails")
  21573. # radio_button("post", "category", "java")
  21574. # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
  21575. # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
  21576. #
  21577. # radio_button("user", "receive_newsletter", "yes")
  21578. # radio_button("user", "receive_newsletter", "no")
  21579. # # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
  21580. # # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
  21581. def radio_button(method, tag_value, options = {})
  21582. @template.radio_button(@object_name, method, tag_value, objectify_options(options))
  21583. end
  21584. # Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
  21585. # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
  21586. # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
  21587. # shown.
  21588. #
  21589. # ==== Examples
  21590. # hidden_field(:signup, :pass_confirm)
  21591. # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
  21592. #
  21593. # hidden_field(:post, :tag_list)
  21594. # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
  21595. #
  21596. # hidden_field(:user, :token)
  21597. # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
  21598. #
  21599. def hidden_field(method, options = {})
  21600. @emitted_hidden_id = true if method == :id
  21601. @template.hidden_field(@object_name, method, objectify_options(options))
  21602. end
  21603. # Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
  21604. # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
  21605. # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
  21606. # shown.
  21607. #
  21608. # Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
  21609. #
  21610. # ==== Options
  21611. # * Creates standard HTML attributes for the tag.
  21612. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  21613. # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
  21614. # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
  21615. #
  21616. # ==== Examples
  21617. # file_field(:user, :avatar)
  21618. # # => <input type="file" id="user_avatar" name="user[avatar]" />
  21619. #
  21620. # file_field(:post, :image, :multiple => true)
  21621. # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
  21622. #
  21623. # file_field(:post, :attached, accept: 'text/html')
  21624. # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
  21625. #
  21626. # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
  21627. # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
  21628. #
  21629. # file_field(:attachment, :file, class: 'file_input')
  21630. # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
  21631. def file_field(method, options = {})
  21632. self.multipart = true
  21633. @template.file_field(@object_name, method, objectify_options(options))
  21634. end
  21635. # Add the submit button for the given form. When no value is given, it checks
  21636. # if the object is a new resource or not to create the proper label:
  21637. #
  21638. # <%= form_for @post do |f| %>
  21639. # <%= f.submit %>
  21640. # <% end %>
  21641. #
  21642. # In the example above, if @post is a new record, it will use "Create Post" as
  21643. # submit button label, otherwise, it uses "Update Post".
  21644. #
  21645. # Those labels can be customized using I18n, under the helpers.submit key and accept
  21646. # the %{model} as translation interpolation:
  21647. #
  21648. # en:
  21649. # helpers:
  21650. # submit:
  21651. # create: "Create a %{model}"
  21652. # update: "Confirm changes to %{model}"
  21653. #
  21654. # It also searches for a key specific for the given object:
  21655. #
  21656. # en:
  21657. # helpers:
  21658. # submit:
  21659. # post:
  21660. # create: "Add %{model}"
  21661. #
  21662. def submit(value=nil, options={})
  21663. value, options = nil, value if value.is_a?(Hash)
  21664. value ||= submit_default_value
  21665. @template.submit_tag(value, options)
  21666. end
  21667. # Add the submit button for the given form. When no value is given, it checks
  21668. # if the object is a new resource or not to create the proper label:
  21669. #
  21670. # <%= form_for @post do |f| %>
  21671. # <%= f.button %>
  21672. # <% end %>
  21673. #
  21674. # In the example above, if @post is a new record, it will use "Create Post" as
  21675. # button label, otherwise, it uses "Update Post".
  21676. #
  21677. # Those labels can be customized using I18n, under the helpers.submit key
  21678. # (the same as submit helper) and accept the %{model} as translation interpolation:
  21679. #
  21680. # en:
  21681. # helpers:
  21682. # submit:
  21683. # create: "Create a %{model}"
  21684. # update: "Confirm changes to %{model}"
  21685. #
  21686. # It also searches for a key specific for the given object:
  21687. #
  21688. # en:
  21689. # helpers:
  21690. # submit:
  21691. # post:
  21692. # create: "Add %{model}"
  21693. #
  21694. # ==== Examples
  21695. # button("Create a post")
  21696. # # => <button name='button' type='submit'>Create post</button>
  21697. #
  21698. # button do
  21699. # content_tag(:strong, 'Ask me!')
  21700. # end
  21701. # # => <button name='button' type='submit'>
  21702. # # <strong>Ask me!</strong>
  21703. # # </button>
  21704. #
  21705. def button(value = nil, options = {}, &block)
  21706. value, options = nil, value if value.is_a?(Hash)
  21707. value ||= submit_default_value
  21708. @template.button_tag(value, options, &block)
  21709. end
  21710. def emitted_hidden_id?
  21711. @emitted_hidden_id ||= nil
  21712. end
  21713. private
  21714. def objectify_options(options)
  21715. @default_options.merge(options.merge(object: @object))
  21716. end
  21717. def submit_default_value
  21718. object = convert_to_model(@object)
  21719. key = object ? (object.persisted? ? :update : :create) : :submit
  21720. model = if object.class.respond_to?(:model_name)
  21721. object.class.model_name.human
  21722. else
  21723. @object_name.to_s.humanize
  21724. end
  21725. defaults = []
  21726. defaults << :"helpers.submit.#{object_name}.#{key}"
  21727. defaults << :"helpers.submit.#{key}"
  21728. defaults << "#{key.to_s.humanize} #{model}"
  21729. I18n.t(defaults.shift, model: model, default: defaults)
  21730. end
  21731. def nested_attributes_association?(association_name)
  21732. @object.respond_to?("#{association_name}_attributes=")
  21733. end
  21734. def fields_for_with_nested_attributes(association_name, association, options, block)
  21735. name = "#{object_name}[#{association_name}_attributes]"
  21736. association = convert_to_model(association)
  21737. if association.respond_to?(:persisted?)
  21738. association = [association] if @object.send(association_name).respond_to?(:to_ary)
  21739. elsif !association.respond_to?(:to_ary)
  21740. association = @object.send(association_name)
  21741. end
  21742. if association.respond_to?(:to_ary)
  21743. explicit_child_index = options[:child_index]
  21744. output = ActiveSupport::SafeBuffer.new
  21745. association.each do |child|
  21746. options[:child_index] = nested_child_index(name) unless explicit_child_index
  21747. output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
  21748. end
  21749. output
  21750. elsif association
  21751. fields_for_nested_model(name, association, options, block)
  21752. end
  21753. end
  21754. def fields_for_nested_model(name, object, fields_options, block)
  21755. object = convert_to_model(object)
  21756. emit_hidden_id = object.persisted? && fields_options.fetch(:include_id) {
  21757. options.fetch(:include_id, true)
  21758. }
  21759. @template.fields_for(name, object, fields_options) do |f|
  21760. output = @template.capture(f, &block)
  21761. output.concat f.hidden_field(:id) if output && emit_hidden_id && !f.emitted_hidden_id?
  21762. output
  21763. end
  21764. end
  21765. def nested_child_index(name)
  21766. @nested_child_index[name] ||= -1
  21767. @nested_child_index[name] += 1
  21768. end
  21769. end
  21770. end
  21771. ActiveSupport.on_load(:action_view) do
  21772. cattr_accessor(:default_form_builder) { ::ActionView::Helpers::FormBuilder }
  21773. end
  21774. end
  21775. require 'cgi'
  21776. require 'erb'
  21777. require 'action_view/helpers/form_helper'
  21778. require 'active_support/core_ext/string/output_safety'
  21779. require 'active_support/core_ext/array/extract_options'
  21780. require 'active_support/core_ext/array/wrap'
  21781. module ActionView
  21782. # = Action View Form Option Helpers
  21783. module Helpers
  21784. # Provides a number of methods for turning different kinds of containers into a set of option tags.
  21785. #
  21786. # The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash:
  21787. #
  21788. # * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
  21789. #
  21790. # select("post", "category", Post::CATEGORIES, {include_blank: true})
  21791. #
  21792. # could become:
  21793. #
  21794. # <select name="post[category]">
  21795. # <option></option>
  21796. # <option>joke</option>
  21797. # <option>poem</option>
  21798. # </select>
  21799. #
  21800. # Another common case is a select tag for a <tt>belongs_to</tt>-associated object.
  21801. #
  21802. # Example with @post.person_id => 2:
  21803. #
  21804. # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'None'})
  21805. #
  21806. # could become:
  21807. #
  21808. # <select name="post[person_id]">
  21809. # <option value="">None</option>
  21810. # <option value="1">David</option>
  21811. # <option value="2" selected="selected">Sam</option>
  21812. # <option value="3">Tobias</option>
  21813. # </select>
  21814. #
  21815. # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
  21816. #
  21817. # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
  21818. #
  21819. # could become:
  21820. #
  21821. # <select name="post[person_id]">
  21822. # <option value="">Select Person</option>
  21823. # <option value="1">David</option>
  21824. # <option value="2">Sam</option>
  21825. # <option value="3">Tobias</option>
  21826. # </select>
  21827. #
  21828. # Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
  21829. # option to be in the +html_options+ parameter.
  21830. #
  21831. # select("album[]", "genre", %w[rap rock country], {}, { index: nil })
  21832. #
  21833. # becomes:
  21834. #
  21835. # <select name="album[][genre]" id="album__genre">
  21836. # <option value="rap">rap</option>
  21837. # <option value="rock">rock</option>
  21838. # <option value="country">country</option>
  21839. # </select>
  21840. #
  21841. # * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
  21842. #
  21843. # select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
  21844. #
  21845. # could become:
  21846. #
  21847. # <select name="post[category]">
  21848. # <option></option>
  21849. # <option>joke</option>
  21850. # <option>poem</option>
  21851. # <option disabled="disabled">restricted</option>
  21852. # </select>
  21853. #
  21854. # When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
  21855. #
  21856. # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
  21857. #
  21858. # If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
  21859. # <select name="post[category_id]">
  21860. # <option value="1" disabled="disabled">2008 stuff</option>
  21861. # <option value="2" disabled="disabled">Christmas</option>
  21862. # <option value="3">Jokes</option>
  21863. # <option value="4">Poems</option>
  21864. # </select>
  21865. #
  21866. module FormOptionsHelper
  21867. # ERB::Util can mask some helpers like textilize. Make sure to include them.
  21868. include TextHelper
  21869. # Create a select tag and a series of contained option tags for the provided object and method.
  21870. # The option currently held by the object will be selected, provided that the object is available.
  21871. #
  21872. # There are two possible formats for the choices parameter, corresponding to other helpers' output:
  21873. # * A flat collection: see options_for_select
  21874. # * A nested collection: see grouped_options_for_select
  21875. #
  21876. # Example with @post.person_id => 1:
  21877. # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
  21878. #
  21879. # could become:
  21880. #
  21881. # <select name="post[person_id]">
  21882. # <option value=""></option>
  21883. # <option value="1" selected="selected">David</option>
  21884. # <option value="2">Sam</option>
  21885. # <option value="3">Tobias</option>
  21886. # </select>
  21887. #
  21888. # This can be used to provide a default set of options in the standard way: before rendering the create form, a
  21889. # new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
  21890. # to the database. Instead, a second model object is created when the create request is received.
  21891. # This allows the user to submit a form page more than once with the expected results of creating multiple records.
  21892. # In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
  21893. #
  21894. # By default, <tt>post.person_id</tt> is the selected option. Specify <tt>selected: value</tt> to use a different selection
  21895. # or <tt>selected: nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option
  21896. # tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled.
  21897. #
  21898. # ==== Gotcha
  21899. #
  21900. # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
  21901. # web browsers do not send any value to server. Unfortunately this introduces a gotcha:
  21902. # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
  21903. # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
  21904. # any mass-assignment idiom like
  21905. #
  21906. # @user.update(params[:user])
  21907. #
  21908. # wouldn't update roles.
  21909. #
  21910. # To prevent this the helper generates an auxiliary hidden field before
  21911. # every multiple select. The hidden field has the same name as multiple select and blank value.
  21912. #
  21913. # This way, the client either sends only the hidden field (representing
  21914. # the deselected multiple select box), or both fields. Since the HTML specification
  21915. # says key/value pairs have to be sent in the same order they appear in the
  21916. # form, and parameters extraction gets the last occurrence of any repeated
  21917. # key in the query string, that works for ordinary forms.
  21918. #
  21919. # In case if you don't want the helper to generate this hidden field you can specify
  21920. # <tt>include_hidden: false</tt> option.
  21921. #
  21922. def select(object, method, choices, options = {}, html_options = {})
  21923. Tags::Select.new(object, method, self, choices, options, html_options).render
  21924. end
  21925. # Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
  21926. # +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
  21927. # be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
  21928. # or <tt>:include_blank</tt> in the +options+ hash.
  21929. #
  21930. # The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
  21931. # of +collection+. The return values are used as the +value+ attribute and contents of each
  21932. # <tt><option></tt> tag, respectively. They can also be any object that responds to +call+, such
  21933. # as a +proc+, that will be called for each member of the +collection+ to
  21934. # retrieve the value/text.
  21935. #
  21936. # Example object structure for use with this method:
  21937. #
  21938. # class Post < ActiveRecord::Base
  21939. # belongs_to :author
  21940. # end
  21941. #
  21942. # class Author < ActiveRecord::Base
  21943. # has_many :posts
  21944. # def name_with_initial
  21945. # "#{first_name.first}. #{last_name}"
  21946. # end
  21947. # end
  21948. #
  21949. # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
  21950. #
  21951. # collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
  21952. #
  21953. # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
  21954. # <select name="post[author_id]">
  21955. # <option value="">Please select</option>
  21956. # <option value="1" selected="selected">D. Heinemeier Hansson</option>
  21957. # <option value="2">D. Thomas</option>
  21958. # <option value="3">M. Clark</option>
  21959. # </select>
  21960. def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
  21961. Tags::CollectionSelect.new(object, method, self, collection, value_method, text_method, options, html_options).render
  21962. end
  21963. # Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
  21964. # +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
  21965. # be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
  21966. # or <tt>:include_blank</tt> in the +options+ hash.
  21967. #
  21968. # Parameters:
  21969. # * +object+ - The instance of the class to be used for the select tag
  21970. # * +method+ - The attribute of +object+ corresponding to the select tag
  21971. # * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
  21972. # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
  21973. # array of child objects representing the <tt><option></tt> tags.
  21974. # * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
  21975. # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
  21976. # * +option_key_method+ - The name of a method which, when called on a child object of a member of
  21977. # +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
  21978. # * +option_value_method+ - The name of a method which, when called on a child object of a member of
  21979. # +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
  21980. #
  21981. # Example object structure for use with this method:
  21982. #
  21983. # class Continent < ActiveRecord::Base
  21984. # has_many :countries
  21985. # # attribs: id, name
  21986. # end
  21987. #
  21988. # class Country < ActiveRecord::Base
  21989. # belongs_to :continent
  21990. # # attribs: id, name, continent_id
  21991. # end
  21992. #
  21993. # class City < ActiveRecord::Base
  21994. # belongs_to :country
  21995. # # attribs: id, name, country_id
  21996. # end
  21997. #
  21998. # Sample usage:
  21999. #
  22000. # grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
  22001. #
  22002. # Possible output:
  22003. #
  22004. # <select name="city[country_id]">
  22005. # <optgroup label="Africa">
  22006. # <option value="1">South Africa</option>
  22007. # <option value="3">Somalia</option>
  22008. # </optgroup>
  22009. # <optgroup label="Europe">
  22010. # <option value="7" selected="selected">Denmark</option>
  22011. # <option value="2">Ireland</option>
  22012. # </optgroup>
  22013. # </select>
  22014. #
  22015. def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
  22016. Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render
  22017. end
  22018. # Return select and option tags for the given object and method, using
  22019. # #time_zone_options_for_select to generate the list of option tags.
  22020. #
  22021. # In addition to the <tt>:include_blank</tt> option documented above,
  22022. # this method also supports a <tt>:model</tt> option, which defaults
  22023. # to ActiveSupport::TimeZone. This may be used by users to specify a
  22024. # different time zone model object. (See +time_zone_options_for_select+
  22025. # for more information.)
  22026. #
  22027. # You can also supply an array of ActiveSupport::TimeZone objects
  22028. # as +priority_zones+, so that they will be listed above the rest of the
  22029. # (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
  22030. # for obtaining a list of the US time zones, or a Regexp to select the zones
  22031. # of your choice)
  22032. #
  22033. # Finally, this method supports a <tt>:default</tt> option, which selects
  22034. # a default ActiveSupport::TimeZone if the object's time zone is +nil+.
  22035. #
  22036. # time_zone_select( "user", "time_zone", nil, include_blank: true)
  22037. #
  22038. # time_zone_select( "user", "time_zone", nil, default: "Pacific Time (US & Canada)" )
  22039. #
  22040. # time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)")
  22041. #
  22042. # time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ])
  22043. #
  22044. # time_zone_select( "user", 'time_zone', /Australia/)
  22045. #
  22046. # time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, model: ActiveSupport::TimeZone)
  22047. def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
  22048. Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render
  22049. end
  22050. # Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
  22051. # where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
  22052. # the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
  22053. # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
  22054. # may also be an array of values to be selected when using a multiple select.
  22055. #
  22056. # options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
  22057. # # => <option value="$">Dollar</option>
  22058. # # => <option value="DKK">Kroner</option>
  22059. #
  22060. # options_for_select([ "VISA", "MasterCard" ], "MasterCard")
  22061. # # => <option>VISA</option>
  22062. # # => <option selected="selected">MasterCard</option>
  22063. #
  22064. # options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
  22065. # # => <option value="$20">Basic</option>
  22066. # # => <option value="$40" selected="selected">Plus</option>
  22067. #
  22068. # options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
  22069. # # => <option selected="selected">VISA</option>
  22070. # # => <option>MasterCard</option>
  22071. # # => <option selected="selected">Discover</option>
  22072. #
  22073. # You can optionally provide html attributes as the last element of the array.
  22074. #
  22075. # options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
  22076. # # => <option value="Denmark">Denmark</option>
  22077. # # => <option value="USA" class="bold" selected="selected">USA</option>
  22078. # # => <option value="Sweden" selected="selected">Sweden</option>
  22079. #
  22080. # options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
  22081. # # => <option value="$" class="bold">Dollar</option>
  22082. # # => <option value="DKK" onclick="alert('HI');">Kroner</option>
  22083. #
  22084. # If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value
  22085. # or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags.
  22086. #
  22087. # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: "Super Platinum")
  22088. # # => <option value="Free">Free</option>
  22089. # # => <option value="Basic">Basic</option>
  22090. # # => <option value="Advanced">Advanced</option>
  22091. # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
  22092. #
  22093. # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: ["Advanced", "Super Platinum"])
  22094. # # => <option value="Free">Free</option>
  22095. # # => <option value="Basic">Basic</option>
  22096. # # => <option value="Advanced" disabled="disabled">Advanced</option>
  22097. # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
  22098. #
  22099. # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], selected: "Free", disabled: "Super Platinum")
  22100. # # => <option value="Free" selected="selected">Free</option>
  22101. # # => <option value="Basic">Basic</option>
  22102. # # => <option value="Advanced">Advanced</option>
  22103. # # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
  22104. #
  22105. # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
  22106. def options_for_select(container, selected = nil)
  22107. return container if String === container
  22108. selected, disabled = extract_selected_and_disabled(selected).map do |r|
  22109. Array(r).map { |item| item.to_s }
  22110. end
  22111. container.map do |element|
  22112. html_attributes = option_html_attributes(element)
  22113. text, value = option_text_and_value(element).map { |item| item.to_s }
  22114. html_attributes[:selected] = 'selected' if option_value_selected?(value, selected)
  22115. html_attributes[:disabled] = 'disabled' if disabled && option_value_selected?(value, disabled)
  22116. html_attributes[:value] = value
  22117. content_tag_string(:option, text, html_attributes)
  22118. end.join("\n").html_safe
  22119. end
  22120. # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning
  22121. # the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
  22122. #
  22123. # options_from_collection_for_select(@people, 'id', 'name')
  22124. # # => <option value="#{person.id}">#{person.name}</option>
  22125. #
  22126. # This is more often than not used inside a #select_tag like this example:
  22127. #
  22128. # select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
  22129. #
  22130. # If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+
  22131. # will be selected option tag(s).
  22132. #
  22133. # If +selected+ is specified as a Proc, those members of the collection that return true for the anonymous
  22134. # function are the selected values.
  22135. #
  22136. # +selected+ can also be a hash, specifying both <tt>:selected</tt> and/or <tt>:disabled</tt> values as required.
  22137. #
  22138. # Be sure to specify the same class as the +value_method+ when specifying selected or disabled options.
  22139. # Failure to do this will produce undesired results. Example:
  22140. # options_from_collection_for_select(@people, 'id', 'name', '1')
  22141. # Will not select a person with the id of 1 because 1 (an Integer) is not the same as '1' (a string)
  22142. # options_from_collection_for_select(@people, 'id', 'name', 1)
  22143. # should produce the desired results.
  22144. def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
  22145. options = collection.map do |element|
  22146. [value_for_collection(element, text_method), value_for_collection(element, value_method)]
  22147. end
  22148. selected, disabled = extract_selected_and_disabled(selected)
  22149. select_deselect = {
  22150. :selected => extract_values_from_collection(collection, value_method, selected),
  22151. :disabled => extract_values_from_collection(collection, value_method, disabled)
  22152. }
  22153. options_for_select(options, select_deselect)
  22154. end
  22155. # Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but
  22156. # groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments.
  22157. #
  22158. # Parameters:
  22159. # * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
  22160. # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
  22161. # array of child objects representing the <tt><option></tt> tags.
  22162. # * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
  22163. # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
  22164. # * +option_key_method+ - The name of a method which, when called on a child object of a member of
  22165. # +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
  22166. # * +option_value_method+ - The name of a method which, when called on a child object of a member of
  22167. # +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
  22168. # * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
  22169. # which will have the +selected+ attribute set. Corresponds to the return value of one of the calls
  22170. # to +option_key_method+. If +nil+, no selection is made. Can also be a hash if disabled values are
  22171. # to be specified.
  22172. #
  22173. # Example object structure for use with this method:
  22174. #
  22175. # class Continent < ActiveRecord::Base
  22176. # has_many :countries
  22177. # # attribs: id, name
  22178. # end
  22179. #
  22180. # class Country < ActiveRecord::Base
  22181. # belongs_to :continent
  22182. # # attribs: id, name, continent_id
  22183. # end
  22184. #
  22185. # Sample usage:
  22186. # option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
  22187. #
  22188. # Possible output:
  22189. # <optgroup label="Africa">
  22190. # <option value="1">Egypt</option>
  22191. # <option value="4">Rwanda</option>
  22192. # ...
  22193. # </optgroup>
  22194. # <optgroup label="Asia">
  22195. # <option value="3" selected="selected">China</option>
  22196. # <option value="12">India</option>
  22197. # <option value="5">Japan</option>
  22198. # ...
  22199. # </optgroup>
  22200. #
  22201. # <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
  22202. # wrap the output in an appropriate <tt><select></tt> tag.
  22203. def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
  22204. collection.map do |group|
  22205. option_tags = options_from_collection_for_select(
  22206. group.send(group_method), option_key_method, option_value_method, selected_key)
  22207. content_tag(:optgroup, option_tags, :label => group.send(group_label_method))
  22208. end.join.html_safe
  22209. end
  22210. # Returns a string of <tt><option></tt> tags, like <tt>options_for_select</tt>, but
  22211. # wraps them with <tt><optgroup></tt> tags.
  22212. #
  22213. # Parameters:
  22214. # * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the
  22215. # <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a
  22216. # nested array of text-value pairs. See <tt>options_for_select</tt> for more info.
  22217. # Ex. ["North America",[["United States","US"],["Canada","CA"]]]
  22218. # * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
  22219. # which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
  22220. # as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
  22221. #
  22222. # Options:
  22223. # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this
  22224. # prepends an option with a generic prompt - "Please select" - or the given prompt string.
  22225. # * <tt>:divider</tt> - the divider for the options groups.
  22226. #
  22227. # grouped_options = [
  22228. # ['North America',
  22229. # [['United States','US'],'Canada']],
  22230. # ['Europe',
  22231. # ['Denmark','Germany','France']]
  22232. # ]
  22233. # grouped_options_for_select(grouped_options)
  22234. #
  22235. # grouped_options = {
  22236. # 'North America' => [['United States','US'], 'Canada'],
  22237. # 'Europe' => ['Denmark','Germany','France']
  22238. # }
  22239. # grouped_options_for_select(grouped_options)
  22240. #
  22241. # Possible output:
  22242. # <optgroup label="North America">
  22243. # <option value="US">United States</option>
  22244. # <option value="Canada">Canada</option>
  22245. # </optgroup>
  22246. # <optgroup label="Europe">
  22247. # <option value="Denmark">Denmark</option>
  22248. # <option value="Germany">Germany</option>
  22249. # <option value="France">France</option>
  22250. # </optgroup>
  22251. #
  22252. # grouped_options = [
  22253. # [['United States','US'], 'Canada'],
  22254. # ['Denmark','Germany','France']
  22255. # ]
  22256. # grouped_options_for_select(grouped_options, nil, divider: '---------')
  22257. #
  22258. # Possible output:
  22259. # <optgroup label="---------">
  22260. # <option value="US">United States</option>
  22261. # <option value="Canada">Canada</option>
  22262. # </optgroup>
  22263. # <optgroup label="---------">
  22264. # <option value="Denmark">Denmark</option>
  22265. # <option value="Germany">Germany</option>
  22266. # <option value="France">France</option>
  22267. # </optgroup>
  22268. #
  22269. # <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
  22270. # wrap the output in an appropriate <tt><select></tt> tag.
  22271. def grouped_options_for_select(grouped_options, selected_key = nil, options = {})
  22272. if options.is_a?(Hash)
  22273. prompt = options[:prompt]
  22274. divider = options[:divider]
  22275. else
  22276. prompt = options
  22277. options = {}
  22278. message = "Passing the prompt to grouped_options_for_select as an argument is deprecated. " \
  22279. "Please use an options hash like `{ prompt: #{prompt.inspect} }`."
  22280. ActiveSupport::Deprecation.warn message
  22281. end
  22282. body = "".html_safe
  22283. if prompt
  22284. body.safe_concat content_tag(:option, prompt_text(prompt), :value => "")
  22285. end
  22286. grouped_options.each do |container|
  22287. if divider
  22288. label = divider
  22289. else
  22290. label, container = container
  22291. end
  22292. body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), :label => label)
  22293. end
  22294. body
  22295. end
  22296. # Returns a string of option tags for pretty much any time zone in the
  22297. # world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
  22298. # marked as the selected option tag. You can also supply an array of
  22299. # ActiveSupport::TimeZone objects as +priority_zones+, so that they will
  22300. # be listed above the rest of the (long) list. (You can use
  22301. # ActiveSupport::TimeZone.us_zones as a convenience for obtaining a list
  22302. # of the US time zones, or a Regexp to select the zones of your choice)
  22303. #
  22304. # The +selected+ parameter must be either +nil+, or a string that names
  22305. # a ActiveSupport::TimeZone.
  22306. #
  22307. # By default, +model+ is the ActiveSupport::TimeZone constant (which can
  22308. # be obtained in Active Record as a value object). The only requirement
  22309. # is that the +model+ parameter be an object that responds to +all+, and
  22310. # returns an array of objects that represent time zones.
  22311. #
  22312. # NOTE: Only the option tags are returned, you have to wrap this call in
  22313. # a regular HTML select tag.
  22314. def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
  22315. zone_options = "".html_safe
  22316. zones = model.all
  22317. convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
  22318. if priority_zones
  22319. if priority_zones.is_a?(Regexp)
  22320. priority_zones = zones.select { |z| z =~ priority_zones }
  22321. end
  22322. zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
  22323. zone_options.safe_concat content_tag(:option, '-------------', :value => '', :disabled => 'disabled')
  22324. zone_options.safe_concat "\n"
  22325. zones.reject! { |z| priority_zones.include?(z) }
  22326. end
  22327. zone_options.safe_concat options_for_select(convert_zones[zones], selected)
  22328. end
  22329. # Returns radio button tags for the collection of existing return values
  22330. # of +method+ for +object+'s class. The value returned from calling
  22331. # +method+ on the instance +object+ will be selected. If calling +method+
  22332. # returns +nil+, no selection is made.
  22333. #
  22334. # The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
  22335. # methods to be called on each member of +collection+. The return values
  22336. # are used as the +value+ attribute and contents of each radio button tag,
  22337. # respectively. They can also be any object that responds to +call+, such
  22338. # as a +proc+, that will be called for each member of the +collection+ to
  22339. # retrieve the value/text.
  22340. #
  22341. # Example object structure for use with this method:
  22342. # class Post < ActiveRecord::Base
  22343. # belongs_to :author
  22344. # end
  22345. # class Author < ActiveRecord::Base
  22346. # has_many :posts
  22347. # def name_with_initial
  22348. # "#{first_name.first}. #{last_name}"
  22349. # end
  22350. # end
  22351. #
  22352. # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
  22353. # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
  22354. #
  22355. # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
  22356. # <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" />
  22357. # <label for="post_author_id_1">D. Heinemeier Hansson</label>
  22358. # <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
  22359. # <label for="post_author_id_2">D. Thomas</label>
  22360. # <input id="post_author_id_3" name="post[author_id]" type="radio" value="3" />
  22361. # <label for="post_author_id_3">M. Clark</label>
  22362. #
  22363. # It is also possible to customize the way the elements will be shown by
  22364. # giving a block to the method:
  22365. # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
  22366. # b.label { b.radio_button }
  22367. # end
  22368. #
  22369. # The argument passed to the block is a special kind of builder for this
  22370. # collection, which has the ability to generate the label and radio button
  22371. # for the current item in the collection, with proper text and value.
  22372. # Using it, you can change the label and radio button display order or
  22373. # even use the label as wrapper, as in the example above.
  22374. #
  22375. # The builder methods <tt>label</tt> and <tt>radio_button</tt> also accept
  22376. # extra html options:
  22377. # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
  22378. # b.label(class: "radio_button") { b.radio_button(class: "radio_button") }
  22379. # end
  22380. #
  22381. # There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
  22382. # <tt>value</tt>, which are the current item being rendered, its text and value methods,
  22383. # respectively. You can use them like this:
  22384. # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
  22385. # b.label(:"data-value" => b.value) { b.radio_button + b.text }
  22386. # end
  22387. def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
  22388. Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
  22389. end
  22390. # Returns check box tags for the collection of existing return values of
  22391. # +method+ for +object+'s class. The value returned from calling +method+
  22392. # on the instance +object+ will be selected. If calling +method+ returns
  22393. # +nil+, no selection is made.
  22394. #
  22395. # The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
  22396. # methods to be called on each member of +collection+. The return values
  22397. # are used as the +value+ attribute and contents of each check box tag,
  22398. # respectively. They can also be any object that responds to +call+, such
  22399. # as a +proc+, that will be called for each member of the +collection+ to
  22400. # retrieve the value/text.
  22401. #
  22402. # Example object structure for use with this method:
  22403. # class Post < ActiveRecord::Base
  22404. # has_and_belongs_to_many :author
  22405. # end
  22406. # class Author < ActiveRecord::Base
  22407. # has_and_belongs_to_many :posts
  22408. # def name_with_initial
  22409. # "#{first_name.first}. #{last_name}"
  22410. # end
  22411. # end
  22412. #
  22413. # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
  22414. # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
  22415. #
  22416. # If <tt>@post.author_ids</tt> is already <tt>[1]</tt>, this would return:
  22417. # <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
  22418. # <label for="post_author_ids_1">D. Heinemeier Hansson</label>
  22419. # <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
  22420. # <label for="post_author_ids_2">D. Thomas</label>
  22421. # <input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
  22422. # <label for="post_author_ids_3">M. Clark</label>
  22423. # <input name="post[author_ids][]" type="hidden" value="" />
  22424. #
  22425. # It is also possible to customize the way the elements will be shown by
  22426. # giving a block to the method:
  22427. # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
  22428. # b.label { b.check_box }
  22429. # end
  22430. #
  22431. # The argument passed to the block is a special kind of builder for this
  22432. # collection, which has the ability to generate the label and check box
  22433. # for the current item in the collection, with proper text and value.
  22434. # Using it, you can change the label and check box display order or even
  22435. # use the label as wrapper, as in the example above.
  22436. #
  22437. # The builder methods <tt>label</tt> and <tt>check_box</tt> also accept
  22438. # extra html options:
  22439. # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
  22440. # b.label(class: "check_box") { b.check_box(class: "check_box") }
  22441. # end
  22442. #
  22443. # There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
  22444. # <tt>value</tt>, which are the current item being rendered, its text and value methods,
  22445. # respectively. You can use them like this:
  22446. # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
  22447. # b.label(:"data-value" => b.value) { b.check_box + b.text }
  22448. # end
  22449. def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
  22450. Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
  22451. end
  22452. private
  22453. def option_html_attributes(element)
  22454. if Array === element
  22455. element.select { |e| Hash === e }.reduce({}, :merge!)
  22456. else
  22457. {}
  22458. end
  22459. end
  22460. def option_text_and_value(option)
  22461. # Options are [text, value] pairs or strings used for both.
  22462. if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
  22463. option = option.reject { |e| Hash === e } if Array === option
  22464. [option.first, option.last]
  22465. else
  22466. [option, option]
  22467. end
  22468. end
  22469. def option_value_selected?(value, selected)
  22470. Array(selected).include? value
  22471. end
  22472. def extract_selected_and_disabled(selected)
  22473. if selected.is_a?(Proc)
  22474. [selected, nil]
  22475. else
  22476. selected = Array.wrap(selected)
  22477. options = selected.extract_options!.symbolize_keys
  22478. selected_items = options.fetch(:selected, selected)
  22479. [selected_items, options[:disabled]]
  22480. end
  22481. end
  22482. def extract_values_from_collection(collection, value_method, selected)
  22483. if selected.is_a?(Proc)
  22484. collection.map do |element|
  22485. element.send(value_method) if selected.call(element)
  22486. end.compact
  22487. else
  22488. selected
  22489. end
  22490. end
  22491. def value_for_collection(item, value)
  22492. value.respond_to?(:call) ? value.call(item) : item.send(value)
  22493. end
  22494. def prompt_text(prompt)
  22495. prompt = prompt.kind_of?(String) ? prompt : I18n.translate('helpers.select.prompt', :default => 'Please select')
  22496. end
  22497. end
  22498. class FormBuilder
  22499. # Wraps ActionView::Helpers::FormOptionsHelper#select for form builders:
  22500. #
  22501. # <%= form_for @post do |f| %>
  22502. # <%= f.select :person_id, Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true }) %>
  22503. # <%= f.submit %>
  22504. # <% end %>
  22505. #
  22506. # Please refer to the documentation of the base helper for details.
  22507. def select(method, choices, options = {}, html_options = {})
  22508. @template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
  22509. end
  22510. # Wraps ActionView::Helpers::FormOptionsHelper#collection_select for form builders:
  22511. #
  22512. # <%= form_for @post do |f| %>
  22513. # <%= f.collection_select :person_id, Author.all, :id, :name_with_initial, prompt: true %>
  22514. # <%= f.submit %>
  22515. # <% end %>
  22516. #
  22517. # Please refer to the documentation of the base helper for details.
  22518. def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
  22519. @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
  22520. end
  22521. # Wraps ActionView::Helpers::FormOptionsHelper#grouped_collection_select for form builders:
  22522. #
  22523. # <%= form_for @city do |f| %>
  22524. # <%= f.grouped_collection_select :country_id, :country_id, @continents, :countries, :name, :id, :name %>
  22525. # <%= f.submit %>
  22526. # <% end %>
  22527. #
  22528. # Please refer to the documentation of the base helper for details.
  22529. def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
  22530. @template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
  22531. end
  22532. # Wraps ActionView::Helpers::FormOptionsHelper#time_zone_select for form builders:
  22533. #
  22534. # <%= form_for @user do |f| %>
  22535. # <%= f.time_zone_select :time_zone, nil, include_blank: true %>
  22536. # <%= f.submit %>
  22537. # <% end %>
  22538. #
  22539. # Please refer to the documentation of the base helper for details.
  22540. def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
  22541. @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
  22542. end
  22543. # Wraps ActionView::Helpers::FormOptionsHelper#collection_check_boxes for form builders:
  22544. #
  22545. # <%= form_for @post do |f| %>
  22546. # <%= f.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial %>
  22547. # <%= f.submit %>
  22548. # <% end %>
  22549. #
  22550. # Please refer to the documentation of the base helper for details.
  22551. def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
  22552. @template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
  22553. end
  22554. # Wraps ActionView::Helpers::FormOptionsHelper#collection_radio_buttons for form builders:
  22555. #
  22556. # <%= form_for @post do |f| %>
  22557. # <%= f.collection_radio_buttons :author_id, Author.all, :id, :name_with_initial %>
  22558. # <%= f.submit %>
  22559. # <% end %>
  22560. #
  22561. # Please refer to the documentation of the base helper for details.
  22562. def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
  22563. @template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
  22564. end
  22565. end
  22566. end
  22567. end
  22568. require 'cgi'
  22569. require 'action_view/helpers/tag_helper'
  22570. require 'active_support/core_ext/string/output_safety'
  22571. require 'active_support/core_ext/module/attribute_accessors'
  22572. module ActionView
  22573. # = Action View Form Tag Helpers
  22574. module Helpers
  22575. # Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like
  22576. # FormHelper does. Instead, you provide the names and values manually.
  22577. #
  22578. # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
  22579. # <tt>disabled: true</tt> will give <tt>disabled="disabled"</tt>.
  22580. module FormTagHelper
  22581. extend ActiveSupport::Concern
  22582. include UrlHelper
  22583. include TextHelper
  22584. mattr_accessor :embed_authenticity_token_in_remote_forms
  22585. self.embed_authenticity_token_in_remote_forms = false
  22586. # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
  22587. # ActionController::Base#url_for. The method for the form defaults to POST.
  22588. #
  22589. # ==== Options
  22590. # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
  22591. # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
  22592. # If "patch", "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
  22593. # is added to simulate the verb over post.
  22594. # * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
  22595. # pass custom authenticity token string, or to not add authenticity_token field at all
  22596. # (by passing <tt>false</tt>). Remote forms may omit the embedded authenticity token
  22597. # by setting <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
  22598. # This is helpful when you're fragment-caching the form. Remote forms get the
  22599. # authenticity from the <tt>meta</tt> tag, so embedding is unnecessary unless you
  22600. # support browsers without JavaScript.
  22601. # * A list of parameters to feed to the URL the form will be posted to.
  22602. # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
  22603. # submit behavior. By default this behavior is an ajax submit.
  22604. #
  22605. # ==== Examples
  22606. # form_tag('/posts')
  22607. # # => <form action="/posts" method="post">
  22608. #
  22609. # form_tag('/posts/1', method: :put)
  22610. # # => <form action="/posts/1" method="post"> ... <input name="_method" type="hidden" value="put" /> ...
  22611. #
  22612. # form_tag('/upload', multipart: true)
  22613. # # => <form action="/upload" method="post" enctype="multipart/form-data">
  22614. #
  22615. # <%= form_tag('/posts') do -%>
  22616. # <div><%= submit_tag 'Save' %></div>
  22617. # <% end -%>
  22618. # # => <form action="/posts" method="post"><div><input type="submit" name="commit" value="Save" /></div></form>
  22619. #
  22620. # <%= form_tag('/posts', remote: true) %>
  22621. # # => <form action="/posts" method="post" data-remote="true">
  22622. #
  22623. # form_tag('http://far.away.com/form', authenticity_token: false)
  22624. # # form without authenticity token
  22625. #
  22626. # form_tag('http://far.away.com/form', authenticity_token: "cf50faa3fe97702ca1ae")
  22627. # # form with custom authenticity token
  22628. #
  22629. def form_tag(url_for_options = {}, options = {}, &block)
  22630. html_options = html_options_for_form(url_for_options, options)
  22631. if block_given?
  22632. form_tag_in_block(html_options, &block)
  22633. else
  22634. form_tag_html(html_options)
  22635. end
  22636. end
  22637. # Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
  22638. # choice selection box.
  22639. #
  22640. # Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or
  22641. # associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
  22642. #
  22643. # ==== Options
  22644. # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
  22645. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  22646. # * <tt>:include_blank</tt> - If set to true, an empty option will be create
  22647. # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
  22648. # * Any other key creates standard HTML attributes for the tag.
  22649. #
  22650. # ==== Examples
  22651. # select_tag "people", options_from_collection_for_select(@people, "id", "name")
  22652. # # <select id="people" name="people"><option value="1">David</option></select>
  22653. #
  22654. # select_tag "people", "<option>David</option>".html_safe
  22655. # # => <select id="people" name="people"><option>David</option></select>
  22656. #
  22657. # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
  22658. # # => <select id="count" name="count"><option>1</option><option>2</option>
  22659. # # <option>3</option><option>4</option></select>
  22660. #
  22661. # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
  22662. # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
  22663. # # <option>Green</option><option>Blue</option></select>
  22664. #
  22665. # select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
  22666. # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
  22667. # # <option>Out</option></select>
  22668. #
  22669. # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input'
  22670. # # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
  22671. # # <option>Write</option></select>
  22672. #
  22673. # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
  22674. # # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
  22675. #
  22676. # select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
  22677. # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
  22678. #
  22679. # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, disabled: true
  22680. # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
  22681. # # <option>Paris</option><option>Rome</option></select>
  22682. #
  22683. # select_tag "credit_card", options_for_select([ "VISA", "MasterCard" ], "MasterCard")
  22684. # # => <select id="credit_card" name="credit_card"><option>VISA</option>
  22685. # # <option selected="selected">MasterCard</option></select>
  22686. def select_tag(name, option_tags = nil, options = {})
  22687. option_tags ||= ""
  22688. html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
  22689. if options.delete(:include_blank)
  22690. option_tags = content_tag(:option, '', :value => '').safe_concat(option_tags)
  22691. end
  22692. if prompt = options.delete(:prompt)
  22693. option_tags = content_tag(:option, prompt, :value => '').safe_concat(option_tags)
  22694. end
  22695. content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
  22696. end
  22697. # Creates a standard text field; use these text fields to input smaller chunks of text like a username
  22698. # or a search query.
  22699. #
  22700. # ==== Options
  22701. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  22702. # * <tt>:size</tt> - The number of visible characters that will fit in the input.
  22703. # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
  22704. # * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
  22705. # * Any other key creates standard HTML attributes for the tag.
  22706. #
  22707. # ==== Examples
  22708. # text_field_tag 'name'
  22709. # # => <input id="name" name="name" type="text" />
  22710. #
  22711. # text_field_tag 'query', 'Enter your search query here'
  22712. # # => <input id="query" name="query" type="text" value="Enter your search query here" />
  22713. #
  22714. # text_field_tag 'search', nil, placeholder: 'Enter search term...'
  22715. # # => <input id="search" name="search" placeholder="Enter search term..." type="text" />
  22716. #
  22717. # text_field_tag 'request', nil, class: 'special_input'
  22718. # # => <input class="special_input" id="request" name="request" type="text" />
  22719. #
  22720. # text_field_tag 'address', '', size: 75
  22721. # # => <input id="address" name="address" size="75" type="text" value="" />
  22722. #
  22723. # text_field_tag 'zip', nil, maxlength: 5
  22724. # # => <input id="zip" maxlength="5" name="zip" type="text" />
  22725. #
  22726. # text_field_tag 'payment_amount', '$0.00', disabled: true
  22727. # # => <input disabled="disabled" id="payment_amount" name="payment_amount" type="text" value="$0.00" />
  22728. #
  22729. # text_field_tag 'ip', '0.0.0.0', maxlength: 15, size: 20, class: "ip-input"
  22730. # # => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
  22731. def text_field_tag(name, value = nil, options = {})
  22732. tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
  22733. end
  22734. # Creates a label element. Accepts a block.
  22735. #
  22736. # ==== Options
  22737. # * Creates standard HTML attributes for the tag.
  22738. #
  22739. # ==== Examples
  22740. # label_tag 'name'
  22741. # # => <label for="name">Name</label>
  22742. #
  22743. # label_tag 'name', 'Your name'
  22744. # # => <label for="name">Your name</label>
  22745. #
  22746. # label_tag 'name', nil, class: 'small_label'
  22747. # # => <label for="name" class="small_label">Name</label>
  22748. def label_tag(name = nil, content_or_options = nil, options = nil, &block)
  22749. if block_given? && content_or_options.is_a?(Hash)
  22750. options = content_or_options = content_or_options.stringify_keys
  22751. else
  22752. options ||= {}
  22753. options = options.stringify_keys
  22754. end
  22755. options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
  22756. content_tag :label, content_or_options || name.to_s.humanize, options, &block
  22757. end
  22758. # Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
  22759. # data that should be hidden from the user.
  22760. #
  22761. # ==== Options
  22762. # * Creates standard HTML attributes for the tag.
  22763. #
  22764. # ==== Examples
  22765. # hidden_field_tag 'tags_list'
  22766. # # => <input id="tags_list" name="tags_list" type="hidden" />
  22767. #
  22768. # hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
  22769. # # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
  22770. #
  22771. # hidden_field_tag 'collected_input', '', onchange: "alert('Input collected!')"
  22772. # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
  22773. # # type="hidden" value="" />
  22774. def hidden_field_tag(name, value = nil, options = {})
  22775. text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
  22776. end
  22777. # Creates a file upload field. If you are using file uploads then you will also need
  22778. # to set the multipart option for the form tag:
  22779. #
  22780. # <%= form_tag '/upload', multipart: true do %>
  22781. # <label for="file">File to Upload</label> <%= file_field_tag "file" %>
  22782. # <%= submit_tag %>
  22783. # <% end %>
  22784. #
  22785. # The specified URL will then be passed a File object containing the selected file, or if the field
  22786. # was left blank, a StringIO object.
  22787. #
  22788. # ==== Options
  22789. # * Creates standard HTML attributes for the tag.
  22790. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  22791. # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
  22792. # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
  22793. #
  22794. # ==== Examples
  22795. # file_field_tag 'attachment'
  22796. # # => <input id="attachment" name="attachment" type="file" />
  22797. #
  22798. # file_field_tag 'avatar', class: 'profile_input'
  22799. # # => <input class="profile_input" id="avatar" name="avatar" type="file" />
  22800. #
  22801. # file_field_tag 'picture', disabled: true
  22802. # # => <input disabled="disabled" id="picture" name="picture" type="file" />
  22803. #
  22804. # file_field_tag 'resume', value: '~/resume.doc'
  22805. # # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
  22806. #
  22807. # file_field_tag 'user_pic', accept: 'image/png,image/gif,image/jpeg'
  22808. # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
  22809. #
  22810. # file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
  22811. # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
  22812. def file_field_tag(name, options = {})
  22813. text_field_tag(name, nil, options.update("type" => "file"))
  22814. end
  22815. # Creates a password field, a masked text field that will hide the users input behind a mask character.
  22816. #
  22817. # ==== Options
  22818. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  22819. # * <tt>:size</tt> - The number of visible characters that will fit in the input.
  22820. # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
  22821. # * Any other key creates standard HTML attributes for the tag.
  22822. #
  22823. # ==== Examples
  22824. # password_field_tag 'pass'
  22825. # # => <input id="pass" name="pass" type="password" />
  22826. #
  22827. # password_field_tag 'secret', 'Your secret here'
  22828. # # => <input id="secret" name="secret" type="password" value="Your secret here" />
  22829. #
  22830. # password_field_tag 'masked', nil, class: 'masked_input_field'
  22831. # # => <input class="masked_input_field" id="masked" name="masked" type="password" />
  22832. #
  22833. # password_field_tag 'token', '', size: 15
  22834. # # => <input id="token" name="token" size="15" type="password" value="" />
  22835. #
  22836. # password_field_tag 'key', nil, maxlength: 16
  22837. # # => <input id="key" maxlength="16" name="key" type="password" />
  22838. #
  22839. # password_field_tag 'confirm_pass', nil, disabled: true
  22840. # # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
  22841. #
  22842. # password_field_tag 'pin', '1234', maxlength: 4, size: 6, class: "pin_input"
  22843. # # => <input class="pin_input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
  22844. def password_field_tag(name = "password", value = nil, options = {})
  22845. text_field_tag(name, value, options.update("type" => "password"))
  22846. end
  22847. # Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
  22848. #
  22849. # ==== Options
  22850. # * <tt>:size</tt> - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10").
  22851. # * <tt>:rows</tt> - Specify the number of rows in the textarea
  22852. # * <tt>:cols</tt> - Specify the number of columns in the textarea
  22853. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  22854. # * <tt>:escape</tt> - By default, the contents of the text input are HTML escaped.
  22855. # If you need unescaped contents, set this to false.
  22856. # * Any other key creates standard HTML attributes for the tag.
  22857. #
  22858. # ==== Examples
  22859. # text_area_tag 'post'
  22860. # # => <textarea id="post" name="post"></textarea>
  22861. #
  22862. # text_area_tag 'bio', @user.bio
  22863. # # => <textarea id="bio" name="bio">This is my biography.</textarea>
  22864. #
  22865. # text_area_tag 'body', nil, rows: 10, cols: 25
  22866. # # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
  22867. #
  22868. # text_area_tag 'body', nil, size: "25x10"
  22869. # # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
  22870. #
  22871. # text_area_tag 'description', "Description goes here.", disabled: true
  22872. # # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
  22873. #
  22874. # text_area_tag 'comment', nil, class: 'comment_input'
  22875. # # => <textarea class="comment_input" id="comment" name="comment"></textarea>
  22876. def text_area_tag(name, content = nil, options = {})
  22877. options = options.stringify_keys
  22878. if size = options.delete("size")
  22879. options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
  22880. end
  22881. escape = options.delete("escape") { true }
  22882. content = ERB::Util.html_escape(content) if escape
  22883. content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
  22884. end
  22885. # Creates a check box form input tag.
  22886. #
  22887. # ==== Options
  22888. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  22889. # * Any other key creates standard HTML options for the tag.
  22890. #
  22891. # ==== Examples
  22892. # check_box_tag 'accept'
  22893. # # => <input id="accept" name="accept" type="checkbox" value="1" />
  22894. #
  22895. # check_box_tag 'rock', 'rock music'
  22896. # # => <input id="rock" name="rock" type="checkbox" value="rock music" />
  22897. #
  22898. # check_box_tag 'receive_email', 'yes', true
  22899. # # => <input checked="checked" id="receive_email" name="receive_email" type="checkbox" value="yes" />
  22900. #
  22901. # check_box_tag 'tos', 'yes', false, class: 'accept_tos'
  22902. # # => <input class="accept_tos" id="tos" name="tos" type="checkbox" value="yes" />
  22903. #
  22904. # check_box_tag 'eula', 'accepted', false, disabled: true
  22905. # # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
  22906. def check_box_tag(name, value = "1", checked = false, options = {})
  22907. html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
  22908. html_options["checked"] = "checked" if checked
  22909. tag :input, html_options
  22910. end
  22911. # Creates a radio button; use groups of radio buttons named the same to allow users to
  22912. # select from a group of options.
  22913. #
  22914. # ==== Options
  22915. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  22916. # * Any other key creates standard HTML options for the tag.
  22917. #
  22918. # ==== Examples
  22919. # radio_button_tag 'gender', 'male'
  22920. # # => <input id="gender_male" name="gender" type="radio" value="male" />
  22921. #
  22922. # radio_button_tag 'receive_updates', 'no', true
  22923. # # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
  22924. #
  22925. # radio_button_tag 'time_slot', "3:00 p.m.", false, disabled: true
  22926. # # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
  22927. #
  22928. # radio_button_tag 'color', "green", true, class: "color_input"
  22929. # # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
  22930. def radio_button_tag(name, value, checked = false, options = {})
  22931. html_options = { "type" => "radio", "name" => name, "id" => "#{sanitize_to_id(name)}_#{sanitize_to_id(value)}", "value" => value }.update(options.stringify_keys)
  22932. html_options["checked"] = "checked" if checked
  22933. tag :input, html_options
  22934. end
  22935. # Creates a submit button with the text <tt>value</tt> as the caption.
  22936. #
  22937. # ==== Options
  22938. # * <tt>:data</tt> - This option can be used to add custom data attributes.
  22939. # * <tt>:disabled</tt> - If true, the user will not be able to use this input.
  22940. # * Any other key creates standard HTML options for the tag.
  22941. #
  22942. # ==== Data attributes
  22943. #
  22944. # * <tt>confirm: 'question?'</tt> - If present the unobtrusive JavaScript
  22945. # drivers will provide a prompt with the question specified. If the user accepts,
  22946. # the form is processed normally, otherwise no action is taken.
  22947. # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
  22948. # disabled version of the submit button when the form is submitted. This feature is
  22949. # provided by the unobtrusive JavaScript driver.
  22950. #
  22951. # ==== Examples
  22952. # submit_tag
  22953. # # => <input name="commit" type="submit" value="Save changes" />
  22954. #
  22955. # submit_tag "Edit this article"
  22956. # # => <input name="commit" type="submit" value="Edit this article" />
  22957. #
  22958. # submit_tag "Save edits", disabled: true
  22959. # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
  22960. #
  22961. # submit_tag "Complete sale", data: { disable_with: "Please wait..." }
  22962. # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
  22963. #
  22964. # submit_tag nil, class: "form_submit"
  22965. # # => <input class="form_submit" name="commit" type="submit" />
  22966. #
  22967. # submit_tag "Edit", class: "edit_button"
  22968. # # => <input class="edit_button" name="commit" type="submit" value="Edit" />
  22969. #
  22970. # submit_tag "Save", data: { confirm: "Are you sure?" }
  22971. # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
  22972. #
  22973. def submit_tag(value = "Save changes", options = {})
  22974. options = options.stringify_keys
  22975. if disable_with = options.delete("disable_with")
  22976. message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \
  22977. "Use 'data: { disable_with: \'Text\' }' instead."
  22978. ActiveSupport::Deprecation.warn message
  22979. options["data-disable-with"] = disable_with
  22980. end
  22981. if confirm = options.delete("confirm")
  22982. message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
  22983. "Use 'data: { confirm: \'Text\' }' instead'."
  22984. ActiveSupport::Deprecation.warn message
  22985. options["data-confirm"] = confirm
  22986. end
  22987. tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
  22988. end
  22989. # Creates a button element that defines a <tt>submit</tt> button,
  22990. # <tt>reset</tt>button or a generic button which can be used in
  22991. # JavaScript, for example. You can use the button tag as a regular
  22992. # submit tag but it isn't supported in legacy browsers. However,
  22993. # the button tag allows richer labels such as images and emphasis,
  22994. # so this helper will also accept a block.
  22995. #
  22996. # ==== Options
  22997. # * <tt>:data</tt> - This option can be used to add custom data attributes.
  22998. # * <tt>:disabled</tt> - If true, the user will not be able to
  22999. # use this input.
  23000. # * Any other key creates standard HTML options for the tag.
  23001. #
  23002. # ==== Data attributes
  23003. #
  23004. # * <tt>confirm: 'question?'</tt> - If present, the
  23005. # unobtrusive JavaScript drivers will provide a prompt with
  23006. # the question specified. If the user accepts, the form is
  23007. # processed normally, otherwise no action is taken.
  23008. # * <tt>:disable_with</tt> - Value of this parameter will be
  23009. # used as the value for a disabled version of the submit
  23010. # button when the form is submitted. This feature is provided
  23011. # by the unobtrusive JavaScript driver.
  23012. #
  23013. # ==== Examples
  23014. # button_tag
  23015. # # => <button name="button" type="submit">Button</button>
  23016. #
  23017. # button_tag(type: 'button') do
  23018. # content_tag(:strong, 'Ask me!')
  23019. # end
  23020. # # => <button name="button" type="button">
  23021. # # <strong>Ask me!</strong>
  23022. # # </button>
  23023. #
  23024. # button_tag "Checkout", data: { disable_with => "Please wait..." }
  23025. # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
  23026. #
  23027. def button_tag(content_or_options = nil, options = nil, &block)
  23028. options = content_or_options if block_given? && content_or_options.is_a?(Hash)
  23029. options ||= {}
  23030. options = options.stringify_keys
  23031. if disable_with = options.delete("disable_with")
  23032. message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \
  23033. "Use 'data: { disable_with: \'Text\' }' instead."
  23034. ActiveSupport::Deprecation.warn message
  23035. options["data-disable-with"] = disable_with
  23036. end
  23037. if confirm = options.delete("confirm")
  23038. message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
  23039. "Use 'data: { confirm: \'Text\' }' instead'."
  23040. ActiveSupport::Deprecation.warn message
  23041. options["data-confirm"] = confirm
  23042. end
  23043. options.reverse_merge! 'name' => 'button', 'type' => 'submit'
  23044. content_tag :button, content_or_options || 'Button', options, &block
  23045. end
  23046. # Displays an image which when clicked will submit the form.
  23047. #
  23048. # <tt>source</tt> is passed to AssetTagHelper#path_to_image
  23049. #
  23050. # ==== Options
  23051. # * <tt>:data</tt> - This option can be used to add custom data attributes.
  23052. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
  23053. # * Any other key creates standard HTML options for the tag.
  23054. #
  23055. # ==== Data attributes
  23056. #
  23057. # * <tt>confirm: 'question?'</tt> - This will add a JavaScript confirm
  23058. # prompt with the question specified. If the user accepts, the form is
  23059. # processed normally, otherwise no action is taken.
  23060. #
  23061. # ==== Examples
  23062. # image_submit_tag("login.png")
  23063. # # => <input alt="Login" src="/images/login.png" type="image" />
  23064. #
  23065. # image_submit_tag("purchase.png", disabled: true)
  23066. # # => <input alt="Purchase" disabled="disabled" src="/images/purchase.png" type="image" />
  23067. #
  23068. # image_submit_tag("search.png", class: 'search_button', alt: 'Find')
  23069. # # => <input alt="Find" class="search_button" src="/images/search.png" type="image" />
  23070. #
  23071. # image_submit_tag("agree.png", disabled: true, class: "agree_disagree_button")
  23072. # # => <input alt="Agree" class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
  23073. #
  23074. # image_submit_tag("save.png", data: { confirm: "Are you sure?" })
  23075. # # => <input alt="Save" src="/images/save.png" data-confirm="Are you sure?" type="image" />
  23076. def image_submit_tag(source, options = {})
  23077. options = options.stringify_keys
  23078. if confirm = options.delete("confirm")
  23079. message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
  23080. "Use 'data: { confirm: \'Text\' }' instead'."
  23081. ActiveSupport::Deprecation.warn message
  23082. options["data-confirm"] = confirm
  23083. end
  23084. tag :input, { "alt" => image_alt(source), "type" => "image", "src" => path_to_image(source) }.update(options)
  23085. end
  23086. # Creates a field set for grouping HTML form elements.
  23087. #
  23088. # <tt>legend</tt> will become the fieldset's title (optional as per W3C).
  23089. # <tt>options</tt> accept the same values as tag.
  23090. #
  23091. # ==== Examples
  23092. # <%= field_set_tag do %>
  23093. # <p><%= text_field_tag 'name' %></p>
  23094. # <% end %>
  23095. # # => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
  23096. #
  23097. # <%= field_set_tag 'Your details' do %>
  23098. # <p><%= text_field_tag 'name' %></p>
  23099. # <% end %>
  23100. # # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
  23101. #
  23102. # <%= field_set_tag nil, class: 'format' do %>
  23103. # <p><%= text_field_tag 'name' %></p>
  23104. # <% end %>
  23105. # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
  23106. def field_set_tag(legend = nil, options = nil, &block)
  23107. output = tag(:fieldset, options, true)
  23108. output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
  23109. output.concat(capture(&block)) if block_given?
  23110. output.safe_concat("</fieldset>")
  23111. end
  23112. # Creates a text field of type "color".
  23113. #
  23114. # ==== Options
  23115. # * Accepts the same options as text_field_tag.
  23116. def color_field_tag(name, value = nil, options = {})
  23117. text_field_tag(name, value, options.stringify_keys.update("type" => "color"))
  23118. end
  23119. # Creates a text field of type "search".
  23120. #
  23121. # ==== Options
  23122. # * Accepts the same options as text_field_tag.
  23123. def search_field_tag(name, value = nil, options = {})
  23124. text_field_tag(name, value, options.stringify_keys.update("type" => "search"))
  23125. end
  23126. # Creates a text field of type "tel".
  23127. #
  23128. # ==== Options
  23129. # * Accepts the same options as text_field_tag.
  23130. def telephone_field_tag(name, value = nil, options = {})
  23131. text_field_tag(name, value, options.stringify_keys.update("type" => "tel"))
  23132. end
  23133. alias phone_field_tag telephone_field_tag
  23134. # Creates a text field of type "date".
  23135. #
  23136. # ==== Options
  23137. # * Accepts the same options as text_field_tag.
  23138. def date_field_tag(name, value = nil, options = {})
  23139. text_field_tag(name, value, options.stringify_keys.update("type" => "date"))
  23140. end
  23141. # Creates a text field of type "time".
  23142. #
  23143. # === Options
  23144. # * <tt>:min</tt> - The minimum acceptable value.
  23145. # * <tt>:max</tt> - The maximum acceptable value.
  23146. # * <tt>:step</tt> - The acceptable value granularity.
  23147. # * Otherwise accepts the same options as text_field_tag.
  23148. def time_field_tag(name, value = nil, options = {})
  23149. text_field_tag(name, value, options.stringify_keys.update("type" => "time"))
  23150. end
  23151. # Creates a text field of type "datetime".
  23152. #
  23153. # === Options
  23154. # * <tt>:min</tt> - The minimum acceptable value.
  23155. # * <tt>:max</tt> - The maximum acceptable value.
  23156. # * <tt>:step</tt> - The acceptable value granularity.
  23157. # * Otherwise accepts the same options as text_field_tag.
  23158. def datetime_field_tag(name, value = nil, options = {})
  23159. text_field_tag(name, value, options.stringify_keys.update("type" => "datetime"))
  23160. end
  23161. # Creates a text field of type "datetime-local".
  23162. #
  23163. # === Options
  23164. # * <tt>:min</tt> - The minimum acceptable value.
  23165. # * <tt>:max</tt> - The maximum acceptable value.
  23166. # * <tt>:step</tt> - The acceptable value granularity.
  23167. # * Otherwise accepts the same options as text_field_tag.
  23168. def datetime_local_field_tag(name, value = nil, options = {})
  23169. text_field_tag(name, value, options.stringify_keys.update("type" => "datetime-local"))
  23170. end
  23171. # Creates a text field of type "month".
  23172. #
  23173. # === Options
  23174. # * <tt>:min</tt> - The minimum acceptable value.
  23175. # * <tt>:max</tt> - The maximum acceptable value.
  23176. # * <tt>:step</tt> - The acceptable value granularity.
  23177. # * Otherwise accepts the same options as text_field_tag.
  23178. def month_field_tag(name, value = nil, options = {})
  23179. text_field_tag(name, value, options.stringify_keys.update("type" => "month"))
  23180. end
  23181. # Creates a text field of type "week".
  23182. #
  23183. # === Options
  23184. # * <tt>:min</tt> - The minimum acceptable value.
  23185. # * <tt>:max</tt> - The maximum acceptable value.
  23186. # * <tt>:step</tt> - The acceptable value granularity.
  23187. # * Otherwise accepts the same options as text_field_tag.
  23188. def week_field_tag(name, value = nil, options = {})
  23189. text_field_tag(name, value, options.stringify_keys.update("type" => "week"))
  23190. end
  23191. # Creates a text field of type "url".
  23192. #
  23193. # ==== Options
  23194. # * Accepts the same options as text_field_tag.
  23195. def url_field_tag(name, value = nil, options = {})
  23196. text_field_tag(name, value, options.stringify_keys.update("type" => "url"))
  23197. end
  23198. # Creates a text field of type "email".
  23199. #
  23200. # ==== Options
  23201. # * Accepts the same options as text_field_tag.
  23202. def email_field_tag(name, value = nil, options = {})
  23203. text_field_tag(name, value, options.stringify_keys.update("type" => "email"))
  23204. end
  23205. # Creates a number field.
  23206. #
  23207. # ==== Options
  23208. # * <tt>:min</tt> - The minimum acceptable value.
  23209. # * <tt>:max</tt> - The maximum acceptable value.
  23210. # * <tt>:in</tt> - A range specifying the <tt>:min</tt> and
  23211. # <tt>:max</tt> values.
  23212. # * <tt>:step</tt> - The acceptable value granularity.
  23213. # * Otherwise accepts the same options as text_field_tag.
  23214. #
  23215. # ==== Examples
  23216. # number_field_tag 'quantity', nil, in: 1...10
  23217. # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
  23218. def number_field_tag(name, value = nil, options = {})
  23219. options = options.stringify_keys
  23220. options["type"] ||= "number"
  23221. if range = options.delete("in") || options.delete("within")
  23222. options.update("min" => range.min, "max" => range.max)
  23223. end
  23224. text_field_tag(name, value, options)
  23225. end
  23226. # Creates a range form element.
  23227. #
  23228. # ==== Options
  23229. # * Accepts the same options as number_field_tag.
  23230. def range_field_tag(name, value = nil, options = {})
  23231. number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
  23232. end
  23233. # Creates the hidden UTF8 enforcer tag. Override this method in a helper
  23234. # to customize the tag.
  23235. def utf8_enforcer_tag
  23236. tag(:input, :type => "hidden", :name => "utf8", :value => "&#x2713;".html_safe)
  23237. end
  23238. private
  23239. def html_options_for_form(url_for_options, options)
  23240. options.stringify_keys.tap do |html_options|
  23241. html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
  23242. # The following URL is unescaped, this is just a hash of options, and it is the
  23243. # responsibility of the caller to escape all the values.
  23244. html_options["action"] = url_for(url_for_options)
  23245. html_options["accept-charset"] = "UTF-8"
  23246. html_options["data-remote"] = true if html_options.delete("remote")
  23247. if html_options["data-remote"] &&
  23248. !embed_authenticity_token_in_remote_forms &&
  23249. html_options["authenticity_token"].blank?
  23250. # The authenticity token is taken from the meta tag in this case
  23251. html_options["authenticity_token"] = false
  23252. elsif html_options["authenticity_token"] == true
  23253. # Include the default authenticity_token, which is only generated when its set to nil,
  23254. # but we needed the true value to override the default of no authenticity_token on data-remote.
  23255. html_options["authenticity_token"] = nil
  23256. end
  23257. end
  23258. end
  23259. def extra_tags_for_form(html_options)
  23260. authenticity_token = html_options.delete("authenticity_token")
  23261. method = html_options.delete("method").to_s
  23262. method_tag = case method
  23263. when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
  23264. html_options["method"] = "get"
  23265. ''
  23266. when /^post$/i, "", nil
  23267. html_options["method"] = "post"
  23268. token_tag(authenticity_token)
  23269. else
  23270. html_options["method"] = "post"
  23271. method_tag(method) + token_tag(authenticity_token)
  23272. end
  23273. tags = utf8_enforcer_tag << method_tag
  23274. content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
  23275. end
  23276. def form_tag_html(html_options)
  23277. extra_tags = extra_tags_for_form(html_options)
  23278. tag(:form, html_options, true) + extra_tags
  23279. end
  23280. def form_tag_in_block(html_options, &block)
  23281. content = capture(&block)
  23282. output = form_tag_html(html_options)
  23283. output << content
  23284. output.safe_concat("</form>")
  23285. end
  23286. # see http://www.w3.org/TR/html4/types.html#type-name
  23287. def sanitize_to_id(name)
  23288. name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_")
  23289. end
  23290. end
  23291. end
  23292. end
  23293. require 'action_view/helpers/tag_helper'
  23294. module ActionView
  23295. module Helpers
  23296. module JavaScriptHelper
  23297. JS_ESCAPE_MAP = {
  23298. '\\' => '\\\\',
  23299. '</' => '<\/',
  23300. "\r\n" => '\n',
  23301. "\n" => '\n',
  23302. "\r" => '\n',
  23303. '"' => '\\"',
  23304. "'" => "\\'"
  23305. }
  23306. JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '&#x2028;'
  23307. JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '&#x2029;'
  23308. # Escapes carriage returns and single and double quotes for JavaScript segments.
  23309. #
  23310. # Also available through the alias j(). This is particularly helpful in JavaScript
  23311. # responses, like:
  23312. #
  23313. # $('some_element').replaceWith('<%=j render 'some/element_template' %>');
  23314. def escape_javascript(javascript)
  23315. if javascript
  23316. result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
  23317. javascript.html_safe? ? result.html_safe : result
  23318. else
  23319. ''
  23320. end
  23321. end
  23322. alias_method :j, :escape_javascript
  23323. # Returns a JavaScript tag with the +content+ inside. Example:
  23324. # javascript_tag "alert('All is good')"
  23325. #
  23326. # Returns:
  23327. # <script>
  23328. # //<![CDATA[
  23329. # alert('All is good')
  23330. # //]]>
  23331. # </script>
  23332. #
  23333. # +html_options+ may be a hash of attributes for the <tt>\<script></tt>
  23334. # tag.
  23335. #
  23336. # javascript_tag "alert('All is good')", defer: 'defer'
  23337. # # => <script defer="defer">alert('All is good')</script>
  23338. #
  23339. # Instead of passing the content as an argument, you can also use a block
  23340. # in which case, you pass your +html_options+ as the first parameter.
  23341. #
  23342. # <%= javascript_tag defer: 'defer' do -%>
  23343. # alert('All is good')
  23344. # <% end -%>
  23345. def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
  23346. content =
  23347. if block_given?
  23348. html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
  23349. capture(&block)
  23350. else
  23351. content_or_options_with_block
  23352. end
  23353. content_tag(:script, javascript_cdata_section(content), html_options)
  23354. end
  23355. def javascript_cdata_section(content) #:nodoc:
  23356. "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
  23357. end
  23358. # Returns a button whose +onclick+ handler triggers the passed JavaScript.
  23359. #
  23360. # The helper receives a name, JavaScript code, and an optional hash of HTML options. The
  23361. # name is used as button label and the JavaScript code goes into its +onclick+ attribute.
  23362. # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+.
  23363. #
  23364. # button_to_function "Greeting", "alert('Hello world!')", class: "ok"
  23365. # # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
  23366. #
  23367. def button_to_function(name, function=nil, html_options={})
  23368. message = "button_to_function is deprecated and will be removed from Rails 4.1. We recomend to use Unobtrusive JavaScript instead. " +
  23369. "See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript"
  23370. ActiveSupport::Deprecation.warn message
  23371. onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
  23372. tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
  23373. end
  23374. # Returns a link whose +onclick+ handler triggers the passed JavaScript.
  23375. #
  23376. # The helper receives a name, JavaScript code, and an optional hash of HTML options. The
  23377. # name is used as the link text and the JavaScript code goes into the +onclick+ attribute.
  23378. # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all
  23379. # the JavaScript is set, the helper appends "; return false;".
  23380. #
  23381. # The +href+ attribute of the tag is set to "#" unless +html_options+ has one.
  23382. #
  23383. # link_to_function "Greeting", "alert('Hello world!')", class: "nav_link"
  23384. # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
  23385. #
  23386. def link_to_function(name, function, html_options={})
  23387. message = "link_to_function is deprecated and will be removed from Rails 4.1. We recomend to use Unobtrusive JavaScript instead. " +
  23388. "See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript"
  23389. ActiveSupport::Deprecation.warn message
  23390. onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
  23391. href = html_options[:href] || '#'
  23392. content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
  23393. end
  23394. end
  23395. end
  23396. end
  23397. # encoding: utf-8
  23398. require 'active_support/core_ext/hash/keys'
  23399. require 'active_support/core_ext/string/output_safety'
  23400. require 'active_support/number_helper'
  23401. module ActionView
  23402. # = Action View Number Helpers
  23403. module Helpers #:nodoc:
  23404. # Provides methods for converting numbers into formatted strings.
  23405. # Methods are provided for phone numbers, currency, percentage,
  23406. # precision, positional notation, file size and pretty printing.
  23407. #
  23408. # Most methods expect a +number+ argument, and will return it
  23409. # unchanged if can't be converted into a valid number.
  23410. module NumberHelper
  23411. # Raised when argument +number+ param given to the helpers is invalid and
  23412. # the option :raise is set to +true+.
  23413. class InvalidNumberError < StandardError
  23414. attr_accessor :number
  23415. def initialize(number)
  23416. @number = number
  23417. end
  23418. end
  23419. # Formats a +number+ into a US phone number (e.g., (555)
  23420. # 123-9876). You can customize the format in the +options+ hash.
  23421. #
  23422. # * <tt>:area_code</tt> - Adds parentheses around the area code.
  23423. # * <tt>:delimiter</tt> - Specifies the delimiter to use
  23424. # (defaults to "-").
  23425. # * <tt>:extension</tt> - Specifies an extension to add to the
  23426. # end of the generated number.
  23427. # * <tt>:country_code</tt> - Sets the country code for the phone
  23428. # number.
  23429. # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
  23430. # the argument is invalid.
  23431. #
  23432. # number_to_phone(5551234) # => 555-1234
  23433. # number_to_phone("5551234") # => 555-1234
  23434. # number_to_phone(1235551234) # => 123-555-1234
  23435. # number_to_phone(1235551234, area_code: true) # => (123) 555-1234
  23436. # number_to_phone(1235551234, delimiter: " ") # => 123 555 1234
  23437. # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
  23438. # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
  23439. # number_to_phone("123a456") # => 123a456
  23440. # number_to_phone("1234a567", raise: true) # => InvalidNumberError
  23441. #
  23442. # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
  23443. # # => +1.123.555.1234 x 1343
  23444. def number_to_phone(number, options = {})
  23445. return unless number
  23446. options = options.symbolize_keys
  23447. parse_float(number, true) if options.delete(:raise)
  23448. ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options))
  23449. end
  23450. # Formats a +number+ into a currency string (e.g., $13.65). You
  23451. # can customize the format in the +options+ hash.
  23452. #
  23453. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  23454. # (defaults to current locale).
  23455. # * <tt>:precision</tt> - Sets the level of precision (defaults
  23456. # to 2).
  23457. # * <tt>:unit</tt> - Sets the denomination of the currency
  23458. # (defaults to "$").
  23459. # * <tt>:separator</tt> - Sets the separator between the units
  23460. # (defaults to ".").
  23461. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  23462. # to ",").
  23463. # * <tt>:format</tt> - Sets the format for non-negative numbers
  23464. # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
  23465. # currency, and <tt>%n</tt> for the number.
  23466. # * <tt>:negative_format</tt> - Sets the format for negative
  23467. # numbers (defaults to prepending an hyphen to the formatted
  23468. # number given by <tt>:format</tt>). Accepts the same fields
  23469. # than <tt>:format</tt>, except <tt>%n</tt> is here the
  23470. # absolute value of the number.
  23471. # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
  23472. # the argument is invalid.
  23473. #
  23474. # number_to_currency(1234567890.50) # => $1,234,567,890.50
  23475. # number_to_currency(1234567890.506) # => $1,234,567,890.51
  23476. # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
  23477. # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51
  23478. # number_to_currency("123a456") # => $123a456
  23479. #
  23480. # number_to_currency("123a456", raise: true) # => InvalidNumberError
  23481. #
  23482. # number_to_currency(-1234567890.50, negative_format: "(%u%n)")
  23483. # # => ($1,234,567,890.50)
  23484. # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "")
  23485. # # => &pound;1234567890,50
  23486. # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "", format: "%n %u")
  23487. # # => 1234567890,50 &pound;
  23488. def number_to_currency(number, options = {})
  23489. return unless number
  23490. options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
  23491. wrap_with_output_safety_handling(number, options.delete(:raise)) {
  23492. ActiveSupport::NumberHelper.number_to_currency(number, options)
  23493. }
  23494. end
  23495. # Formats a +number+ as a percentage string (e.g., 65%). You can
  23496. # customize the format in the +options+ hash.
  23497. #
  23498. #
  23499. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  23500. # (defaults to current locale).
  23501. # * <tt>:precision</tt> - Sets the precision of the number
  23502. # (defaults to 3).
  23503. # * <tt>:significant</tt> - If +true+, precision will be the #
  23504. # of significant_digits. If +false+, the # of fractional
  23505. # digits (defaults to +false+).
  23506. # * <tt>:separator</tt> - Sets the separator between the
  23507. # fractional and integer digits (defaults to ".").
  23508. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  23509. # to "").
  23510. # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
  23511. # insignificant zeros after the decimal separator (defaults to
  23512. # +false+).
  23513. # * <tt>:format</tt> - Specifies the format of the percentage
  23514. # string The number field is <tt>%n</tt> (defaults to "%n%").
  23515. # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
  23516. # the argument is invalid.
  23517. #
  23518. # number_to_percentage(100) # => 100.000%
  23519. # number_to_percentage("98") # => 98.000%
  23520. # number_to_percentage(100, precision: 0) # => 100%
  23521. # number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
  23522. # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
  23523. # number_to_percentage(1000, locale: :fr) # => 1 000,000%
  23524. # number_to_percentage("98a") # => 98a%
  23525. # number_to_percentage(100, format: "%n %") # => 100 %
  23526. #
  23527. # number_to_percentage("98a", raise: true) # => InvalidNumberError
  23528. def number_to_percentage(number, options = {})
  23529. return unless number
  23530. options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
  23531. wrap_with_output_safety_handling(number, options.delete(:raise)) {
  23532. ActiveSupport::NumberHelper.number_to_percentage(number, options)
  23533. }
  23534. end
  23535. # Formats a +number+ with grouped thousands using +delimiter+
  23536. # (e.g., 12,324). You can customize the format in the +options+
  23537. # hash.
  23538. #
  23539. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  23540. # (defaults to current locale).
  23541. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  23542. # to ",").
  23543. # * <tt>:separator</tt> - Sets the separator between the
  23544. # fractional and integer digits (defaults to ".").
  23545. # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
  23546. # the argument is invalid.
  23547. #
  23548. # number_with_delimiter(12345678) # => 12,345,678
  23549. # number_with_delimiter("123456") # => 123,456
  23550. # number_with_delimiter(12345678.05) # => 12,345,678.05
  23551. # number_with_delimiter(12345678, delimiter: ".") # => 12.345.678
  23552. # number_with_delimiter(12345678, delimiter: ",") # => 12,345,678
  23553. # number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05
  23554. # number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05
  23555. # number_with_delimiter("112a") # => 112a
  23556. # number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
  23557. # # => 98 765 432,98
  23558. #
  23559. # number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
  23560. def number_with_delimiter(number, options = {})
  23561. options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
  23562. wrap_with_output_safety_handling(number, options.delete(:raise)) {
  23563. ActiveSupport::NumberHelper.number_to_delimited(number, options)
  23564. }
  23565. end
  23566. # Formats a +number+ with the specified level of
  23567. # <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
  23568. # +:significant+ is +false+, and 5 if +:significant+ is +true+).
  23569. # You can customize the format in the +options+ hash.
  23570. #
  23571. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  23572. # (defaults to current locale).
  23573. # * <tt>:precision</tt> - Sets the precision of the number
  23574. # (defaults to 3).
  23575. # * <tt>:significant</tt> - If +true+, precision will be the #
  23576. # of significant_digits. If +false+, the # of fractional
  23577. # digits (defaults to +false+).
  23578. # * <tt>:separator</tt> - Sets the separator between the
  23579. # fractional and integer digits (defaults to ".").
  23580. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  23581. # to "").
  23582. # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
  23583. # insignificant zeros after the decimal separator (defaults to
  23584. # +false+).
  23585. # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
  23586. # the argument is invalid.
  23587. #
  23588. # number_with_precision(111.2345) # => 111.235
  23589. # number_with_precision(111.2345, precision: 2) # => 111.23
  23590. # number_with_precision(13, precision: 5) # => 13.00000
  23591. # number_with_precision(389.32314, precision: 0) # => 389
  23592. # number_with_precision(111.2345, significant: true) # => 111
  23593. # number_with_precision(111.2345, precision: 1, significant: true) # => 100
  23594. # number_with_precision(13, precision: 5, significant: true) # => 13.000
  23595. # number_with_precision(111.234, locale: :fr) # => 111,234
  23596. #
  23597. # number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true)
  23598. # # => 13
  23599. #
  23600. # number_with_precision(389.32314, precision: 4, significant: true) # => 389.3
  23601. # number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
  23602. # # => 1.111,23
  23603. def number_with_precision(number, options = {})
  23604. options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
  23605. wrap_with_output_safety_handling(number, options.delete(:raise)) {
  23606. ActiveSupport::NumberHelper.number_to_rounded(number, options)
  23607. }
  23608. end
  23609. # Formats the bytes in +number+ into a more understandable
  23610. # representation (e.g., giving it 1500 yields 1.5 KB). This
  23611. # method is useful for reporting file sizes to users. You can
  23612. # customize the format in the +options+ hash.
  23613. #
  23614. # See <tt>number_to_human</tt> if you want to pretty-print a
  23615. # generic number.
  23616. #
  23617. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  23618. # (defaults to current locale).
  23619. # * <tt>:precision</tt> - Sets the precision of the number
  23620. # (defaults to 3).
  23621. # * <tt>:significant</tt> - If +true+, precision will be the #
  23622. # of significant_digits. If +false+, the # of fractional
  23623. # digits (defaults to +true+)
  23624. # * <tt>:separator</tt> - Sets the separator between the
  23625. # fractional and integer digits (defaults to ".").
  23626. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  23627. # to "").
  23628. # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
  23629. # insignificant zeros after the decimal separator (defaults to
  23630. # +true+)
  23631. # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
  23632. # prefix (defaults to :binary)
  23633. # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
  23634. # the argument is invalid.
  23635. #
  23636. # number_to_human_size(123) # => 123 Bytes
  23637. # number_to_human_size(1234) # => 1.21 KB
  23638. # number_to_human_size(12345) # => 12.1 KB
  23639. # number_to_human_size(1234567) # => 1.18 MB
  23640. # number_to_human_size(1234567890) # => 1.15 GB
  23641. # number_to_human_size(1234567890123) # => 1.12 TB
  23642. # number_to_human_size(1234567, precision: 2) # => 1.2 MB
  23643. # number_to_human_size(483989, precision: 2) # => 470 KB
  23644. # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
  23645. #
  23646. # Non-significant zeros after the fractional separator are
  23647. # stripped out by default (set
  23648. # <tt>:strip_insignificant_zeros</tt> to +false+ to change
  23649. # that):
  23650. #
  23651. # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
  23652. # number_to_human_size(524288000, precision: 5) # => "500 MB"
  23653. def number_to_human_size(number, options = {})
  23654. options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
  23655. wrap_with_output_safety_handling(number, options.delete(:raise)) {
  23656. ActiveSupport::NumberHelper.number_to_human_size(number, options)
  23657. }
  23658. end
  23659. # Pretty prints (formats and approximates) a number in a way it
  23660. # is more readable by humans (eg.: 1200000000 becomes "1.2
  23661. # Billion"). This is useful for numbers that can get very large
  23662. # (and too hard to read).
  23663. #
  23664. # See <tt>number_to_human_size</tt> if you want to print a file
  23665. # size.
  23666. #
  23667. # You can also define you own unit-quantifier names if you want
  23668. # to use other decimal units (eg.: 1500 becomes "1.5
  23669. # kilometers", 0.150 becomes "150 milliliters", etc). You may
  23670. # define a wide range of unit quantifiers, even fractional ones
  23671. # (centi, deci, mili, etc).
  23672. #
  23673. # ==== Options
  23674. #
  23675. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  23676. # (defaults to current locale).
  23677. # * <tt>:precision</tt> - Sets the precision of the number
  23678. # (defaults to 3).
  23679. # * <tt>:significant</tt> - If +true+, precision will be the #
  23680. # of significant_digits. If +false+, the # of fractional
  23681. # digits (defaults to +true+)
  23682. # * <tt>:separator</tt> - Sets the separator between the
  23683. # fractional and integer digits (defaults to ".").
  23684. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  23685. # to "").
  23686. # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
  23687. # insignificant zeros after the decimal separator (defaults to
  23688. # +true+)
  23689. # * <tt>:units</tt> - A Hash of unit quantifier names. Or a
  23690. # string containing an i18n scope where to find this hash. It
  23691. # might have the following keys:
  23692. # * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
  23693. # *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
  23694. # *<tt>:billion</tt>, <tt>:trillion</tt>,
  23695. # *<tt>:quadrillion</tt>
  23696. # * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
  23697. # *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
  23698. # *<tt>:pico</tt>, <tt>:femto</tt>
  23699. # * <tt>:format</tt> - Sets the format of the output string
  23700. # (defaults to "%n %u"). The field types are:
  23701. # * %u - The quantifier (ex.: 'thousand')
  23702. # * %n - The number
  23703. # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
  23704. # the argument is invalid.
  23705. #
  23706. # number_to_human(123) # => "123"
  23707. # number_to_human(1234) # => "1.23 Thousand"
  23708. # number_to_human(12345) # => "12.3 Thousand"
  23709. # number_to_human(1234567) # => "1.23 Million"
  23710. # number_to_human(1234567890) # => "1.23 Billion"
  23711. # number_to_human(1234567890123) # => "1.23 Trillion"
  23712. # number_to_human(1234567890123456) # => "1.23 Quadrillion"
  23713. # number_to_human(1234567890123456789) # => "1230 Quadrillion"
  23714. # number_to_human(489939, precision: 2) # => "490 Thousand"
  23715. # number_to_human(489939, precision: 4) # => "489.9 Thousand"
  23716. # number_to_human(1234567, precision: 4,
  23717. # significant: false) # => "1.2346 Million"
  23718. # number_to_human(1234567, precision: 1,
  23719. # separator: ',',
  23720. # significant: false) # => "1,2 Million"
  23721. #
  23722. # Non-significant zeros after the decimal separator are stripped
  23723. # out by default (set <tt>:strip_insignificant_zeros</tt> to
  23724. # +false+ to change that):
  23725. # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
  23726. # number_to_human(500000000, precision: 5) # => "500 Million"
  23727. #
  23728. # ==== Custom Unit Quantifiers
  23729. #
  23730. # You can also use your own custom unit quantifiers:
  23731. # number_to_human(500000, units: {unit: "ml", thousand: "lt"}) # => "500 lt"
  23732. #
  23733. # If in your I18n locale you have:
  23734. # distance:
  23735. # centi:
  23736. # one: "centimeter"
  23737. # other: "centimeters"
  23738. # unit:
  23739. # one: "meter"
  23740. # other: "meters"
  23741. # thousand:
  23742. # one: "kilometer"
  23743. # other: "kilometers"
  23744. # billion: "gazillion-distance"
  23745. #
  23746. # Then you could do:
  23747. #
  23748. # number_to_human(543934, units: :distance) # => "544 kilometers"
  23749. # number_to_human(54393498, units: :distance) # => "54400 kilometers"
  23750. # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
  23751. # number_to_human(343, units: :distance, precision: 1) # => "300 meters"
  23752. # number_to_human(1, units: :distance) # => "1 meter"
  23753. # number_to_human(0.34, units: :distance) # => "34 centimeters"
  23754. #
  23755. def number_to_human(number, options = {})
  23756. options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
  23757. wrap_with_output_safety_handling(number, options.delete(:raise)) {
  23758. ActiveSupport::NumberHelper.number_to_human(number, options)
  23759. }
  23760. end
  23761. private
  23762. def escape_unsafe_delimiters_and_separators(options)
  23763. options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator] && !options[:separator].html_safe?
  23764. options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter] && !options[:delimiter].html_safe?
  23765. options
  23766. end
  23767. def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
  23768. valid_float = valid_float?(number)
  23769. raise InvalidNumberError, number if raise_on_invalid && !valid_float
  23770. formatted_number = yield
  23771. if valid_float || number.html_safe?
  23772. formatted_number.html_safe
  23773. else
  23774. formatted_number
  23775. end
  23776. end
  23777. def valid_float?(number)
  23778. !parse_float(number, false).nil?
  23779. end
  23780. def parse_float(number, raise_error)
  23781. Float(number)
  23782. rescue ArgumentError, TypeError
  23783. raise InvalidNumberError, number if raise_error
  23784. end
  23785. end
  23786. end
  23787. end
  23788. require 'active_support/core_ext/string/output_safety'
  23789. module ActionView #:nodoc:
  23790. # = Action View Raw Output Helper
  23791. module Helpers #:nodoc:
  23792. module OutputSafetyHelper
  23793. # This method outputs without escaping a string. Since escaping tags is
  23794. # now default, this can be used when you don't want Rails to automatically
  23795. # escape tags. This is not recommended if the data is coming from the user's
  23796. # input.
  23797. #
  23798. # For example:
  23799. #
  23800. # raw @user.name
  23801. # # => 'Jimmy <alert>Tables</alert>'
  23802. def raw(stringish)
  23803. stringish.to_s.html_safe
  23804. end
  23805. # This method returns a html safe string similar to what <tt>Array#join</tt>
  23806. # would return. All items in the array, including the supplied separator, are
  23807. # html escaped unless they are html safe, and the returned string is marked
  23808. # as html safe.
  23809. #
  23810. # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />")
  23811. # # => "<p>foo</p>&lt;br /&gt;&lt;p&gt;bar&lt;/p&gt;"
  23812. #
  23813. # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>".html_safe], "<br />".html_safe)
  23814. # # => "<p>foo</p><br /><p>bar</p>"
  23815. #
  23816. def safe_join(array, sep=$,)
  23817. sep = ERB::Util.html_escape(sep)
  23818. array.map { |i| ERB::Util.html_escape(i) }.join(sep).html_safe
  23819. end
  23820. end
  23821. end
  23822. end
  23823. module ActionView
  23824. # = Action View Record Tag Helpers
  23825. module Helpers
  23826. module RecordTagHelper
  23827. include ActionView::RecordIdentifier
  23828. # Produces a wrapper DIV element with id and class parameters that
  23829. # relate to the specified Active Record object. Usage example:
  23830. #
  23831. # <%= div_for(@person, class: "foo") do %>
  23832. # <%= @person.name %>
  23833. # <% end %>
  23834. #
  23835. # produces:
  23836. #
  23837. # <div id="person_123" class="person foo"> Joe Bloggs </div>
  23838. #
  23839. # You can also pass an array of Active Record objects, which will then
  23840. # get iterated over and yield each record as an argument for the block.
  23841. # For example:
  23842. #
  23843. # <%= div_for(@people, class: "foo") do |person| %>
  23844. # <%= person.name %>
  23845. # <% end %>
  23846. #
  23847. # produces:
  23848. #
  23849. # <div id="person_123" class="person foo"> Joe Bloggs </div>
  23850. # <div id="person_124" class="person foo"> Jane Bloggs </div>
  23851. #
  23852. def div_for(record, *args, &block)
  23853. content_tag_for(:div, record, *args, &block)
  23854. end
  23855. # content_tag_for creates an HTML element with id and class parameters
  23856. # that relate to the specified Active Record object. For example:
  23857. #
  23858. # <%= content_tag_for(:tr, @person) do %>
  23859. # <td><%= @person.first_name %></td>
  23860. # <td><%= @person.last_name %></td>
  23861. # <% end %>
  23862. #
  23863. # would produce the following HTML (assuming @person is an instance of
  23864. # a Person object, with an id value of 123):
  23865. #
  23866. # <tr id="person_123" class="person">....</tr>
  23867. #
  23868. # If you require the HTML id attribute to have a prefix, you can specify it:
  23869. #
  23870. # <%= content_tag_for(:tr, @person, :foo) do %> ...
  23871. #
  23872. # produces:
  23873. #
  23874. # <tr id="foo_person_123" class="person">...
  23875. #
  23876. # You can also pass an array of objects which this method will loop through
  23877. # and yield the current object to the supplied block, reducing the need for
  23878. # having to iterate through the object (using <tt>each</tt>) beforehand.
  23879. # For example (assuming @people is an array of Person objects):
  23880. #
  23881. # <%= content_tag_for(:tr, @people) do |person| %>
  23882. # <td><%= person.first_name %></td>
  23883. # <td><%= person.last_name %></td>
  23884. # <% end %>
  23885. #
  23886. # produces:
  23887. #
  23888. # <tr id="person_123" class="person">...</tr>
  23889. # <tr id="person_124" class="person">...</tr>
  23890. #
  23891. # content_tag_for also accepts a hash of options, which will be converted to
  23892. # additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined
  23893. # with the default class name for your object. For example:
  23894. #
  23895. # <%= content_tag_for(:li, @person, class: "bar") %>...
  23896. #
  23897. # produces:
  23898. #
  23899. # <li id="person_123" class="person bar">...
  23900. #
  23901. def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options = nil, &block)
  23902. options, prefix = prefix, nil if prefix.is_a?(Hash)
  23903. Array(single_or_multiple_records).map do |single_record|
  23904. content_tag_for_single_record(tag_name, single_record, prefix, options, &block)
  23905. end.join("\n").html_safe
  23906. end
  23907. private
  23908. # Called by <tt>content_tag_for</tt> internally to render a content tag
  23909. # for each record.
  23910. def content_tag_for_single_record(tag_name, record, prefix, options, &block)
  23911. options = options ? options.dup : {}
  23912. options[:class] = [ dom_class(record, prefix), options[:class] ].compact
  23913. options[:id] = dom_id(record, prefix)
  23914. if block_given?
  23915. content_tag(tag_name, capture(record, &block), options)
  23916. else
  23917. content_tag(tag_name, "", options)
  23918. end
  23919. end
  23920. end
  23921. end
  23922. end
  23923. module ActionView
  23924. module Helpers
  23925. # = Action View Rendering
  23926. #
  23927. # Implements methods that allow rendering from a view context.
  23928. # In order to use this module, all you need is to implement
  23929. # view_renderer that returns an ActionView::Renderer object.
  23930. module RenderingHelper
  23931. # Returns the result of a render that's dictated by the options hash. The primary options are:
  23932. #
  23933. # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt>.
  23934. # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
  23935. # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
  23936. # * <tt>:text</tt> - Renders the text passed in out.
  23937. #
  23938. # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
  23939. # as the locals hash.
  23940. def render(options = {}, locals = {}, &block)
  23941. case options
  23942. when Hash
  23943. if block_given?
  23944. view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block)
  23945. else
  23946. view_renderer.render(self, options)
  23947. end
  23948. else
  23949. view_renderer.render_partial(self, :partial => options, :locals => locals)
  23950. end
  23951. end
  23952. # Overwrites _layout_for in the context object so it supports the case a block is
  23953. # passed to a partial. Returns the contents that are yielded to a layout, given a
  23954. # name or a block.
  23955. #
  23956. # You can think of a layout as a method that is called with a block. If the user calls
  23957. # <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
  23958. # If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
  23959. #
  23960. # The user can override this default by passing a block to the layout:
  23961. #
  23962. # # The template
  23963. # <%= render layout: "my_layout" do %>
  23964. # Content
  23965. # <% end %>
  23966. #
  23967. # # The layout
  23968. # <html>
  23969. # <%= yield %>
  23970. # </html>
  23971. #
  23972. # In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
  23973. # this method returns the block that was passed in to <tt>render :layout</tt>, and the response
  23974. # would be
  23975. #
  23976. # <html>
  23977. # Content
  23978. # </html>
  23979. #
  23980. # Finally, the block can take block arguments, which can be passed in by +yield+:
  23981. #
  23982. # # The template
  23983. # <%= render layout: "my_layout" do |customer| %>
  23984. # Hello <%= customer.name %>
  23985. # <% end %>
  23986. #
  23987. # # The layout
  23988. # <html>
  23989. # <%= yield Struct.new(:name).new("David") %>
  23990. # </html>
  23991. #
  23992. # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
  23993. # and the struct specified would be passed into the block as an argument. The result
  23994. # would be
  23995. #
  23996. # <html>
  23997. # Hello David
  23998. # </html>
  23999. #
  24000. def _layout_for(*args, &block)
  24001. name = args.first
  24002. if block && !name.is_a?(Symbol)
  24003. capture(*args, &block)
  24004. else
  24005. super
  24006. end
  24007. end
  24008. end
  24009. end
  24010. end
  24011. require 'active_support/core_ext/object/try'
  24012. require 'action_view/vendor/html-scanner'
  24013. module ActionView
  24014. # = Action View Sanitize Helpers
  24015. module Helpers
  24016. # The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
  24017. # These helper methods extend Action View making them callable within your template files.
  24018. module SanitizeHelper
  24019. extend ActiveSupport::Concern
  24020. # This +sanitize+ helper will html encode all tags and strip all attributes that
  24021. # aren't specifically allowed.
  24022. #
  24023. # It also strips href/src tags with invalid protocols, like javascript: especially.
  24024. # It does its best to counter any tricks that hackers may use, like throwing in
  24025. # unicode/ascii/hex values to get past the javascript: filters. Check out
  24026. # the extensive test suite.
  24027. #
  24028. # <%= sanitize @article.body %>
  24029. #
  24030. # You can add or remove tags/attributes if you want to customize it a bit.
  24031. # See ActionView::Base for full docs on the available options. You can add
  24032. # tags/attributes for single uses of +sanitize+ by passing either the
  24033. # <tt>:attributes</tt> or <tt>:tags</tt> options:
  24034. #
  24035. # Normal Use
  24036. #
  24037. # <%= sanitize @article.body %>
  24038. #
  24039. # Custom Use (only the mentioned tags and attributes are allowed, nothing else)
  24040. #
  24041. # <%= sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style) %>
  24042. #
  24043. # Add table tags to the default allowed tags
  24044. #
  24045. # class Application < Rails::Application
  24046. # config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
  24047. # end
  24048. #
  24049. # Remove tags to the default allowed tags
  24050. #
  24051. # class Application < Rails::Application
  24052. # config.after_initialize do
  24053. # ActionView::Base.sanitized_allowed_tags.delete 'div'
  24054. # end
  24055. # end
  24056. #
  24057. # Change allowed default attributes
  24058. #
  24059. # class Application < Rails::Application
  24060. # config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style'
  24061. # end
  24062. #
  24063. # Please note that sanitizing user-provided text does not guarantee that the
  24064. # resulting markup is valid (conforming to a document type) or even well-formed.
  24065. # The output may still contain e.g. unescaped '<', '>', '&' characters and
  24066. # confuse browsers.
  24067. #
  24068. def sanitize(html, options = {})
  24069. self.class.white_list_sanitizer.sanitize(html, options).try(:html_safe)
  24070. end
  24071. # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
  24072. def sanitize_css(style)
  24073. self.class.white_list_sanitizer.sanitize_css(style)
  24074. end
  24075. # Strips all HTML tags from the +html+, including comments. This uses the
  24076. # html-scanner tokenizer and so its HTML parsing ability is limited by
  24077. # that of html-scanner.
  24078. #
  24079. # strip_tags("Strip <i>these</i> tags!")
  24080. # # => Strip these tags!
  24081. #
  24082. # strip_tags("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
  24083. # # => Bold no more! See more here...
  24084. #
  24085. # strip_tags("<div id='top-bar'>Welcome to my website!</div>")
  24086. # # => Welcome to my website!
  24087. def strip_tags(html)
  24088. self.class.full_sanitizer.sanitize(html)
  24089. end
  24090. # Strips all link tags from +text+ leaving just the link text.
  24091. #
  24092. # strip_links('<a href="http://www.rubyonrails.org">Ruby on Rails</a>')
  24093. # # => Ruby on Rails
  24094. #
  24095. # strip_links('Please e-mail me at <a href="mailto:me@email.com">me@email.com</a>.')
  24096. # # => Please e-mail me at me@email.com.
  24097. #
  24098. # strip_links('Blog: <a href="http://www.myblog.com/" class="nav" target=\"_blank\">Visit</a>.')
  24099. # # => Blog: Visit.
  24100. def strip_links(html)
  24101. self.class.link_sanitizer.sanitize(html)
  24102. end
  24103. module ClassMethods #:nodoc:
  24104. attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
  24105. def sanitized_protocol_separator
  24106. white_list_sanitizer.protocol_separator
  24107. end
  24108. def sanitized_uri_attributes
  24109. white_list_sanitizer.uri_attributes
  24110. end
  24111. def sanitized_bad_tags
  24112. white_list_sanitizer.bad_tags
  24113. end
  24114. def sanitized_allowed_tags
  24115. white_list_sanitizer.allowed_tags
  24116. end
  24117. def sanitized_allowed_attributes
  24118. white_list_sanitizer.allowed_attributes
  24119. end
  24120. def sanitized_allowed_css_properties
  24121. white_list_sanitizer.allowed_css_properties
  24122. end
  24123. def sanitized_allowed_css_keywords
  24124. white_list_sanitizer.allowed_css_keywords
  24125. end
  24126. def sanitized_shorthand_css_properties
  24127. white_list_sanitizer.shorthand_css_properties
  24128. end
  24129. def sanitized_allowed_protocols
  24130. white_list_sanitizer.allowed_protocols
  24131. end
  24132. def sanitized_protocol_separator=(value)
  24133. white_list_sanitizer.protocol_separator = value
  24134. end
  24135. # Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with
  24136. # any object that responds to +sanitize+.
  24137. #
  24138. # class Application < Rails::Application
  24139. # config.action_view.full_sanitizer = MySpecialSanitizer.new
  24140. # end
  24141. #
  24142. def full_sanitizer
  24143. @full_sanitizer ||= HTML::FullSanitizer.new
  24144. end
  24145. # Gets the HTML::LinkSanitizer instance used by +strip_links+. Replace with
  24146. # any object that responds to +sanitize+.
  24147. #
  24148. # class Application < Rails::Application
  24149. # config.action_view.link_sanitizer = MySpecialSanitizer.new
  24150. # end
  24151. #
  24152. def link_sanitizer
  24153. @link_sanitizer ||= HTML::LinkSanitizer.new
  24154. end
  24155. # Gets the HTML::WhiteListSanitizer instance used by sanitize and +sanitize_css+.
  24156. # Replace with any object that responds to +sanitize+.
  24157. #
  24158. # class Application < Rails::Application
  24159. # config.action_view.white_list_sanitizer = MySpecialSanitizer.new
  24160. # end
  24161. #
  24162. def white_list_sanitizer
  24163. @white_list_sanitizer ||= HTML::WhiteListSanitizer.new
  24164. end
  24165. # Adds valid HTML attributes that the +sanitize+ helper checks for URIs.
  24166. #
  24167. # class Application < Rails::Application
  24168. # config.action_view.sanitized_uri_attributes = 'lowsrc', 'target'
  24169. # end
  24170. #
  24171. def sanitized_uri_attributes=(attributes)
  24172. HTML::WhiteListSanitizer.uri_attributes.merge(attributes)
  24173. end
  24174. # Adds to the Set of 'bad' tags for the +sanitize+ helper.
  24175. #
  24176. # class Application < Rails::Application
  24177. # config.action_view.sanitized_bad_tags = 'embed', 'object'
  24178. # end
  24179. #
  24180. def sanitized_bad_tags=(attributes)
  24181. HTML::WhiteListSanitizer.bad_tags.merge(attributes)
  24182. end
  24183. # Adds to the Set of allowed tags for the +sanitize+ helper.
  24184. #
  24185. # class Application < Rails::Application
  24186. # config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
  24187. # end
  24188. #
  24189. def sanitized_allowed_tags=(attributes)
  24190. HTML::WhiteListSanitizer.allowed_tags.merge(attributes)
  24191. end
  24192. # Adds to the Set of allowed HTML attributes for the +sanitize+ helper.
  24193. #
  24194. # class Application < Rails::Application
  24195. # config.action_view.sanitized_allowed_attributes = 'onclick', 'longdesc'
  24196. # end
  24197. #
  24198. def sanitized_allowed_attributes=(attributes)
  24199. HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
  24200. end
  24201. # Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
  24202. #
  24203. # class Application < Rails::Application
  24204. # config.action_view.sanitized_allowed_css_properties = 'expression'
  24205. # end
  24206. #
  24207. def sanitized_allowed_css_properties=(attributes)
  24208. HTML::WhiteListSanitizer.allowed_css_properties.merge(attributes)
  24209. end
  24210. # Adds to the Set of allowed CSS keywords for the +sanitize+ and +sanitize_css+ helpers.
  24211. #
  24212. # class Application < Rails::Application
  24213. # config.action_view.sanitized_allowed_css_keywords = 'expression'
  24214. # end
  24215. #
  24216. def sanitized_allowed_css_keywords=(attributes)
  24217. HTML::WhiteListSanitizer.allowed_css_keywords.merge(attributes)
  24218. end
  24219. # Adds to the Set of allowed shorthand CSS properties for the +sanitize+ and +sanitize_css+ helpers.
  24220. #
  24221. # class Application < Rails::Application
  24222. # config.action_view.sanitized_shorthand_css_properties = 'expression'
  24223. # end
  24224. #
  24225. def sanitized_shorthand_css_properties=(attributes)
  24226. HTML::WhiteListSanitizer.shorthand_css_properties.merge(attributes)
  24227. end
  24228. # Adds to the Set of allowed protocols for the +sanitize+ helper.
  24229. #
  24230. # class Application < Rails::Application
  24231. # config.action_view.sanitized_allowed_protocols = 'ssh', 'feed'
  24232. # end
  24233. #
  24234. def sanitized_allowed_protocols=(attributes)
  24235. HTML::WhiteListSanitizer.allowed_protocols.merge(attributes)
  24236. end
  24237. end
  24238. end
  24239. end
  24240. end
  24241. require 'active_support/core_ext/string/output_safety'
  24242. require 'set'
  24243. module ActionView
  24244. # = Action View Tag Helpers
  24245. module Helpers #:nodoc:
  24246. # Provides methods to generate HTML tags programmatically when you can't use
  24247. # a Builder. By default, they output XHTML compliant tags.
  24248. module TagHelper
  24249. extend ActiveSupport::Concern
  24250. include CaptureHelper
  24251. BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
  24252. autoplay controls loop selected hidden scoped async
  24253. defer reversed ismap seemless muted required
  24254. autofocus novalidate formnovalidate open pubdate itemscope).to_set
  24255. BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
  24256. PRE_CONTENT_STRINGS = {
  24257. :textarea => "\n"
  24258. }
  24259. # Returns an empty HTML tag of type +name+ which by default is XHTML
  24260. # compliant. Set +open+ to true to create an open tag compatible
  24261. # with HTML 4.0 and below. Add HTML attributes by passing an attributes
  24262. # hash to +options+. Set +escape+ to false to disable attribute value
  24263. # escaping.
  24264. #
  24265. # ==== Options
  24266. # You can use symbols or strings for the attribute names.
  24267. #
  24268. # Use +true+ with boolean attributes that can render with no value, like
  24269. # +disabled+ and +readonly+.
  24270. #
  24271. # HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
  24272. # pointing to a hash of sub-attributes.
  24273. #
  24274. # To play nicely with JavaScript conventions sub-attributes are dasherized.
  24275. # For example, a key +user_id+ would render as <tt>data-user-id</tt> and
  24276. # thus accessed as <tt>dataset.userId</tt>.
  24277. #
  24278. # Values are encoded to JSON, with the exception of strings and symbols.
  24279. # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
  24280. # from 1.4.3.
  24281. #
  24282. # ==== Examples
  24283. # tag("br")
  24284. # # => <br />
  24285. #
  24286. # tag("br", nil, true)
  24287. # # => <br>
  24288. #
  24289. # tag("input", type: 'text', disabled: true)
  24290. # # => <input type="text" disabled="disabled" />
  24291. #
  24292. # tag("img", src: "open & shut.png")
  24293. # # => <img src="open &amp; shut.png" />
  24294. #
  24295. # tag("img", {src: "open &amp; shut.png"}, false, false)
  24296. # # => <img src="open &amp; shut.png" />
  24297. #
  24298. # tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
  24299. # # => <div data-name="Stephen" data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]" />
  24300. def tag(name, options = nil, open = false, escape = true)
  24301. "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
  24302. end
  24303. # Returns an HTML block tag of type +name+ surrounding the +content+. Add
  24304. # HTML attributes by passing an attributes hash to +options+.
  24305. # Instead of passing the content as an argument, you can also use a block
  24306. # in which case, you pass your +options+ as the second parameter.
  24307. # Set escape to false to disable attribute value escaping.
  24308. #
  24309. # ==== Options
  24310. # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
  24311. # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
  24312. # symbols or strings for the attribute names.
  24313. #
  24314. # ==== Examples
  24315. # content_tag(:p, "Hello world!")
  24316. # # => <p>Hello world!</p>
  24317. # content_tag(:div, content_tag(:p, "Hello world!"), class: "strong")
  24318. # # => <div class="strong"><p>Hello world!</p></div>
  24319. # content_tag("select", options, multiple: true)
  24320. # # => <select multiple="multiple">...options...</select>
  24321. #
  24322. # <%= content_tag :div, class: "strong" do -%>
  24323. # Hello world!
  24324. # <% end -%>
  24325. # # => <div class="strong">Hello world!</div>
  24326. def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
  24327. if block_given?
  24328. options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
  24329. content_tag_string(name, capture(&block), options, escape)
  24330. else
  24331. content_tag_string(name, content_or_options_with_block, options, escape)
  24332. end
  24333. end
  24334. # Returns a CDATA section with the given +content+. CDATA sections
  24335. # are used to escape blocks of text containing characters which would
  24336. # otherwise be recognized as markup. CDATA sections begin with the string
  24337. # <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>.
  24338. #
  24339. # cdata_section("<hello world>")
  24340. # # => <![CDATA[<hello world>]]>
  24341. #
  24342. # cdata_section(File.read("hello_world.txt"))
  24343. # # => <![CDATA[<hello from a text file]]>
  24344. #
  24345. # cdata_section("hello]]>world")
  24346. # # => <![CDATA[hello]]]]><![CDATA[>world]]>
  24347. def cdata_section(content)
  24348. splitted = content.gsub(']]>', ']]]]><![CDATA[>')
  24349. "<![CDATA[#{splitted}]]>".html_safe
  24350. end
  24351. # Returns an escaped version of +html+ without affecting existing escaped entities.
  24352. #
  24353. # escape_once("1 < 2 &amp; 3")
  24354. # # => "1 &lt; 2 &amp; 3"
  24355. #
  24356. # escape_once("&lt;&lt; Accept & Checkout")
  24357. # # => "&lt;&lt; Accept &amp; Checkout"
  24358. def escape_once(html)
  24359. ERB::Util.html_escape_once(html)
  24360. end
  24361. private
  24362. def content_tag_string(name, content, options, escape = true)
  24363. tag_options = tag_options(options, escape) if options
  24364. content = ERB::Util.h(content) if escape
  24365. "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
  24366. end
  24367. def tag_options(options, escape = true)
  24368. return if options.blank?
  24369. attrs = []
  24370. options.each_pair do |key, value|
  24371. if key.to_s == 'data' && value.is_a?(Hash)
  24372. value.each_pair do |k, v|
  24373. attrs << data_tag_option(k, v, escape)
  24374. end
  24375. elsif BOOLEAN_ATTRIBUTES.include?(key)
  24376. attrs << boolean_tag_option(key) if value
  24377. elsif !value.nil?
  24378. attrs << tag_option(key, value, escape)
  24379. end
  24380. end
  24381. " #{attrs.sort * ' '}".html_safe unless attrs.empty?
  24382. end
  24383. def data_tag_option(key, value, escape)
  24384. key = "data-#{key.to_s.dasherize}"
  24385. unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
  24386. value = value.to_json
  24387. end
  24388. tag_option(key, value, escape)
  24389. end
  24390. def boolean_tag_option(key)
  24391. %(#{key}="#{key}")
  24392. end
  24393. def tag_option(key, value, escape)
  24394. value = value.join(" ") if value.is_a?(Array)
  24395. value = ERB::Util.h(value) if escape
  24396. %(#{key}="#{value}")
  24397. end
  24398. end
  24399. end
  24400. end
  24401. module ActionView
  24402. module Helpers
  24403. module Tags
  24404. class Base #:nodoc:
  24405. include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
  24406. include FormOptionsHelper
  24407. attr_reader :object
  24408. def initialize(object_name, method_name, template_object, options = {})
  24409. @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
  24410. @template_object = template_object
  24411. @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
  24412. @object = retrieve_object(options.delete(:object))
  24413. @options = options
  24414. @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
  24415. end
  24416. # This is what child classes implement.
  24417. def render
  24418. raise NotImplementedError, "Subclasses must implement a render method"
  24419. end
  24420. private
  24421. def value(object)
  24422. object.send @method_name if object
  24423. end
  24424. def value_before_type_cast(object)
  24425. unless object.nil?
  24426. method_before_type_cast = @method_name + "_before_type_cast"
  24427. object.respond_to?(method_before_type_cast) ?
  24428. object.send(method_before_type_cast) :
  24429. value(object)
  24430. end
  24431. end
  24432. def retrieve_object(object)
  24433. if object
  24434. object
  24435. elsif @template_object.instance_variable_defined?("@#{@object_name}")
  24436. @template_object.instance_variable_get("@#{@object_name}")
  24437. end
  24438. rescue NameError
  24439. # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
  24440. nil
  24441. end
  24442. def retrieve_autoindex(pre_match)
  24443. object = self.object || @template_object.instance_variable_get("@#{pre_match}")
  24444. if object && object.respond_to?(:to_param)
  24445. object.to_param
  24446. else
  24447. raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
  24448. end
  24449. end
  24450. def add_default_name_and_id_for_value(tag_value, options)
  24451. if tag_value.nil?
  24452. add_default_name_and_id(options)
  24453. else
  24454. specified_id = options["id"]
  24455. add_default_name_and_id(options)
  24456. if specified_id.blank? && options["id"].present?
  24457. options["id"] += "_#{sanitized_value(tag_value)}"
  24458. end
  24459. end
  24460. end
  24461. def add_default_name_and_id(options)
  24462. if options.has_key?("index")
  24463. options["name"] ||= options.fetch("name"){ tag_name_with_index(options["index"]) }
  24464. options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
  24465. options.delete("index")
  24466. elsif defined?(@auto_index)
  24467. options["name"] ||= options.fetch("name"){ tag_name_with_index(@auto_index) }
  24468. options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
  24469. else
  24470. options["name"] ||= options.fetch("name"){ tag_name }
  24471. options["id"] = options.fetch("id"){ tag_id }
  24472. end
  24473. options["name"] += "[]" if options["multiple"]
  24474. options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
  24475. end
  24476. def tag_name
  24477. "#{@object_name}[#{sanitized_method_name}]"
  24478. end
  24479. def tag_name_with_index(index)
  24480. "#{@object_name}[#{index}][#{sanitized_method_name}]"
  24481. end
  24482. def tag_id
  24483. "#{sanitized_object_name}_#{sanitized_method_name}"
  24484. end
  24485. def tag_id_with_index(index)
  24486. "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
  24487. end
  24488. def sanitized_object_name
  24489. @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
  24490. end
  24491. def sanitized_method_name
  24492. @sanitized_method_name ||= @method_name.sub(/\?$/,"")
  24493. end
  24494. def sanitized_value(value)
  24495. value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
  24496. end
  24497. def select_content_tag(option_tags, options, html_options)
  24498. html_options = html_options.stringify_keys
  24499. add_default_name_and_id(html_options)
  24500. options[:include_blank] ||= true unless options[:prompt] || select_not_required?(html_options)
  24501. select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
  24502. if html_options["multiple"] && options.fetch(:include_hidden, true)
  24503. tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
  24504. else
  24505. select
  24506. end
  24507. end
  24508. def select_not_required?(html_options)
  24509. !html_options["required"] || html_options["multiple"] || html_options["size"].to_i > 1
  24510. end
  24511. def add_options(option_tags, options, value = nil)
  24512. if options[:include_blank]
  24513. option_tags = content_tag_string('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
  24514. end
  24515. if value.blank? && options[:prompt]
  24516. option_tags = content_tag_string('option', prompt_text(options[:prompt]), :value => '') + "\n" + option_tags
  24517. end
  24518. option_tags
  24519. end
  24520. end
  24521. end
  24522. end
  24523. end
  24524. require 'action_view/helpers/tags/checkable'
  24525. module ActionView
  24526. module Helpers
  24527. module Tags
  24528. class CheckBox < Base #:nodoc:
  24529. include Checkable
  24530. def initialize(object_name, method_name, template_object, checked_value, unchecked_value, options)
  24531. @checked_value = checked_value
  24532. @unchecked_value = unchecked_value
  24533. super(object_name, method_name, template_object, options)
  24534. end
  24535. def render
  24536. options = @options.stringify_keys
  24537. options["type"] = "checkbox"
  24538. options["value"] = @checked_value
  24539. options["checked"] = "checked" if input_checked?(object, options)
  24540. if options["multiple"]
  24541. add_default_name_and_id_for_value(@checked_value, options)
  24542. options.delete("multiple")
  24543. else
  24544. add_default_name_and_id(options)
  24545. end
  24546. include_hidden = options.delete("include_hidden") { true }
  24547. checkbox = tag("input", options)
  24548. if include_hidden
  24549. hidden = hidden_field_for_checkbox(options)
  24550. hidden + checkbox
  24551. else
  24552. checkbox
  24553. end
  24554. end
  24555. private
  24556. def checked?(value)
  24557. case value
  24558. when TrueClass, FalseClass
  24559. value == !!@checked_value
  24560. when NilClass
  24561. false
  24562. when String
  24563. value == @checked_value
  24564. else
  24565. if value.respond_to?(:include?)
  24566. value.include?(@checked_value)
  24567. else
  24568. value.to_i == @checked_value.to_i
  24569. end
  24570. end
  24571. end
  24572. def hidden_field_for_checkbox(options)
  24573. @unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value)) : "".html_safe
  24574. end
  24575. end
  24576. end
  24577. end
  24578. end
  24579. module ActionView
  24580. module Helpers
  24581. module Tags
  24582. module Checkable
  24583. def input_checked?(object, options)
  24584. if options.has_key?("checked")
  24585. checked = options.delete "checked"
  24586. checked == true || checked == "checked"
  24587. else
  24588. checked?(value(object))
  24589. end
  24590. end
  24591. end
  24592. end
  24593. end
  24594. end
  24595. require 'action_view/helpers/tags/collection_helpers'
  24596. module ActionView
  24597. module Helpers
  24598. module Tags
  24599. class CollectionCheckBoxes < Base
  24600. include CollectionHelpers
  24601. class CheckBoxBuilder < Builder
  24602. def check_box(extra_html_options={})
  24603. html_options = extra_html_options.merge(@input_html_options)
  24604. @template_object.check_box(@object_name, @method_name, html_options, @value, nil)
  24605. end
  24606. end
  24607. def render(&block)
  24608. rendered_collection = render_collection do |item, value, text, default_html_options|
  24609. default_html_options[:multiple] = true
  24610. builder = instantiate_builder(CheckBoxBuilder, item, value, text, default_html_options)
  24611. if block_given?
  24612. @template_object.capture(builder, &block)
  24613. else
  24614. render_component(builder)
  24615. end
  24616. end
  24617. # Append a hidden field to make sure something will be sent back to the
  24618. # server if all check boxes are unchecked.
  24619. hidden = @template_object.hidden_field_tag("#{tag_name}[]", "", :id => nil)
  24620. rendered_collection + hidden
  24621. end
  24622. private
  24623. def render_component(builder)
  24624. builder.check_box + builder.label
  24625. end
  24626. end
  24627. end
  24628. end
  24629. end
  24630. module ActionView
  24631. module Helpers
  24632. module Tags
  24633. module CollectionHelpers
  24634. class Builder
  24635. attr_reader :object, :text, :value
  24636. def initialize(template_object, object_name, method_name, object,
  24637. sanitized_attribute_name, text, value, input_html_options)
  24638. @template_object = template_object
  24639. @object_name = object_name
  24640. @method_name = method_name
  24641. @object = object
  24642. @sanitized_attribute_name = sanitized_attribute_name
  24643. @text = text
  24644. @value = value
  24645. @input_html_options = input_html_options
  24646. end
  24647. def label(label_html_options={}, &block)
  24648. @template_object.label(@object_name, @sanitized_attribute_name, @text, label_html_options, &block)
  24649. end
  24650. end
  24651. def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
  24652. @collection = collection
  24653. @value_method = value_method
  24654. @text_method = text_method
  24655. @html_options = html_options
  24656. super(object_name, method_name, template_object, options)
  24657. end
  24658. private
  24659. def instantiate_builder(builder_class, item, value, text, html_options)
  24660. builder_class.new(@template_object, @object_name, @method_name, item,
  24661. sanitize_attribute_name(value), text, value, html_options)
  24662. end
  24663. # Generate default options for collection helpers, such as :checked and
  24664. # :disabled.
  24665. def default_html_options_for_collection(item, value) #:nodoc:
  24666. html_options = @html_options.dup
  24667. [:checked, :selected, :disabled].each do |option|
  24668. current_value = @options[option]
  24669. next if current_value.nil?
  24670. accept = if current_value.respond_to?(:call)
  24671. current_value.call(item)
  24672. else
  24673. Array(current_value).map(&:to_s).include?(value.to_s)
  24674. end
  24675. if accept
  24676. html_options[option] = true
  24677. elsif option == :checked
  24678. html_options[option] = false
  24679. end
  24680. end
  24681. html_options[:object] = @object
  24682. html_options
  24683. end
  24684. def sanitize_attribute_name(value) #:nodoc:
  24685. "#{sanitized_method_name}_#{sanitized_value(value)}"
  24686. end
  24687. def render_collection #:nodoc:
  24688. @collection.map do |item|
  24689. value = value_for_collection(item, @value_method)
  24690. text = value_for_collection(item, @text_method)
  24691. default_html_options = default_html_options_for_collection(item, value)
  24692. yield item, value, text, default_html_options
  24693. end.join.html_safe
  24694. end
  24695. end
  24696. end
  24697. end
  24698. end
  24699. require 'action_view/helpers/tags/collection_helpers'
  24700. module ActionView
  24701. module Helpers
  24702. module Tags
  24703. class CollectionRadioButtons < Base
  24704. include CollectionHelpers
  24705. class RadioButtonBuilder < Builder
  24706. def radio_button(extra_html_options={})
  24707. html_options = extra_html_options.merge(@input_html_options)
  24708. @template_object.radio_button(@object_name, @method_name, @value, html_options)
  24709. end
  24710. end
  24711. def render(&block)
  24712. render_collection do |item, value, text, default_html_options|
  24713. builder = instantiate_builder(RadioButtonBuilder, item, value, text, default_html_options)
  24714. if block_given?
  24715. @template_object.capture(builder, &block)
  24716. else
  24717. render_component(builder)
  24718. end
  24719. end
  24720. end
  24721. private
  24722. def render_component(builder)
  24723. builder.radio_button + builder.label
  24724. end
  24725. end
  24726. end
  24727. end
  24728. end
  24729. module ActionView
  24730. module Helpers
  24731. module Tags
  24732. class CollectionSelect < Base #:nodoc:
  24733. def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
  24734. @collection = collection
  24735. @value_method = value_method
  24736. @text_method = text_method
  24737. @html_options = html_options
  24738. super(object_name, method_name, template_object, options)
  24739. end
  24740. def render
  24741. option_tags_options = {
  24742. :selected => @options.fetch(:selected) { value(@object) },
  24743. :disabled => @options[:disabled]
  24744. }
  24745. select_content_tag(
  24746. options_from_collection_for_select(@collection, @value_method, @text_method, option_tags_options),
  24747. @options, @html_options
  24748. )
  24749. end
  24750. end
  24751. end
  24752. end
  24753. end
  24754. module ActionView
  24755. module Helpers
  24756. module Tags
  24757. class ColorField < TextField #:nodoc:
  24758. def render
  24759. options = @options.stringify_keys
  24760. options["value"] = @options.fetch("value") { validate_color_string(value(object)) }
  24761. @options = options
  24762. super
  24763. end
  24764. private
  24765. def validate_color_string(string)
  24766. regex = /#[0-9a-fA-F]{6}/
  24767. if regex.match(string)
  24768. string.downcase
  24769. else
  24770. "#000000"
  24771. end
  24772. end
  24773. end
  24774. end
  24775. end
  24776. end
  24777. module ActionView
  24778. module Helpers
  24779. module Tags
  24780. class DateField < DatetimeField #:nodoc:
  24781. private
  24782. def format_date(value)
  24783. value.try(:strftime, "%Y-%m-%d")
  24784. end
  24785. end
  24786. end
  24787. end
  24788. end
  24789. require 'active_support/core_ext/time/calculations'
  24790. module ActionView
  24791. module Helpers
  24792. module Tags
  24793. class DateSelect < Base #:nodoc:
  24794. def initialize(object_name, method_name, template_object, options, html_options)
  24795. @html_options = html_options
  24796. super(object_name, method_name, template_object, options)
  24797. end
  24798. def render
  24799. error_wrapping(datetime_selector(@options, @html_options).send("select_#{select_type}").html_safe)
  24800. end
  24801. class << self
  24802. def select_type
  24803. @select_type ||= self.name.split("::").last.sub("Select", "").downcase
  24804. end
  24805. end
  24806. private
  24807. def select_type
  24808. self.class.select_type
  24809. end
  24810. def datetime_selector(options, html_options)
  24811. datetime = options.fetch(:selected) { value(object) || default_datetime(options) }
  24812. @auto_index ||= nil
  24813. options = options.dup
  24814. options[:field_name] = @method_name
  24815. options[:include_position] = true
  24816. options[:prefix] ||= @object_name
  24817. options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
  24818. DateTimeSelector.new(datetime, options, html_options)
  24819. end
  24820. def default_datetime(options)
  24821. return if options[:include_blank] || options[:prompt]
  24822. case options[:default]
  24823. when nil
  24824. Time.current
  24825. when Date, Time
  24826. options[:default]
  24827. else
  24828. default = options[:default].dup
  24829. # Rename :minute and :second to :min and :sec
  24830. default[:min] ||= default[:minute]
  24831. default[:sec] ||= default[:second]
  24832. time = Time.current
  24833. [:year, :month, :day, :hour, :min, :sec].each do |key|
  24834. default[key] ||= time.send(key)
  24835. end
  24836. Time.utc(
  24837. default[:year], default[:month], default[:day],
  24838. default[:hour], default[:min], default[:sec]
  24839. )
  24840. end
  24841. end
  24842. end
  24843. end
  24844. end
  24845. end
  24846. module ActionView
  24847. module Helpers
  24848. module Tags
  24849. class DatetimeField < TextField #:nodoc:
  24850. def render
  24851. options = @options.stringify_keys
  24852. options["value"] = @options.fetch("value") { format_date(value(object)) }
  24853. options["min"] = format_date(options["min"])
  24854. options["max"] = format_date(options["max"])
  24855. @options = options
  24856. super
  24857. end
  24858. private
  24859. def format_date(value)
  24860. value.try(:strftime, "%Y-%m-%dT%T.%L%z")
  24861. end
  24862. end
  24863. end
  24864. end
  24865. end
  24866. module ActionView
  24867. module Helpers
  24868. module Tags
  24869. class DatetimeLocalField < DatetimeField #:nodoc:
  24870. class << self
  24871. def field_type
  24872. @field_type ||= "datetime-local"
  24873. end
  24874. end
  24875. private
  24876. def format_date(value)
  24877. value.try(:strftime, "%Y-%m-%dT%T")
  24878. end
  24879. end
  24880. end
  24881. end
  24882. end
  24883. module ActionView
  24884. module Helpers
  24885. module Tags
  24886. class DatetimeSelect < DateSelect #:nodoc:
  24887. end
  24888. end
  24889. end
  24890. end
  24891. module ActionView
  24892. module Helpers
  24893. module Tags
  24894. class EmailField < TextField #:nodoc:
  24895. end
  24896. end
  24897. end
  24898. end
  24899. module ActionView
  24900. module Helpers
  24901. module Tags
  24902. class FileField < TextField #:nodoc:
  24903. end
  24904. end
  24905. end
  24906. end
  24907. module ActionView
  24908. module Helpers
  24909. module Tags
  24910. class GroupedCollectionSelect < Base #:nodoc:
  24911. def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
  24912. @collection = collection
  24913. @group_method = group_method
  24914. @group_label_method = group_label_method
  24915. @option_key_method = option_key_method
  24916. @option_value_method = option_value_method
  24917. @html_options = html_options
  24918. super(object_name, method_name, template_object, options)
  24919. end
  24920. def render
  24921. option_tags_options = {
  24922. :selected => @options.fetch(:selected) { value(@object) },
  24923. :disabled => @options[:disabled]
  24924. }
  24925. select_content_tag(
  24926. option_groups_from_collection_for_select(@collection, @group_method, @group_label_method, @option_key_method, @option_value_method, option_tags_options), @options, @html_options
  24927. )
  24928. end
  24929. end
  24930. end
  24931. end
  24932. end
  24933. module ActionView
  24934. module Helpers
  24935. module Tags
  24936. class HiddenField < TextField #:nodoc:
  24937. end
  24938. end
  24939. end
  24940. end
  24941. module ActionView
  24942. module Helpers
  24943. module Tags
  24944. class Label < Base #:nodoc:
  24945. def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil)
  24946. options ||= {}
  24947. content_is_options = content_or_options.is_a?(Hash)
  24948. if content_is_options
  24949. options.merge! content_or_options
  24950. @content = nil
  24951. else
  24952. @content = content_or_options
  24953. end
  24954. super(object_name, method_name, template_object, options)
  24955. end
  24956. def render(&block)
  24957. options = @options.stringify_keys
  24958. tag_value = options.delete("value")
  24959. name_and_id = options.dup
  24960. if name_and_id["for"]
  24961. name_and_id["id"] = name_and_id["for"]
  24962. else
  24963. name_and_id.delete("id")
  24964. end
  24965. add_default_name_and_id_for_value(tag_value, name_and_id)
  24966. options.delete("index")
  24967. options.delete("namespace")
  24968. options["for"] = name_and_id["id"] unless options.key?("for")
  24969. if block_given?
  24970. content = @template_object.capture(&block)
  24971. else
  24972. content = if @content.blank?
  24973. @object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1')
  24974. method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name
  24975. if object.respond_to?(:to_model)
  24976. key = object.class.model_name.i18n_key
  24977. i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
  24978. end
  24979. i18n_default ||= ""
  24980. I18n.t("#{@object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence
  24981. else
  24982. @content.to_s
  24983. end
  24984. content ||= if object && object.class.respond_to?(:human_attribute_name)
  24985. object.class.human_attribute_name(@method_name)
  24986. end
  24987. content ||= @method_name.humanize
  24988. end
  24989. label_tag(name_and_id["id"], content, options)
  24990. end
  24991. end
  24992. end
  24993. end
  24994. end
  24995. module ActionView
  24996. module Helpers
  24997. module Tags
  24998. class MonthField < DatetimeField #:nodoc:
  24999. private
  25000. def format_date(value)
  25001. value.try(:strftime, "%Y-%m")
  25002. end
  25003. end
  25004. end
  25005. end
  25006. end
  25007. module ActionView
  25008. module Helpers
  25009. module Tags
  25010. class NumberField < TextField #:nodoc:
  25011. def render
  25012. options = @options.stringify_keys
  25013. if range = options.delete("in") || options.delete("within")
  25014. options.update("min" => range.min, "max" => range.max)
  25015. end
  25016. @options = options
  25017. super
  25018. end
  25019. end
  25020. end
  25021. end
  25022. end
  25023. module ActionView
  25024. module Helpers
  25025. module Tags
  25026. class PasswordField < TextField #:nodoc:
  25027. def render
  25028. @options = {:value => nil}.merge!(@options)
  25029. super
  25030. end
  25031. end
  25032. end
  25033. end
  25034. end
  25035. require 'action_view/helpers/tags/checkable'
  25036. module ActionView
  25037. module Helpers
  25038. module Tags
  25039. class RadioButton < Base #:nodoc:
  25040. include Checkable
  25041. def initialize(object_name, method_name, template_object, tag_value, options)
  25042. @tag_value = tag_value
  25043. super(object_name, method_name, template_object, options)
  25044. end
  25045. def render
  25046. options = @options.stringify_keys
  25047. options["type"] = "radio"
  25048. options["value"] = @tag_value
  25049. options["checked"] = "checked" if input_checked?(object, options)
  25050. add_default_name_and_id_for_value(@tag_value, options)
  25051. tag("input", options)
  25052. end
  25053. private
  25054. def checked?(value)
  25055. value.to_s == @tag_value.to_s
  25056. end
  25057. end
  25058. end
  25059. end
  25060. end
  25061. module ActionView
  25062. module Helpers
  25063. module Tags
  25064. class RangeField < NumberField #:nodoc:
  25065. end
  25066. end
  25067. end
  25068. end
  25069. module ActionView
  25070. module Helpers
  25071. module Tags
  25072. class SearchField < TextField #:nodoc:
  25073. def render
  25074. options = @options.stringify_keys
  25075. if options["autosave"]
  25076. if options["autosave"] == true
  25077. options["autosave"] = request.host.split(".").reverse.join(".")
  25078. end
  25079. options["results"] ||= 10
  25080. end
  25081. if options["onsearch"]
  25082. options["incremental"] = true unless options.has_key?("incremental")
  25083. end
  25084. super
  25085. end
  25086. end
  25087. end
  25088. end
  25089. end
  25090. module ActionView
  25091. module Helpers
  25092. module Tags
  25093. class Select < Base #:nodoc:
  25094. def initialize(object_name, method_name, template_object, choices, options, html_options)
  25095. @choices = choices
  25096. @choices = @choices.to_a if @choices.is_a?(Range)
  25097. @html_options = html_options
  25098. super(object_name, method_name, template_object, options)
  25099. end
  25100. def render
  25101. option_tags_options = {
  25102. :selected => @options.fetch(:selected) { value(@object) },
  25103. :disabled => @options[:disabled]
  25104. }
  25105. option_tags = if grouped_choices?
  25106. grouped_options_for_select(@choices, option_tags_options)
  25107. else
  25108. options_for_select(@choices, option_tags_options)
  25109. end
  25110. select_content_tag(option_tags, @options, @html_options)
  25111. end
  25112. private
  25113. # Grouped choices look like this:
  25114. #
  25115. # [nil, []]
  25116. # { nil => [] }
  25117. #
  25118. def grouped_choices?
  25119. !@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
  25120. end
  25121. end
  25122. end
  25123. end
  25124. end
  25125. module ActionView
  25126. module Helpers
  25127. module Tags
  25128. class TelField < TextField #:nodoc:
  25129. end
  25130. end
  25131. end
  25132. end
  25133. module ActionView
  25134. module Helpers
  25135. module Tags
  25136. class TextArea < Base #:nodoc:
  25137. def render
  25138. options = @options.stringify_keys
  25139. add_default_name_and_id(options)
  25140. if size = options.delete("size")
  25141. options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
  25142. end
  25143. content_tag("textarea", options.delete('value') || value_before_type_cast(object), options)
  25144. end
  25145. end
  25146. end
  25147. end
  25148. end
  25149. module ActionView
  25150. module Helpers
  25151. module Tags
  25152. class TextField < Base #:nodoc:
  25153. def render
  25154. options = @options.stringify_keys
  25155. options["size"] = options["maxlength"] unless options.key?("size")
  25156. options["type"] ||= field_type
  25157. options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file"
  25158. options["value"] &&= ERB::Util.html_escape(options["value"])
  25159. add_default_name_and_id(options)
  25160. tag("input", options)
  25161. end
  25162. class << self
  25163. def field_type
  25164. @field_type ||= self.name.split("::").last.sub("Field", "").downcase
  25165. end
  25166. end
  25167. private
  25168. def field_type
  25169. self.class.field_type
  25170. end
  25171. end
  25172. end
  25173. end
  25174. end
  25175. module ActionView
  25176. module Helpers
  25177. module Tags
  25178. class TimeField < DatetimeField #:nodoc:
  25179. private
  25180. def format_date(value)
  25181. value.try(:strftime, "%T.%L")
  25182. end
  25183. end
  25184. end
  25185. end
  25186. end
  25187. module ActionView
  25188. module Helpers
  25189. module Tags
  25190. class TimeSelect < DateSelect #:nodoc:
  25191. end
  25192. end
  25193. end
  25194. end
  25195. module ActionView
  25196. module Helpers
  25197. module Tags
  25198. class TimeZoneSelect < Base #:nodoc:
  25199. def initialize(object_name, method_name, template_object, priority_zones, options, html_options)
  25200. @priority_zones = priority_zones
  25201. @html_options = html_options
  25202. super(object_name, method_name, template_object, options)
  25203. end
  25204. def render
  25205. select_content_tag(
  25206. time_zone_options_for_select(value(@object) || @options[:default], @priority_zones, @options[:model] || ActiveSupport::TimeZone), @options, @html_options
  25207. )
  25208. end
  25209. end
  25210. end
  25211. end
  25212. end
  25213. module ActionView
  25214. module Helpers
  25215. module Tags
  25216. class UrlField < TextField #:nodoc:
  25217. end
  25218. end
  25219. end
  25220. end
  25221. module ActionView
  25222. module Helpers
  25223. module Tags
  25224. class WeekField < DatetimeField #:nodoc:
  25225. private
  25226. def format_date(value)
  25227. value.try(:strftime, "%Y-W%W")
  25228. end
  25229. end
  25230. end
  25231. end
  25232. end
  25233. module ActionView
  25234. module Helpers
  25235. module Tags #:nodoc:
  25236. extend ActiveSupport::Autoload
  25237. autoload :Base
  25238. autoload :CheckBox
  25239. autoload :CollectionCheckBoxes
  25240. autoload :CollectionRadioButtons
  25241. autoload :CollectionSelect
  25242. autoload :ColorField
  25243. autoload :DateField
  25244. autoload :DateSelect
  25245. autoload :DatetimeField
  25246. autoload :DatetimeLocalField
  25247. autoload :DatetimeSelect
  25248. autoload :EmailField
  25249. autoload :FileField
  25250. autoload :GroupedCollectionSelect
  25251. autoload :HiddenField
  25252. autoload :Label
  25253. autoload :MonthField
  25254. autoload :NumberField
  25255. autoload :PasswordField
  25256. autoload :RadioButton
  25257. autoload :RangeField
  25258. autoload :SearchField
  25259. autoload :Select
  25260. autoload :TelField
  25261. autoload :TextArea
  25262. autoload :TextField
  25263. autoload :TimeField
  25264. autoload :TimeSelect
  25265. autoload :TimeZoneSelect
  25266. autoload :UrlField
  25267. autoload :WeekField
  25268. end
  25269. end
  25270. end
  25271. require 'active_support/core_ext/string/filters'
  25272. require 'active_support/core_ext/array/extract_options'
  25273. module ActionView
  25274. # = Action View Text Helpers
  25275. module Helpers #:nodoc:
  25276. # The TextHelper module provides a set of methods for filtering, formatting
  25277. # and transforming strings, which can reduce the amount of inline Ruby code in
  25278. # your views. These helper methods extend Action View making them callable
  25279. # within your template files.
  25280. #
  25281. # ==== Sanitization
  25282. #
  25283. # Most text helpers by default sanitize the given content, but do not escape it.
  25284. # This means HTML tags will appear in the page but all malicious code will be removed.
  25285. # Let's look at some examples using the +simple_format+ method:
  25286. #
  25287. # simple_format('<a href="http://example.com/">Example</a>')
  25288. # # => "<p><a href=\"http://example.com/\">Example</a></p>"
  25289. #
  25290. # simple_format('<a href="javascript:alert(\'no!\')">Example</a>')
  25291. # # => "<p><a>Example</a></p>"
  25292. #
  25293. # If you want to escape all content, you should invoke the +h+ method before
  25294. # calling the text helper.
  25295. #
  25296. # simple_format h('<a href="http://example.com/">Example</a>')
  25297. # # => "<p>&lt;a href=\"http://example.com/\"&gt;Example&lt;/a&gt;</p>"
  25298. module TextHelper
  25299. extend ActiveSupport::Concern
  25300. include SanitizeHelper
  25301. include TagHelper
  25302. # The preferred method of outputting text in your views is to use the
  25303. # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
  25304. # do not operate as expected in an eRuby code block. If you absolutely must
  25305. # output text within a non-output code block (i.e., <% %>), you can use the concat method.
  25306. #
  25307. # <%
  25308. # concat "hello"
  25309. # # is the equivalent of <%= "hello" %>
  25310. #
  25311. # if logged_in
  25312. # concat "Logged in!"
  25313. # else
  25314. # concat link_to('login', action: :login)
  25315. # end
  25316. # # will either display "Logged in!" or a login link
  25317. # %>
  25318. def concat(string)
  25319. output_buffer << string
  25320. end
  25321. def safe_concat(string)
  25322. output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string)
  25323. end
  25324. # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
  25325. # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
  25326. # for a total length not exceeding <tt>:length</tt>.
  25327. #
  25328. # Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
  25329. #
  25330. # Pass a block if you want to show extra content when the text is truncated.
  25331. #
  25332. # The result is marked as HTML-safe, but it is escaped by default, unless <tt>:escape</tt> is
  25333. # +false+. Care should be taken if +text+ contains HTML tags or entities, because truncation
  25334. # may produce invalid HTML (such as unbalanced or incomplete tags).
  25335. #
  25336. # truncate("Once upon a time in a world far far away")
  25337. # # => "Once upon a time in a world..."
  25338. #
  25339. # truncate("Once upon a time in a world far far away", length: 17)
  25340. # # => "Once upon a ti..."
  25341. #
  25342. # truncate("Once upon a time in a world far far away", length: 17, separator: ' ')
  25343. # # => "Once upon a..."
  25344. #
  25345. # truncate("And they found that many people were sleeping better.", length: 25, omission: '... (continued)')
  25346. # # => "And they f... (continued)"
  25347. #
  25348. # truncate("<p>Once upon a time in a world far far away</p>")
  25349. # # => "<p>Once upon a time in a wo..."
  25350. #
  25351. # truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
  25352. # # => "Once upon a time in a wo...<a href="#">Continue</a>"
  25353. def truncate(text, options = {}, &block)
  25354. if text
  25355. length = options.fetch(:length, 30)
  25356. content = text.truncate(length, options)
  25357. content = options[:escape] == false ? content.html_safe : ERB::Util.html_escape(content)
  25358. content << capture(&block) if block_given? && text.length > length
  25359. content
  25360. end
  25361. end
  25362. # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
  25363. # a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
  25364. # as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
  25365. # '<mark>\1</mark>')
  25366. #
  25367. # highlight('You searched for: rails', 'rails')
  25368. # # => You searched for: <mark>rails</mark>
  25369. #
  25370. # highlight('You searched for: ruby, rails, dhh', 'actionpack')
  25371. # # => You searched for: ruby, rails, dhh
  25372. #
  25373. # highlight('You searched for: rails', ['for', 'rails'], highlighter: '<em>\1</em>')
  25374. # # => You searched <em>for</em>: <em>rails</em>
  25375. #
  25376. # highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
  25377. # # => You searched for: <a href="search?q=rails">rails</a>
  25378. def highlight(text, phrases, options = {})
  25379. text = sanitize(text) if options.fetch(:sanitize, true)
  25380. if text.blank? || phrases.blank?
  25381. text
  25382. else
  25383. highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
  25384. match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
  25385. text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
  25386. end.html_safe
  25387. end
  25388. # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
  25389. # The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
  25390. # defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
  25391. # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The
  25392. # <tt>:separator</tt> enable to choose the delimation. The resulting string will be stripped in any case. If the +phrase+
  25393. # isn't found, nil is returned.
  25394. #
  25395. # excerpt('This is an example', 'an', radius: 5)
  25396. # # => ...s is an exam...
  25397. #
  25398. # excerpt('This is an example', 'is', radius: 5)
  25399. # # => This is a...
  25400. #
  25401. # excerpt('This is an example', 'is')
  25402. # # => This is an example
  25403. #
  25404. # excerpt('This next thing is an example', 'ex', radius: 2)
  25405. # # => ...next...
  25406. #
  25407. # excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
  25408. # # => <chop> is also an example
  25409. #
  25410. # excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
  25411. # # => ...a very beautiful...
  25412. def excerpt(text, phrase, options = {})
  25413. return unless text && phrase
  25414. separator = options.fetch(:separator, "")
  25415. phrase = Regexp.escape(phrase)
  25416. regex = /#{phrase}/i
  25417. return unless matches = text.match(regex)
  25418. phrase = matches[0]
  25419. text.split(separator).each do |value|
  25420. if value.match(regex)
  25421. regex = phrase = value
  25422. break
  25423. end
  25424. end
  25425. first_part, second_part = text.split(regex, 2)
  25426. prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
  25427. postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
  25428. prefix + (first_part + separator + phrase + separator + second_part).strip + postfix
  25429. end
  25430. # Attempts to pluralize the +singular+ word unless +count+ is 1. If
  25431. # +plural+ is supplied, it will use that when count is > 1, otherwise
  25432. # it will use the Inflector to determine the plural form.
  25433. #
  25434. # pluralize(1, 'person')
  25435. # # => 1 person
  25436. #
  25437. # pluralize(2, 'person')
  25438. # # => 2 people
  25439. #
  25440. # pluralize(3, 'person', 'users')
  25441. # # => 3 users
  25442. #
  25443. # pluralize(0, 'person')
  25444. # # => 0 people
  25445. def pluralize(count, singular, plural = nil)
  25446. word = if (count == 1 || count =~ /^1(\.0+)?$/)
  25447. singular
  25448. else
  25449. plural || singular.pluralize
  25450. end
  25451. "#{count || 0} #{word}"
  25452. end
  25453. # Wraps the +text+ into lines no longer than +line_width+ width. This method
  25454. # breaks on the first whitespace character that does not exceed +line_width+
  25455. # (which is 80 by default).
  25456. #
  25457. # word_wrap('Once upon a time')
  25458. # # => Once upon a time
  25459. #
  25460. # word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
  25461. # # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined...
  25462. #
  25463. # word_wrap('Once upon a time', line_width: 8)
  25464. # # => Once\nupon a\ntime
  25465. #
  25466. # word_wrap('Once upon a time', line_width: 1)
  25467. # # => Once\nupon\na\ntime
  25468. def word_wrap(text, options = {})
  25469. line_width = options.fetch(:line_width, 80)
  25470. text.split("\n").collect do |line|
  25471. line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
  25472. end * "\n"
  25473. end
  25474. # Returns +text+ transformed into HTML using simple formatting rules.
  25475. # Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
  25476. # paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
  25477. # considered as a linebreak and a <tt><br /></tt> tag is appended. This
  25478. # method does not remove the newlines from the +text+.
  25479. #
  25480. # You can pass any HTML attributes into <tt>html_options</tt>. These
  25481. # will be added to all created paragraphs.
  25482. #
  25483. # ==== Options
  25484. # * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
  25485. # * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
  25486. #
  25487. # ==== Examples
  25488. # my_text = "Here is some basic text...\n...with a line break."
  25489. #
  25490. # simple_format(my_text)
  25491. # # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
  25492. #
  25493. # simple_format(my_text, {}, wrapper_tag: "div")
  25494. # # => "<div>Here is some basic text...\n<br />...with a line break.</div>"
  25495. #
  25496. # more_text = "We want to put a paragraph...\n\n...right there."
  25497. #
  25498. # simple_format(more_text)
  25499. # # => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
  25500. #
  25501. # simple_format("Look ma! A class!", class: 'description')
  25502. # # => "<p class='description'>Look ma! A class!</p>"
  25503. #
  25504. # simple_format("<span>I'm allowed!</span> It's true.", {}, sanitize: false)
  25505. # # => "<p><span>I'm allowed!</span> It's true.</p>"
  25506. def simple_format(text, html_options = {}, options = {})
  25507. wrapper_tag = options.fetch(:wrapper_tag, :p)
  25508. text = sanitize(text) if options.fetch(:sanitize, true)
  25509. paragraphs = split_paragraphs(text)
  25510. if paragraphs.empty?
  25511. content_tag(wrapper_tag, nil, html_options)
  25512. else
  25513. paragraphs.map { |paragraph|
  25514. content_tag(wrapper_tag, paragraph, html_options, options[:sanitize])
  25515. }.join("\n\n").html_safe
  25516. end
  25517. end
  25518. # Creates a Cycle object whose _to_s_ method cycles through elements of an
  25519. # array every time it is called. This can be used for example, to alternate
  25520. # classes for table rows. You can use named cycles to allow nesting in loops.
  25521. # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
  25522. # named cycle. The default name for a cycle without a +:name+ key is
  25523. # <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle
  25524. # and passing the name of the cycle. The current cycle string can be obtained
  25525. # anytime using the current_cycle method.
  25526. #
  25527. # # Alternate CSS classes for even and odd numbers...
  25528. # @items = [1,2,3,4]
  25529. # <table>
  25530. # <% @items.each do |item| %>
  25531. # <tr class="<%= cycle("odd", "even") -%>">
  25532. # <td>item</td>
  25533. # </tr>
  25534. # <% end %>
  25535. # </table>
  25536. #
  25537. #
  25538. # # Cycle CSS classes for rows, and text colors for values within each row
  25539. # @items = x = [{first: 'Robert', middle: 'Daniel', last: 'James'},
  25540. # {first: 'Emily', middle: 'Shannon', maiden: 'Pike', last: 'Hicks'},
  25541. # {first: 'June', middle: 'Dae', last: 'Jones'}]
  25542. # <% @items.each do |item| %>
  25543. # <tr class="<%= cycle("odd", "even", name: "row_class") -%>">
  25544. # <td>
  25545. # <% item.values.each do |value| %>
  25546. # <%# Create a named cycle "colors" %>
  25547. # <span style="color:<%= cycle("red", "green", "blue", name: "colors") -%>">
  25548. # <%= value %>
  25549. # </span>
  25550. # <% end %>
  25551. # <% reset_cycle("colors") %>
  25552. # </td>
  25553. # </tr>
  25554. # <% end %>
  25555. def cycle(first_value, *values)
  25556. options = values.extract_options!
  25557. name = options.fetch(:name, 'default')
  25558. values.unshift(first_value)
  25559. cycle = get_cycle(name)
  25560. unless cycle && cycle.values == values
  25561. cycle = set_cycle(name, Cycle.new(*values))
  25562. end
  25563. cycle.to_s
  25564. end
  25565. # Returns the current cycle string after a cycle has been started. Useful
  25566. # for complex table highlighting or any other design need which requires
  25567. # the current cycle string in more than one place.
  25568. #
  25569. # # Alternate background colors
  25570. # @items = [1,2,3,4]
  25571. # <% @items.each do |item| %>
  25572. # <div style="background-color:<%= cycle("red","white","blue") %>">
  25573. # <span style="background-color:<%= current_cycle %>"><%= item %></span>
  25574. # </div>
  25575. # <% end %>
  25576. def current_cycle(name = "default")
  25577. cycle = get_cycle(name)
  25578. cycle.current_value if cycle
  25579. end
  25580. # Resets a cycle so that it starts from the first element the next time
  25581. # it is called. Pass in +name+ to reset a named cycle.
  25582. #
  25583. # # Alternate CSS classes for even and odd numbers...
  25584. # @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
  25585. # <table>
  25586. # <% @items.each do |item| %>
  25587. # <tr class="<%= cycle("even", "odd") -%>">
  25588. # <% item.each do |value| %>
  25589. # <span style="color:<%= cycle("#333", "#666", "#999", name: "colors") -%>">
  25590. # <%= value %>
  25591. # </span>
  25592. # <% end %>
  25593. #
  25594. # <% reset_cycle("colors") %>
  25595. # </tr>
  25596. # <% end %>
  25597. # </table>
  25598. def reset_cycle(name = "default")
  25599. cycle = get_cycle(name)
  25600. cycle.reset if cycle
  25601. end
  25602. class Cycle #:nodoc:
  25603. attr_reader :values
  25604. def initialize(first_value, *values)
  25605. @values = values.unshift(first_value)
  25606. reset
  25607. end
  25608. def reset
  25609. @index = 0
  25610. end
  25611. def current_value
  25612. @values[previous_index].to_s
  25613. end
  25614. def to_s
  25615. value = @values[@index].to_s
  25616. @index = next_index
  25617. return value
  25618. end
  25619. private
  25620. def next_index
  25621. step_index(1)
  25622. end
  25623. def previous_index
  25624. step_index(-1)
  25625. end
  25626. def step_index(n)
  25627. (@index + n) % @values.size
  25628. end
  25629. end
  25630. private
  25631. # The cycle helpers need to store the cycles in a place that is
  25632. # guaranteed to be reset every time a page is rendered, so it
  25633. # uses an instance variable of ActionView::Base.
  25634. def get_cycle(name)
  25635. @_cycles = Hash.new unless defined?(@_cycles)
  25636. return @_cycles[name]
  25637. end
  25638. def set_cycle(name, cycle_object)
  25639. @_cycles = Hash.new unless defined?(@_cycles)
  25640. @_cycles[name] = cycle_object
  25641. end
  25642. def split_paragraphs(text)
  25643. return [] if text.blank?
  25644. text.to_str.gsub(/\r\n?/, "\n").split(/\n\n+/).map! do |t|
  25645. t.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') || t
  25646. end
  25647. end
  25648. def cut_excerpt_part(part_position, part, separator, options)
  25649. return "", "" unless part
  25650. radius = options.fetch(:radius, 100)
  25651. omission = options.fetch(:omission, "...")
  25652. part = part.split(separator)
  25653. part.delete("")
  25654. affix = part.size > radius ? omission : ""
  25655. part = if part_position == :first
  25656. drop_index = [part.length - radius, 0].max
  25657. part.drop(drop_index)
  25658. else
  25659. part.first(radius)
  25660. end
  25661. return affix, part.join(separator)
  25662. end
  25663. end
  25664. end
  25665. end
  25666. require 'action_view/helpers/tag_helper'
  25667. require 'i18n/exceptions'
  25668. module I18n
  25669. class ExceptionHandler
  25670. include Module.new {
  25671. def call(exception, locale, key, options)
  25672. exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super
  25673. end
  25674. }
  25675. end
  25676. end
  25677. module ActionView
  25678. # = Action View Translation Helpers
  25679. module Helpers
  25680. module TranslationHelper
  25681. # Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
  25682. #
  25683. # First, it'll pass the <tt>rescue_format: :html</tt> option to I18n so that any
  25684. # thrown +MissingTranslation+ messages will be turned into inline spans that
  25685. #
  25686. # * have a "translation-missing" class set,
  25687. # * contain the missing key as a title attribute and
  25688. # * a titleized version of the last key segment as a text.
  25689. #
  25690. # E.g. the value returned for a missing translation key :"blog.post.title" will be
  25691. # <span class="translation_missing" title="translation missing: en.blog.post.title">Title</span>.
  25692. # This way your views will display rather reasonable strings but it will still
  25693. # be easy to spot missing translations.
  25694. #
  25695. # Second, it'll scope the key by the current partial if the key starts
  25696. # with a period. So if you call <tt>translate(".foo")</tt> from the
  25697. # <tt>people/index.html.erb</tt> template, you'll actually be calling
  25698. # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
  25699. # to translate many keys within the same partials and gives you a simple framework
  25700. # for scoping them consistently. If you don't prepend the key with a period,
  25701. # nothing is converted.
  25702. #
  25703. # Third, it'll mark the translation as safe HTML if the key has the suffix
  25704. # "_html" or the last element of the key is the word "html". For example,
  25705. # calling translate("footer_html") or translate("footer.html") will return
  25706. # a safe HTML string that won't be escaped by other HTML helper methods. This
  25707. # naming convention helps to identify translations that include HTML tags so that
  25708. # you know what kind of output to expect when you call translate in a template.
  25709. def translate(key, options = {})
  25710. options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
  25711. options[:default] = wrap_translate_defaults(options[:default]) if options[:default]
  25712. if html_safe_translation_key?(key)
  25713. html_safe_options = options.dup
  25714. options.except(*I18n::RESERVED_KEYS).each do |name, value|
  25715. unless name == :count && value.is_a?(Numeric)
  25716. html_safe_options[name] = ERB::Util.html_escape(value.to_s)
  25717. end
  25718. end
  25719. translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
  25720. translation.respond_to?(:html_safe) ? translation.html_safe : translation
  25721. else
  25722. I18n.translate(scope_key_by_partial(key), options)
  25723. end
  25724. end
  25725. alias :t :translate
  25726. # Delegates to <tt>I18n.localize</tt> with no additional functionality.
  25727. #
  25728. # See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
  25729. # for more information.
  25730. def localize(*args)
  25731. I18n.localize(*args)
  25732. end
  25733. alias :l :localize
  25734. private
  25735. def scope_key_by_partial(key)
  25736. if key.to_s.first == "."
  25737. if @virtual_path
  25738. @virtual_path.gsub(%r{/_?}, ".") + key.to_s
  25739. else
  25740. raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
  25741. end
  25742. else
  25743. key
  25744. end
  25745. end
  25746. def html_safe_translation_key?(key)
  25747. key.to_s =~ /(\b|_|\.)html$/
  25748. end
  25749. def wrap_translate_defaults(defaults)
  25750. new_defaults = []
  25751. defaults = Array(defaults)
  25752. while key = defaults.shift
  25753. if key.is_a?(Symbol)
  25754. new_defaults << lambda { |_, options| translate key, options.merge(:default => defaults) }
  25755. break
  25756. else
  25757. new_defaults << key
  25758. end
  25759. end
  25760. new_defaults
  25761. end
  25762. end
  25763. end
  25764. end
  25765. require 'action_view/helpers/javascript_helper'
  25766. require 'active_support/core_ext/array/access'
  25767. require 'active_support/core_ext/hash/keys'
  25768. require 'active_support/core_ext/string/output_safety'
  25769. module ActionView
  25770. # = Action View URL Helpers
  25771. module Helpers #:nodoc:
  25772. # Provides a set of methods for making links and getting URLs that
  25773. # depend on the routing subsystem (see ActionDispatch::Routing).
  25774. # This allows you to use the same format for links in views
  25775. # and controllers.
  25776. module UrlHelper
  25777. # This helper may be included in any class that includes the
  25778. # URL helpers of a routes (routes.url_helpers). Some methods
  25779. # provided here will only work in the context of a request
  25780. # (link_to_unless_current, for instance), which must be provided
  25781. # as a method called #request on the context.
  25782. extend ActiveSupport::Concern
  25783. include TagHelper
  25784. module ClassMethods
  25785. def _url_for_modules
  25786. ActionView::RoutingUrlFor
  25787. end
  25788. end
  25789. # Basic implementation of url_for to allow use helpers without routes existence
  25790. def url_for(options = nil) # :nodoc:
  25791. case options
  25792. when String
  25793. options
  25794. when :back
  25795. _back_url
  25796. else
  25797. raise ArgumentError, "arguments passed to url_for can't be handled. Please require " +
  25798. "routes or provide your own implementation"
  25799. end
  25800. end
  25801. def _back_url # :nodoc:
  25802. referrer = controller.respond_to?(:request) && controller.request.env["HTTP_REFERER"]
  25803. referrer || 'javascript:history.back()'
  25804. end
  25805. protected :_back_url
  25806. # Creates a link tag of the given +name+ using a URL created by the set of +options+.
  25807. # See the valid options in the documentation for +url_for+. It's also possible to
  25808. # pass a String instead of an options hash, which generates a link tag that uses the
  25809. # value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead
  25810. # of an options hash will generate a link to the referrer (a JavaScript back link
  25811. # will be used in place of a referrer if none exists). If +nil+ is passed as the name
  25812. # the value of the link itself will become the name.
  25813. #
  25814. # ==== Signatures
  25815. #
  25816. # link_to(body, url, html_options = {})
  25817. # # url is a String; you can use URL helpers like
  25818. # # posts_path
  25819. #
  25820. # link_to(body, url_options = {}, html_options = {})
  25821. # # url_options, except :method, is passed to url_for
  25822. #
  25823. # link_to(options = {}, html_options = {}) do
  25824. # # name
  25825. # end
  25826. #
  25827. # link_to(url, html_options = {}) do
  25828. # # name
  25829. # end
  25830. #
  25831. # ==== Options
  25832. # * <tt>:data</tt> - This option can be used to add custom data attributes.
  25833. # * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
  25834. # create an HTML form and immediately submit the form for processing using
  25835. # the HTTP verb specified. Useful for having links perform a POST operation
  25836. # in dangerous actions like deleting a record (which search bots can follow
  25837. # while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
  25838. # Note that if the user has JavaScript disabled, the request will fall back
  25839. # to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
  25840. # disabled clicking the link will have no effect. If you are relying on the
  25841. # POST behavior, you should check for it in your controller's action by using
  25842. # the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>:patch</tt>, or <tt>put?</tt>.
  25843. # * <tt>remote: true</tt> - This will allow the unobtrusive JavaScript
  25844. # driver to make an Ajax request to the URL in question instead of following
  25845. # the link. The drivers each provide mechanisms for listening for the
  25846. # completion of the Ajax request and performing JavaScript operations once
  25847. # they're complete
  25848. #
  25849. # ==== Data attributes
  25850. #
  25851. # * <tt>confirm: 'question?'</tt> - This will allow the unobtrusive JavaScript
  25852. # driver to prompt with the question specified. If the user accepts, the link is
  25853. # processed normally, otherwise no action is taken.
  25854. # * <tt>:disable_with</tt> - Value of this parameter will be
  25855. # used as the value for a disabled version of the submit
  25856. # button when the form is submitted. This feature is provided
  25857. # by the unobtrusive JavaScript driver.
  25858. #
  25859. # ==== Examples
  25860. # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
  25861. # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
  25862. # your application on resources and use
  25863. #
  25864. # link_to "Profile", profile_path(@profile)
  25865. # # => <a href="/profiles/1">Profile</a>
  25866. #
  25867. # or the even pithier
  25868. #
  25869. # link_to "Profile", @profile
  25870. # # => <a href="/profiles/1">Profile</a>
  25871. #
  25872. # in place of the older more verbose, non-resource-oriented
  25873. #
  25874. # link_to "Profile", controller: "profiles", action: "show", id: @profile
  25875. # # => <a href="/profiles/show/1">Profile</a>
  25876. #
  25877. # Similarly,
  25878. #
  25879. # link_to "Profiles", profiles_path
  25880. # # => <a href="/profiles">Profiles</a>
  25881. #
  25882. # is better than
  25883. #
  25884. # link_to "Profiles", controller: "profiles"
  25885. # # => <a href="/profiles">Profiles</a>
  25886. #
  25887. # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
  25888. #
  25889. # <%= link_to(@profile) do %>
  25890. # <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
  25891. # <% end %>
  25892. # # => <a href="/profiles/1">
  25893. # <strong>David</strong> -- <span>Check it out!</span>
  25894. # </a>
  25895. #
  25896. # Classes and ids for CSS are easy to produce:
  25897. #
  25898. # link_to "Articles", articles_path, id: "news", class: "article"
  25899. # # => <a href="/articles" class="article" id="news">Articles</a>
  25900. #
  25901. # Be careful when using the older argument style, as an extra literal hash is needed:
  25902. #
  25903. # link_to "Articles", { controller: "articles" }, id: "news", class: "article"
  25904. # # => <a href="/articles" class="article" id="news">Articles</a>
  25905. #
  25906. # Leaving the hash off gives the wrong link:
  25907. #
  25908. # link_to "WRONG!", controller: "articles", id: "news", class: "article"
  25909. # # => <a href="/articles/index/news?class=article">WRONG!</a>
  25910. #
  25911. # +link_to+ can also produce links with anchors or query strings:
  25912. #
  25913. # link_to "Comment wall", profile_path(@profile, anchor: "wall")
  25914. # # => <a href="/profiles/1#wall">Comment wall</a>
  25915. #
  25916. # link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
  25917. # # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
  25918. #
  25919. # link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
  25920. # # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
  25921. #
  25922. # The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
  25923. #
  25924. # link_to("Destroy", "http://www.example.com", method: :delete)
  25925. # # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
  25926. #
  25927. # You can also use custom data attributes using the <tt>:data</tt> option:
  25928. #
  25929. # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
  25930. # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
  25931. def link_to(name = nil, options = nil, html_options = nil, &block)
  25932. html_options, options = options, name if block_given?
  25933. options ||= {}
  25934. html_options = convert_options_to_data_attributes(options, html_options)
  25935. url = url_for(options)
  25936. html_options['href'] ||= url
  25937. content_tag(:a, name || url, html_options, &block)
  25938. end
  25939. # Generates a form containing a single button that submits to the URL created
  25940. # by the set of +options+. This is the safest method to ensure links that
  25941. # cause changes to your data are not triggered by search bots or accelerators.
  25942. # If the HTML button does not work with your layout, you can also consider
  25943. # using the +link_to+ method with the <tt>:method</tt> modifier as described in
  25944. # the +link_to+ documentation.
  25945. #
  25946. # By default, the generated form element has a class name of <tt>button_to</tt>
  25947. # to allow styling of the form itself and its children. This can be changed
  25948. # using the <tt>:form_class</tt> modifier within +html_options+. You can control
  25949. # the form submission and input element behavior using +html_options+.
  25950. # This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
  25951. # If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
  25952. # You can also disable the button by passing <tt>disabled: true</tt> in +html_options+.
  25953. # If you are using RESTful routes, you can pass the <tt>:method</tt>
  25954. # to change the HTTP verb used to submit the form.
  25955. #
  25956. # ==== Options
  25957. # The +options+ hash accepts the same options as +url_for+.
  25958. #
  25959. # There are a few special +html_options+:
  25960. # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
  25961. # <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
  25962. # * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
  25963. # * <tt>:data</tt> - This option can be used to add custom data attributes.
  25964. # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
  25965. # submit behavior. By default this behavior is an ajax submit.
  25966. # * <tt>:form</tt> - This hash will be form attributes
  25967. # * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
  25968. # be placed
  25969. #
  25970. # ==== Data attributes
  25971. #
  25972. # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
  25973. # prompt with the question specified. If the user accepts, the link is
  25974. # processed normally, otherwise no action is taken.
  25975. # * <tt>:disable_with</tt> - Value of this parameter will be
  25976. # used as the value for a disabled version of the submit
  25977. # button when the form is submitted. This feature is provided
  25978. # by the unobtrusive JavaScript driver.
  25979. #
  25980. # ==== Examples
  25981. # <%= button_to "New", action: "new" %>
  25982. # # => "<form method="post" action="/controller/new" class="button_to">
  25983. # # <div><input value="New" type="submit" /></div>
  25984. # # </form>"
  25985. #
  25986. # <%= button_to [:make_happy, @user] do %>
  25987. # Make happy <strong><%= @user.name %></strong>
  25988. # <% end %>
  25989. # # => "<form method="post" action="/users/1/make_happy" class="button_to">
  25990. # # <div>
  25991. # # <button type="submit">
  25992. # # Make happy <strong><%= @user.name %></strong>
  25993. # # </button>
  25994. # # </div>
  25995. # # </form>"
  25996. #
  25997. # <%= button_to "New", { action: "new" }, form_class: "new-thing" %>
  25998. # # => "<form method="post" action="/controller/new" class="new-thing">
  25999. # # <div><input value="New" type="submit" /></div>
  26000. # # </form>"
  26001. #
  26002. #
  26003. # <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
  26004. # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
  26005. # # <div>
  26006. # # <input value="Create" type="submit" />
  26007. # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
  26008. # # </div>
  26009. # # </form>"
  26010. #
  26011. #
  26012. # <%= button_to "Delete Image", { action: "delete", id: @image.id },
  26013. # method: :delete, data: { confirm: "Are you sure?" } %>
  26014. # # => "<form method="post" action="/images/delete/1" class="button_to">
  26015. # # <div>
  26016. # # <input type="hidden" name="_method" value="delete" />
  26017. # # <input data-confirm='Are you sure?' value="Delete Image" type="submit" />
  26018. # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
  26019. # # </div>
  26020. # # </form>"
  26021. #
  26022. #
  26023. # <%= button_to('Destroy', 'http://www.example.com',
  26024. # method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
  26025. # # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
  26026. # # <div>
  26027. # # <input name='_method' value='delete' type='hidden' />
  26028. # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
  26029. # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
  26030. # # </div>
  26031. # # </form>"
  26032. # #
  26033. def button_to(name = nil, options = nil, html_options = nil, &block)
  26034. html_options, options = options, name if block_given?
  26035. options ||= {}
  26036. html_options ||= {}
  26037. html_options = html_options.stringify_keys
  26038. convert_boolean_attributes!(html_options, %w(disabled))
  26039. url = options.is_a?(String) ? options : url_for(options)
  26040. remote = html_options.delete('remote')
  26041. method = html_options.delete('method').to_s
  26042. method_tag = %w{patch put delete}.include?(method) ? method_tag(method) : ''.html_safe
  26043. form_method = method == 'get' ? 'get' : 'post'
  26044. form_options = html_options.delete('form') || {}
  26045. form_options[:class] ||= html_options.delete('form_class') || 'button_to'
  26046. form_options.merge!(method: form_method, action: url)
  26047. form_options.merge!("data-remote" => "true") if remote
  26048. request_token_tag = form_method == 'post' ? token_tag : ''
  26049. html_options = convert_options_to_data_attributes(options, html_options)
  26050. html_options['type'] = 'submit'
  26051. button = if block_given?
  26052. content_tag('button', html_options, &block)
  26053. else
  26054. html_options['value'] = name || url
  26055. tag('input', html_options)
  26056. end
  26057. inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
  26058. content_tag('form', content_tag('div', inner_tags), form_options)
  26059. end
  26060. # Creates a link tag of the given +name+ using a URL created by the set of
  26061. # +options+ unless the current request URI is the same as the links, in
  26062. # which case only the name is returned (or the given block is yielded, if
  26063. # one exists). You can give +link_to_unless_current+ a block which will
  26064. # specialize the default behavior (e.g., show a "Start Here" link rather
  26065. # than the link's text).
  26066. #
  26067. # ==== Examples
  26068. # Let's say you have a navigation menu...
  26069. #
  26070. # <ul id="navbar">
  26071. # <li><%= link_to_unless_current("Home", { action: "index" }) %></li>
  26072. # <li><%= link_to_unless_current("About Us", { action: "about" }) %></li>
  26073. # </ul>
  26074. #
  26075. # If in the "about" action, it will render...
  26076. #
  26077. # <ul id="navbar">
  26078. # <li><a href="/controller/index">Home</a></li>
  26079. # <li>About Us</li>
  26080. # </ul>
  26081. #
  26082. # ...but if in the "index" action, it will render:
  26083. #
  26084. # <ul id="navbar">
  26085. # <li>Home</li>
  26086. # <li><a href="/controller/about">About Us</a></li>
  26087. # </ul>
  26088. #
  26089. # The implicit block given to +link_to_unless_current+ is evaluated if the current
  26090. # action is the action given. So, if we had a comments page and wanted to render a
  26091. # "Go Back" link instead of a link to the comments page, we could do something like this...
  26092. #
  26093. # <%=
  26094. # link_to_unless_current("Comment", { controller: "comments", action: "new" }) do
  26095. # link_to("Go back", { controller: "posts", action: "index" })
  26096. # end
  26097. # %>
  26098. def link_to_unless_current(name, options = {}, html_options = {}, &block)
  26099. link_to_unless current_page?(options), name, options, html_options, &block
  26100. end
  26101. # Creates a link tag of the given +name+ using a URL created by the set of
  26102. # +options+ unless +condition+ is true, in which case only the name is
  26103. # returned. To specialize the default behavior (i.e., show a login link rather
  26104. # than just the plaintext link text), you can pass a block that
  26105. # accepts the name or the full argument list for +link_to_unless+.
  26106. #
  26107. # ==== Examples
  26108. # <%= link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) %>
  26109. # # If the user is logged in...
  26110. # # => <a href="/controller/reply/">Reply</a>
  26111. #
  26112. # <%=
  26113. # link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) do |name|
  26114. # link_to(name, { controller: "accounts", action: "signup" })
  26115. # end
  26116. # %>
  26117. # # If the user is logged in...
  26118. # # => <a href="/controller/reply/">Reply</a>
  26119. # # If not...
  26120. # # => <a href="/accounts/signup">Reply</a>
  26121. def link_to_unless(condition, name, options = {}, html_options = {}, &block)
  26122. if condition
  26123. if block_given?
  26124. block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
  26125. else
  26126. name
  26127. end
  26128. else
  26129. link_to(name, options, html_options)
  26130. end
  26131. end
  26132. # Creates a link tag of the given +name+ using a URL created by the set of
  26133. # +options+ if +condition+ is true, otherwise only the name is
  26134. # returned. To specialize the default behavior, you can pass a block that
  26135. # accepts the name or the full argument list for +link_to_unless+ (see the examples
  26136. # in +link_to_unless+).
  26137. #
  26138. # ==== Examples
  26139. # <%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
  26140. # # If the user isn't logged in...
  26141. # # => <a href="/sessions/new/">Login</a>
  26142. #
  26143. # <%=
  26144. # link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) do
  26145. # link_to(@current_user.login, { controller: "accounts", action: "show", id: @current_user })
  26146. # end
  26147. # %>
  26148. # # If the user isn't logged in...
  26149. # # => <a href="/sessions/new/">Login</a>
  26150. # # If they are logged in...
  26151. # # => <a href="/accounts/show/3">my_username</a>
  26152. def link_to_if(condition, name, options = {}, html_options = {}, &block)
  26153. link_to_unless !condition, name, options, html_options, &block
  26154. end
  26155. # Creates a mailto link tag to the specified +email_address+, which is
  26156. # also used as the name of the link unless +name+ is specified. Additional
  26157. # HTML attributes for the link can be passed in +html_options+.
  26158. #
  26159. # +mail_to+ has several methods for customizing the email itself by
  26160. # passing special keys to +html_options+.
  26161. #
  26162. # ==== Options
  26163. # * <tt>:subject</tt> - Preset the subject line of the email.
  26164. # * <tt>:body</tt> - Preset the body of the email.
  26165. # * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
  26166. # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
  26167. #
  26168. # ==== Obfuscation
  26169. # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
  26170. # in order to hinder email harvesters. To take advantage of these options,
  26171. # install the +actionview-encoded_mail_to+ gem.
  26172. #
  26173. # ==== Examples
  26174. # mail_to "me@domain.com"
  26175. # # => <a href="mailto:me@domain.com">me@domain.com</a>
  26176. #
  26177. # mail_to "me@domain.com", "My email"
  26178. # # => <a href="mailto:me@domain.com">My email</a>
  26179. #
  26180. # mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
  26181. # subject: "This is an example email"
  26182. # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
  26183. def mail_to(email_address, name = nil, html_options = {})
  26184. email_address = ERB::Util.html_escape(email_address)
  26185. html_options.stringify_keys!
  26186. extras = %w{ cc bcc body subject }.map { |item|
  26187. option = html_options.delete(item) || next
  26188. "#{item}=#{Rack::Utils.escape_path(option)}"
  26189. }.compact
  26190. extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
  26191. content_tag "a", name || email_address.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
  26192. end
  26193. # True if the current request URI was generated by the given +options+.
  26194. #
  26195. # ==== Examples
  26196. # Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc</tt> action.
  26197. #
  26198. # current_page?(action: 'process')
  26199. # # => false
  26200. #
  26201. # current_page?(controller: 'shop', action: 'checkout')
  26202. # # => true
  26203. #
  26204. # current_page?(controller: 'shop', action: 'checkout', order: 'asc')
  26205. # # => false
  26206. #
  26207. # current_page?(action: 'checkout')
  26208. # # => true
  26209. #
  26210. # current_page?(controller: 'library', action: 'checkout')
  26211. # # => false
  26212. #
  26213. # current_page?('http://www.example.com/shop/checkout')
  26214. # # => true
  26215. #
  26216. # current_page?('/shop/checkout')
  26217. # # => true
  26218. #
  26219. # Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc&page=1</tt> action.
  26220. #
  26221. # current_page?(action: 'process')
  26222. # # => false
  26223. #
  26224. # current_page?(controller: 'shop', action: 'checkout')
  26225. # # => true
  26226. #
  26227. # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
  26228. # # => true
  26229. #
  26230. # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
  26231. # # => false
  26232. #
  26233. # current_page?(controller: 'shop', action: 'checkout', order: 'desc')
  26234. # # => false
  26235. #
  26236. # current_page?(action: 'checkout')
  26237. # # => true
  26238. #
  26239. # current_page?(controller: 'library', action: 'checkout')
  26240. # # => false
  26241. #
  26242. # Let's say we're in the <tt>http://www.example.com/products</tt> action with method POST in case of invalid product.
  26243. #
  26244. # current_page?(controller: 'product', action: 'index')
  26245. # # => false
  26246. #
  26247. def current_page?(options)
  26248. unless request
  26249. raise "You cannot use helpers that need to determine the current " \
  26250. "page unless your view context provides a Request object " \
  26251. "in a #request method"
  26252. end
  26253. return false unless request.get? || request.head?
  26254. url_string = url_for(options)
  26255. # We ignore any extra parameters in the request_uri if the
  26256. # submitted url doesn't have any either. This lets the function
  26257. # work with things like ?order=asc
  26258. request_uri = url_string.index("?") ? request.fullpath : request.path
  26259. if url_string =~ /^\w+:\/\//
  26260. url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
  26261. else
  26262. url_string == request_uri
  26263. end
  26264. end
  26265. private
  26266. def convert_options_to_data_attributes(options, html_options)
  26267. if html_options
  26268. html_options = html_options.stringify_keys
  26269. html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
  26270. disable_with = html_options.delete("disable_with")
  26271. confirm = html_options.delete('confirm')
  26272. method = html_options.delete('method')
  26273. if confirm
  26274. message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
  26275. "Use 'data: { confirm: \'Text\' }' instead."
  26276. ActiveSupport::Deprecation.warn message
  26277. html_options["data-confirm"] = confirm
  26278. end
  26279. add_method_to_attributes!(html_options, method) if method
  26280. if disable_with
  26281. message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \
  26282. "Use 'data: { disable_with: \'Text\' }' instead."
  26283. ActiveSupport::Deprecation.warn message
  26284. html_options["data-disable-with"] = disable_with
  26285. end
  26286. html_options
  26287. else
  26288. link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
  26289. end
  26290. end
  26291. def link_to_remote_options?(options)
  26292. if options.is_a?(Hash)
  26293. options.delete('remote') || options.delete(:remote)
  26294. end
  26295. end
  26296. def add_method_to_attributes!(html_options, method)
  26297. if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
  26298. html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
  26299. end
  26300. html_options["data-method"] = method
  26301. end
  26302. # Processes the +html_options+ hash, converting the boolean
  26303. # attributes from true/false form into the form required by
  26304. # HTML/XHTML. (An attribute is considered to be boolean if
  26305. # its name is listed in the given +bool_attrs+ array.)
  26306. #
  26307. # More specifically, for each boolean attribute in +html_options+
  26308. # given as:
  26309. #
  26310. # "attr" => bool_value
  26311. #
  26312. # if the associated +bool_value+ evaluates to true, it is
  26313. # replaced with the attribute's name; otherwise the attribute is
  26314. # removed from the +html_options+ hash. (See the XHTML 1.0 spec,
  26315. # section 4.5 "Attribute Minimization" for more:
  26316. # http://www.w3.org/TR/xhtml1/#h-4.5)
  26317. #
  26318. # Returns the updated +html_options+ hash, which is also modified
  26319. # in place.
  26320. #
  26321. # Example:
  26322. #
  26323. # convert_boolean_attributes!( html_options,
  26324. # %w( checked disabled readonly ) )
  26325. def convert_boolean_attributes!(html_options, bool_attrs)
  26326. bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
  26327. html_options
  26328. end
  26329. def token_tag(token=nil)
  26330. if token != false && protect_against_forgery?
  26331. token ||= form_authenticity_token
  26332. tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
  26333. else
  26334. ''
  26335. end
  26336. end
  26337. def method_tag(method)
  26338. tag('input', type: 'hidden', name: '_method', value: method.to_s)
  26339. end
  26340. end
  26341. end
  26342. end
  26343. require 'active_support/benchmarkable'
  26344. module ActionView #:nodoc:
  26345. module Helpers #:nodoc:
  26346. extend ActiveSupport::Autoload
  26347. autoload :ActiveModelHelper
  26348. autoload :AssetTagHelper
  26349. autoload :AssetUrlHelper
  26350. autoload :AtomFeedHelper
  26351. autoload :CacheHelper
  26352. autoload :CaptureHelper
  26353. autoload :ControllerHelper
  26354. autoload :CsrfHelper
  26355. autoload :DateHelper
  26356. autoload :DebugHelper
  26357. autoload :FormHelper
  26358. autoload :FormOptionsHelper
  26359. autoload :FormTagHelper
  26360. autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
  26361. autoload :NumberHelper
  26362. autoload :OutputSafetyHelper
  26363. autoload :RecordTagHelper
  26364. autoload :RenderingHelper
  26365. autoload :SanitizeHelper
  26366. autoload :TagHelper
  26367. autoload :TextHelper
  26368. autoload :TranslationHelper
  26369. autoload :UrlHelper
  26370. extend ActiveSupport::Concern
  26371. include ActiveSupport::Benchmarkable
  26372. include ActiveModelHelper
  26373. include AssetTagHelper
  26374. include AssetUrlHelper
  26375. include AtomFeedHelper
  26376. include CacheHelper
  26377. include CaptureHelper
  26378. include ControllerHelper
  26379. include CsrfHelper
  26380. include DateHelper
  26381. include DebugHelper
  26382. include FormHelper
  26383. include FormOptionsHelper
  26384. include FormTagHelper
  26385. include JavaScriptHelper
  26386. include NumberHelper
  26387. include OutputSafetyHelper
  26388. include RecordTagHelper
  26389. include RenderingHelper
  26390. include SanitizeHelper
  26391. include TagHelper
  26392. include TextHelper
  26393. include TranslationHelper
  26394. include UrlHelper
  26395. end
  26396. end
  26397. module ActionView
  26398. # = Action View Log Subscriber
  26399. #
  26400. # Provides functionality so that Rails can output logs from Action View.
  26401. class LogSubscriber < ActiveSupport::LogSubscriber
  26402. VIEWS_PATTERN = /^app\/views\//.freeze
  26403. def render_template(event)
  26404. return unless logger.info?
  26405. message = " Rendered #{from_rails_root(event.payload[:identifier])}"
  26406. message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
  26407. message << " (#{event.duration.round(1)}ms)"
  26408. info(message)
  26409. end
  26410. alias :render_partial :render_template
  26411. alias :render_collection :render_template
  26412. def logger
  26413. ActionView::Base.logger
  26414. end
  26415. protected
  26416. def from_rails_root(string)
  26417. string.sub("#{Rails.root}/", "").sub(VIEWS_PATTERN, "")
  26418. end
  26419. end
  26420. end
  26421. ActionView::LogSubscriber.attach_to :action_view
  26422. require 'thread_safe'
  26423. require 'active_support/core_ext/module/remove_method'
  26424. module ActionView
  26425. # = Action View Lookup Context
  26426. #
  26427. # LookupContext is the object responsible to hold all information required to lookup
  26428. # templates, i.e. view paths and details. The LookupContext is also responsible to
  26429. # generate a key, given to view paths, used in the resolver cache lookup. Since
  26430. # this key is generated just once during the request, it speeds up all cache accesses.
  26431. class LookupContext #:nodoc:
  26432. attr_accessor :prefixes, :rendered_format
  26433. mattr_accessor :fallbacks
  26434. @@fallbacks = FallbackFileSystemResolver.instances
  26435. mattr_accessor :registered_details
  26436. self.registered_details = []
  26437. def self.register_detail(name, options = {}, &block)
  26438. self.registered_details << name
  26439. initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
  26440. Accessors.send :define_method, :"default_#{name}", &block
  26441. Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
  26442. def #{name}
  26443. @details.fetch(:#{name}, [])
  26444. end
  26445. def #{name}=(value)
  26446. value = value.present? ? Array(value) : default_#{name}
  26447. _set_detail(:#{name}, value) if value != @details[:#{name}]
  26448. end
  26449. remove_possible_method :initialize_details
  26450. def initialize_details(details)
  26451. #{initialize.join("\n")}
  26452. end
  26453. METHOD
  26454. end
  26455. # Holds accessors for the registered details.
  26456. module Accessors #:nodoc:
  26457. end
  26458. register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq }
  26459. register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
  26460. register_detail(:handlers){ Template::Handlers.extensions }
  26461. class DetailsKey #:nodoc:
  26462. alias :eql? :equal?
  26463. alias :object_hash :hash
  26464. attr_reader :hash
  26465. @details_keys = ThreadSafe::Cache.new
  26466. def self.get(details)
  26467. @details_keys[details] ||= new
  26468. end
  26469. def self.clear
  26470. @details_keys.clear
  26471. end
  26472. def initialize
  26473. @hash = object_hash
  26474. end
  26475. end
  26476. # Add caching behavior on top of Details.
  26477. module DetailsCache
  26478. attr_accessor :cache
  26479. # Calculate the details key. Remove the handlers from calculation to improve performance
  26480. # since the user cannot modify it explicitly.
  26481. def details_key #:nodoc:
  26482. @details_key ||= DetailsKey.get(@details) if @cache
  26483. end
  26484. # Temporary skip passing the details_key forward.
  26485. def disable_cache
  26486. old_value, @cache = @cache, false
  26487. yield
  26488. ensure
  26489. @cache = old_value
  26490. end
  26491. protected
  26492. def _set_detail(key, value)
  26493. @details = @details.dup if @details_key
  26494. @details_key = nil
  26495. @details[key] = value
  26496. end
  26497. end
  26498. # Helpers related to template lookup using the lookup context information.
  26499. module ViewPaths
  26500. attr_reader :view_paths, :html_fallback_for_js
  26501. # Whenever setting view paths, makes a copy so we can manipulate then in
  26502. # instance objects as we wish.
  26503. def view_paths=(paths)
  26504. @view_paths = ActionView::PathSet.new(Array(paths))
  26505. end
  26506. def find(name, prefixes = [], partial = false, keys = [], options = {})
  26507. @view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
  26508. end
  26509. alias :find_template :find
  26510. def find_all(name, prefixes = [], partial = false, keys = [], options = {})
  26511. @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
  26512. end
  26513. def exists?(name, prefixes = [], partial = false, keys = [], options = {})
  26514. @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
  26515. end
  26516. alias :template_exists? :exists?
  26517. # Add fallbacks to the view paths. Useful in cases you are rendering a :file.
  26518. def with_fallbacks
  26519. added_resolvers = 0
  26520. self.class.fallbacks.each do |resolver|
  26521. next if view_paths.include?(resolver)
  26522. view_paths.push(resolver)
  26523. added_resolvers += 1
  26524. end
  26525. yield
  26526. ensure
  26527. added_resolvers.times { view_paths.pop }
  26528. end
  26529. protected
  26530. def args_for_lookup(name, prefixes, partial, keys, details_options) #:nodoc:
  26531. name, prefixes = normalize_name(name, prefixes)
  26532. details, details_key = detail_args_for(details_options)
  26533. [name, prefixes, partial || false, details, details_key, keys]
  26534. end
  26535. # Compute details hash and key according to user options (e.g. passed from #render).
  26536. def detail_args_for(options)
  26537. return @details, details_key if options.empty? # most common path.
  26538. user_details = @details.merge(options)
  26539. [user_details, DetailsKey.get(user_details)]
  26540. end
  26541. # Support legacy foo.erb names even though we now ignore .erb
  26542. # as well as incorrectly putting part of the path in the template
  26543. # name instead of the prefix.
  26544. def normalize_name(name, prefixes) #:nodoc:
  26545. prefixes = prefixes.presence
  26546. parts = name.to_s.split('/')
  26547. parts.shift if parts.first.empty?
  26548. name = parts.pop
  26549. return name, prefixes || [""] if parts.empty?
  26550. parts = parts.join('/')
  26551. prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
  26552. return name, prefixes
  26553. end
  26554. end
  26555. include Accessors
  26556. include DetailsCache
  26557. include ViewPaths
  26558. def initialize(view_paths, details = {}, prefixes = [])
  26559. @details, @details_key = {}, nil
  26560. @skip_default_locale = false
  26561. @cache = true
  26562. @prefixes = prefixes
  26563. @rendered_format = nil
  26564. self.view_paths = view_paths
  26565. initialize_details(details)
  26566. end
  26567. # Override formats= to expand ["*/*"] values and automatically
  26568. # add :html as fallback to :js.
  26569. def formats=(values)
  26570. if values
  26571. values.concat(default_formats) if values.delete "*/*"
  26572. if values == [:js]
  26573. values << :html
  26574. @html_fallback_for_js = true
  26575. end
  26576. end
  26577. super(values)
  26578. end
  26579. # Do not use the default locale on template lookup.
  26580. def skip_default_locale!
  26581. @skip_default_locale = true
  26582. self.locale = nil
  26583. end
  26584. # Override locale to return a symbol instead of array.
  26585. def locale
  26586. @details[:locale].first
  26587. end
  26588. # Overload locale= to also set the I18n.locale. If the current I18n.config object responds
  26589. # to original_config, it means that it's has a copy of the original I18n configuration and it's
  26590. # acting as proxy, which we need to skip.
  26591. def locale=(value)
  26592. if value
  26593. config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config
  26594. config.locale = value
  26595. end
  26596. super(@skip_default_locale ? I18n.locale : default_locale)
  26597. end
  26598. # A method which only uses the first format in the formats array for layout lookup.
  26599. def with_layout_format
  26600. if formats.size == 1
  26601. yield
  26602. else
  26603. old_formats = formats
  26604. _set_detail(:formats, formats[0,1])
  26605. begin
  26606. yield
  26607. ensure
  26608. _set_detail(:formats, old_formats)
  26609. end
  26610. end
  26611. end
  26612. end
  26613. end
  26614. module ActionView
  26615. module ModelNaming
  26616. # Converts the given object to an ActiveModel compliant one.
  26617. def convert_to_model(object)
  26618. object.respond_to?(:to_model) ? object.to_model : object
  26619. end
  26620. def model_name_from_record_or_class(record_or_class)
  26621. (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
  26622. end
  26623. end
  26624. end
  26625. module ActionView #:nodoc:
  26626. # = Action View PathSet
  26627. class PathSet #:nodoc:
  26628. include Enumerable
  26629. attr_reader :paths
  26630. delegate :[], :include?, :pop, :size, :each, to: :paths
  26631. def initialize(paths = [])
  26632. @paths = typecast paths
  26633. end
  26634. def initialize_copy(other)
  26635. @paths = other.paths.dup
  26636. self
  26637. end
  26638. def to_ary
  26639. paths.dup
  26640. end
  26641. def compact
  26642. PathSet.new paths.compact
  26643. end
  26644. def +(array)
  26645. PathSet.new(paths + array)
  26646. end
  26647. %w(<< concat push insert unshift).each do |method|
  26648. class_eval <<-METHOD, __FILE__, __LINE__ + 1
  26649. def #{method}(*args)
  26650. paths.#{method}(*typecast(args))
  26651. end
  26652. METHOD
  26653. end
  26654. def find(*args)
  26655. find_all(*args).first || raise(MissingTemplate.new(self, *args))
  26656. end
  26657. def find_all(path, prefixes = [], *args)
  26658. prefixes = [prefixes] if String === prefixes
  26659. prefixes.each do |prefix|
  26660. paths.each do |resolver|
  26661. templates = resolver.find_all(path, prefix, *args)
  26662. return templates unless templates.empty?
  26663. end
  26664. end
  26665. []
  26666. end
  26667. def exists?(path, prefixes, *args)
  26668. find_all(path, prefixes, *args).any?
  26669. end
  26670. private
  26671. def typecast(paths)
  26672. paths.map do |path|
  26673. case path
  26674. when Pathname, String
  26675. OptimizedFileSystemResolver.new path.to_s
  26676. else
  26677. path
  26678. end
  26679. end
  26680. end
  26681. end
  26682. end
  26683. require "action_view"
  26684. require "rails"
  26685. module ActionView
  26686. # = Action View Railtie
  26687. class Railtie < Rails::Railtie # :nodoc:
  26688. config.action_view = ActiveSupport::OrderedOptions.new
  26689. config.action_view.embed_authenticity_token_in_remote_forms = false
  26690. config.eager_load_namespaces << ActionView
  26691. initializer "action_view.embed_authenticity_token_in_remote_forms" do |app|
  26692. ActiveSupport.on_load(:action_view) do
  26693. ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms =
  26694. app.config.action_view.delete(:embed_authenticity_token_in_remote_forms)
  26695. end
  26696. end
  26697. initializer "action_view.logger" do
  26698. ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
  26699. end
  26700. initializer "action_view.set_configs" do |app|
  26701. ActiveSupport.on_load(:action_view) do
  26702. app.config.action_view.each do |k,v|
  26703. send "#{k}=", v
  26704. end
  26705. end
  26706. end
  26707. initializer "action_view.caching" do |app|
  26708. ActiveSupport.on_load(:action_view) do
  26709. if app.config.action_view.cache_template_loading.nil?
  26710. ActionView::Resolver.caching = app.config.cache_classes
  26711. end
  26712. end
  26713. end
  26714. end
  26715. end
  26716. require 'active_support/core_ext/module'
  26717. require 'action_view/model_naming'
  26718. module ActionView
  26719. # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
  26720. # pretty much any other model type that has an id. These patterns are then used to try elevate the view actions to
  26721. # a higher logical level.
  26722. #
  26723. # # routes
  26724. # resources :posts
  26725. #
  26726. # # view
  26727. # <%= div_for(post) do %> <div id="post_45" class="post">
  26728. # <%= post.body %> What a wonderful world!
  26729. # <% end %> </div>
  26730. #
  26731. # # controller
  26732. # def update
  26733. # post = Post.find(params[:id])
  26734. # post.update(params[:post])
  26735. #
  26736. # redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
  26737. # end
  26738. #
  26739. # As the example above shows, you can stop caring to a large extent what the actual id of the post is.
  26740. # You just know that one is being assigned and that the subsequent calls in redirect_to expect that
  26741. # same naming convention and allows you to write less code if you follow it.
  26742. module RecordIdentifier
  26743. extend self
  26744. extend ModelNaming
  26745. include ModelNaming
  26746. JOIN = '_'.freeze
  26747. NEW = 'new'.freeze
  26748. # The DOM class convention is to use the singular form of an object or class.
  26749. #
  26750. # dom_class(post) # => "post"
  26751. # dom_class(Person) # => "person"
  26752. #
  26753. # If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
  26754. #
  26755. # dom_class(post, :edit) # => "edit_post"
  26756. # dom_class(Person, :edit) # => "edit_person"
  26757. def dom_class(record_or_class, prefix = nil)
  26758. singular = model_name_from_record_or_class(record_or_class).param_key
  26759. prefix ? "#{prefix}#{JOIN}#{singular}" : singular
  26760. end
  26761. # The DOM id convention is to use the singular form of an object or class with the id following an underscore.
  26762. # If no id is found, prefix with "new_" instead.
  26763. #
  26764. # dom_id(Post.find(45)) # => "post_45"
  26765. # dom_id(Post.new) # => "new_post"
  26766. #
  26767. # If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
  26768. #
  26769. # dom_id(Post.find(45), :edit) # => "edit_post_45"
  26770. # dom_id(Post.new, :custom) # => "custom_post"
  26771. def dom_id(record, prefix = nil)
  26772. if record_id = record_key_for_dom_id(record)
  26773. "#{dom_class(record, prefix)}#{JOIN}#{record_id}"
  26774. else
  26775. dom_class(record, prefix || NEW)
  26776. end
  26777. end
  26778. protected
  26779. # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
  26780. # This can be overwritten to customize the default generated string representation if desired.
  26781. # If you need to read back a key from a dom_id in order to query for the underlying database record,
  26782. # you should write a helper like 'person_record_from_dom_id' that will extract the key either based
  26783. # on the default implementation (which just joins all key attributes with '_') or on your own
  26784. # overwritten version of the method. By default, this implementation passes the key string through a
  26785. # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
  26786. # make sure yourself that your dom ids are valid, in case you overwrite this method.
  26787. def record_key_for_dom_id(record)
  26788. key = convert_to_model(record).to_key
  26789. key ? key.join('_') : key
  26790. end
  26791. end
  26792. end
  26793. module ActionView
  26794. class AbstractRenderer #:nodoc:
  26795. delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
  26796. def initialize(lookup_context)
  26797. @lookup_context = lookup_context
  26798. end
  26799. def render
  26800. raise NotImplementedError
  26801. end
  26802. protected
  26803. def extract_details(options)
  26804. @lookup_context.registered_details.each_with_object({}) do |key, details|
  26805. next unless value = options[key]
  26806. details[key] = Array(value)
  26807. end
  26808. end
  26809. def instrument(name, options={})
  26810. ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
  26811. end
  26812. def prepend_formats(formats)
  26813. formats = Array(formats)
  26814. return if formats.empty? || @lookup_context.html_fallback_for_js
  26815. @lookup_context.formats = formats | @lookup_context.formats
  26816. end
  26817. end
  26818. end
  26819. require 'thread_safe'
  26820. module ActionView
  26821. # = Action View Partials
  26822. #
  26823. # There's also a convenience method for rendering sub templates within the current controller that depends on a
  26824. # single object (we call this kind of sub templates for partials). It relies on the fact that partials should
  26825. # follow the naming convention of being prefixed with an underscore -- as to separate them from regular
  26826. # templates that could be rendered on their own.
  26827. #
  26828. # In a template for Advertiser#account:
  26829. #
  26830. # <%= render partial: "account" %>
  26831. #
  26832. # This would render "advertiser/_account.html.erb".
  26833. #
  26834. # In another template for Advertiser#buy, we could have:
  26835. #
  26836. # <%= render partial: "account", locals: { account: @buyer } %>
  26837. #
  26838. # <% @advertisements.each do |ad| %>
  26839. # <%= render partial: "ad", locals: { ad: ad } %>
  26840. # <% end %>
  26841. #
  26842. # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then
  26843. # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display.
  26844. #
  26845. # == The :as and :object options
  26846. #
  26847. # By default <tt>ActionView::PartialRenderer</tt> doesn't have any local variables.
  26848. # The <tt>:object</tt> option can be used to pass an object to the partial. For instance:
  26849. #
  26850. # <%= render partial: "account", object: @buyer %>
  26851. #
  26852. # would provide the <tt>@buyer</tt> object to the partial, available under the local variable +account+ and is
  26853. # equivalent to:
  26854. #
  26855. # <%= render partial: "account", locals: { account: @buyer } %>
  26856. #
  26857. # With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
  26858. # wanted it to be +user+ instead of +account+ we'd do:
  26859. #
  26860. # <%= render partial: "account", object: @buyer, as: 'user' %>
  26861. #
  26862. # This is equivalent to
  26863. #
  26864. # <%= render partial: "account", locals: { user: @buyer } %>
  26865. #
  26866. # == Rendering a collection of partials
  26867. #
  26868. # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
  26869. # render a sub template for each of the elements. This pattern has been implemented as a single method that
  26870. # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined
  26871. # example in "Using partials" can be rewritten with a single line:
  26872. #
  26873. # <%= render partial: "ad", collection: @advertisements %>
  26874. #
  26875. # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An
  26876. # iteration counter will automatically be made available to the template with a name of the form
  26877. # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
  26878. #
  26879. # The <tt>:as</tt> option may be used when rendering partials.
  26880. #
  26881. # You can specify a partial to be rendered between elements via the <tt>:spacer_template</tt> option.
  26882. # The following example will render <tt>advertiser/_ad_divider.html.erb</tt> between each ad partial:
  26883. #
  26884. # <%= render partial: "ad", collection: @advertisements, spacer_template: "ad_divider" %>
  26885. #
  26886. # If the given <tt>:collection</tt> is nil or empty, <tt>render</tt> will return nil. This will allow you
  26887. # to specify a text which will displayed instead by using this form:
  26888. #
  26889. # <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>
  26890. #
  26891. # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
  26892. # just keep domain objects, like Active Records, in there.
  26893. #
  26894. # == Rendering shared partials
  26895. #
  26896. # Two controllers can share a set of partials and render them like this:
  26897. #
  26898. # <%= render partial: "advertisement/ad", locals: { ad: @advertisement } %>
  26899. #
  26900. # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
  26901. #
  26902. # == Rendering objects that respond to `to_partial_path`
  26903. #
  26904. # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work
  26905. # and pick the proper path by checking `to_partial_path` method.
  26906. #
  26907. # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
  26908. # # <%= render partial: "accounts/account", locals: { account: @account} %>
  26909. # <%= render partial: @account %>
  26910. #
  26911. # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
  26912. # # that's why we can replace:
  26913. # # <%= render partial: "posts/post", collection: @posts %>
  26914. # <%= render partial: @posts %>
  26915. #
  26916. # == Rendering the default case
  26917. #
  26918. # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand
  26919. # defaults of render to render partials. Examples:
  26920. #
  26921. # # Instead of <%= render partial: "account" %>
  26922. # <%= render "account" %>
  26923. #
  26924. # # Instead of <%= render partial: "account", locals: { account: @buyer } %>
  26925. # <%= render "account", account: @buyer %>
  26926. #
  26927. # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
  26928. # # <%= render partial: "accounts/account", locals: { account: @account} %>
  26929. # <%= render @account %>
  26930. #
  26931. # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
  26932. # # that's why we can replace:
  26933. # # <%= render partial: "posts/post", collection: @posts %>
  26934. # <%= render @posts %>
  26935. #
  26936. # == Rendering partials with layouts
  26937. #
  26938. # Partials can have their own layouts applied to them. These layouts are different than the ones that are
  26939. # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
  26940. # of users:
  26941. #
  26942. # <%# app/views/users/index.html.erb &>
  26943. # Here's the administrator:
  26944. # <%= render partial: "user", layout: "administrator", locals: { user: administrator } %>
  26945. #
  26946. # Here's the editor:
  26947. # <%= render partial: "user", layout: "editor", locals: { user: editor } %>
  26948. #
  26949. # <%# app/views/users/_user.html.erb &>
  26950. # Name: <%= user.name %>
  26951. #
  26952. # <%# app/views/users/_administrator.html.erb &>
  26953. # <div id="administrator">
  26954. # Budget: $<%= user.budget %>
  26955. # <%= yield %>
  26956. # </div>
  26957. #
  26958. # <%# app/views/users/_editor.html.erb &>
  26959. # <div id="editor">
  26960. # Deadline: <%= user.deadline %>
  26961. # <%= yield %>
  26962. # </div>
  26963. #
  26964. # ...this will return:
  26965. #
  26966. # Here's the administrator:
  26967. # <div id="administrator">
  26968. # Budget: $<%= user.budget %>
  26969. # Name: <%= user.name %>
  26970. # </div>
  26971. #
  26972. # Here's the editor:
  26973. # <div id="editor">
  26974. # Deadline: <%= user.deadline %>
  26975. # Name: <%= user.name %>
  26976. # </div>
  26977. #
  26978. # If a collection is given, the layout will be rendered once for each item in
  26979. # the collection. Just think these two snippets have the same output:
  26980. #
  26981. # <%# app/views/users/_user.html.erb %>
  26982. # Name: <%= user.name %>
  26983. #
  26984. # <%# app/views/users/index.html.erb %>
  26985. # <%# This does not use layouts %>
  26986. # <ul>
  26987. # <% users.each do |user| -%>
  26988. # <li>
  26989. # <%= render partial: "user", locals: { user: user } %>
  26990. # </li>
  26991. # <% end -%>
  26992. # </ul>
  26993. #
  26994. # <%# app/views/users/_li_layout.html.erb %>
  26995. # <li>
  26996. # <%= yield %>
  26997. # </li>
  26998. #
  26999. # <%# app/views/users/index.html.erb %>
  27000. # <ul>
  27001. # <%= render partial: "user", layout: "li_layout", collection: users %>
  27002. # </ul>
  27003. #
  27004. # Given two users whose names are Alice and Bob, these snippets return:
  27005. #
  27006. # <ul>
  27007. # <li>
  27008. # Name: Alice
  27009. # </li>
  27010. # <li>
  27011. # Name: Bob
  27012. # </li>
  27013. # </ul>
  27014. #
  27015. # The current object being rendered, as well as the object_counter, will be
  27016. # available as local variables inside the layout template under the same names
  27017. # as available in the partial.
  27018. #
  27019. # You can also apply a layout to a block within any template:
  27020. #
  27021. # <%# app/views/users/_chief.html.erb &>
  27022. # <%= render(layout: "administrator", locals: { user: chief }) do %>
  27023. # Title: <%= chief.title %>
  27024. # <% end %>
  27025. #
  27026. # ...this will return:
  27027. #
  27028. # <div id="administrator">
  27029. # Budget: $<%= user.budget %>
  27030. # Title: <%= chief.name %>
  27031. # </div>
  27032. #
  27033. # As you can see, the <tt>:locals</tt> hash is shared between both the partial and its layout.
  27034. #
  27035. # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
  27036. # an array to layout and treat it as an enumerable.
  27037. #
  27038. # <%# app/views/users/_user.html.erb &>
  27039. # <div class="user">
  27040. # Budget: $<%= user.budget %>
  27041. # <%= yield user %>
  27042. # </div>
  27043. #
  27044. # <%# app/views/users/index.html.erb &>
  27045. # <%= render layout: @users do |user| %>
  27046. # Title: <%= user.title %>
  27047. # <% end %>
  27048. #
  27049. # This will render the layout for each user and yield to the block, passing the user, each time.
  27050. #
  27051. # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
  27052. #
  27053. # <%# app/views/users/_user.html.erb &>
  27054. # <div class="user">
  27055. # <%= yield user, :header %>
  27056. # Budget: $<%= user.budget %>
  27057. # <%= yield user, :footer %>
  27058. # </div>
  27059. #
  27060. # <%# app/views/users/index.html.erb &>
  27061. # <%= render layout: @users do |user, section| %>
  27062. # <%- case section when :header -%>
  27063. # Title: <%= user.title %>
  27064. # <%- when :footer -%>
  27065. # Deadline: <%= user.deadline %>
  27066. # <%- end -%>
  27067. # <% end %>
  27068. class PartialRenderer < AbstractRenderer
  27069. PREFIXED_PARTIAL_NAMES = ThreadSafe::Cache.new do |h, k|
  27070. h[k] = ThreadSafe::Cache.new
  27071. end
  27072. def initialize(*)
  27073. super
  27074. @context_prefix = @lookup_context.prefixes.first
  27075. end
  27076. def render(context, options, block)
  27077. setup(context, options, block)
  27078. identifier = (@template = find_partial) ? @template.identifier : @path
  27079. @lookup_context.rendered_format ||= begin
  27080. if @template && @template.formats.present?
  27081. @template.formats.first
  27082. else
  27083. formats.first
  27084. end
  27085. end
  27086. if @collection
  27087. instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
  27088. render_collection
  27089. end
  27090. else
  27091. instrument(:partial, :identifier => identifier) do
  27092. render_partial
  27093. end
  27094. end
  27095. end
  27096. def render_collection
  27097. return nil if @collection.blank?
  27098. if @options.key?(:spacer_template)
  27099. spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
  27100. end
  27101. result = @template ? collection_with_template : collection_without_template
  27102. result.join(spacer).html_safe
  27103. end
  27104. def render_partial
  27105. view, locals, block = @view, @locals, @block
  27106. object, as = @object, @variable
  27107. if !block && (layout = @options[:layout])
  27108. layout = find_template(layout.to_s, @template_keys)
  27109. end
  27110. object ||= locals[as]
  27111. locals[as] = object
  27112. content = @template.render(view, locals) do |*name|
  27113. view._layout_for(*name, &block)
  27114. end
  27115. content = layout.render(view, locals){ content } if layout
  27116. content
  27117. end
  27118. private
  27119. def setup(context, options, block)
  27120. @view = context
  27121. partial = options[:partial]
  27122. @options = options
  27123. @locals = options[:locals] || {}
  27124. @block = block
  27125. @details = extract_details(options)
  27126. prepend_formats(options[:formats])
  27127. if String === partial
  27128. @object = options[:object]
  27129. @path = partial
  27130. @collection = collection
  27131. else
  27132. @object = partial
  27133. if @collection = collection_from_object || collection
  27134. paths = @collection_data = @collection.map { |o| partial_path(o) }
  27135. @path = paths.uniq.size == 1 ? paths.first : nil
  27136. else
  27137. @path = partial_path
  27138. end
  27139. end
  27140. if as = options[:as]
  27141. raise_invalid_identifier(as) unless as.to_s =~ /\A[a-z_]\w*\z/
  27142. as = as.to_sym
  27143. end
  27144. if @path
  27145. @variable, @variable_counter = retrieve_variable(@path, as)
  27146. @template_keys = retrieve_template_keys
  27147. else
  27148. paths.map! { |path| retrieve_variable(path, as).unshift(path) }
  27149. end
  27150. self
  27151. end
  27152. def collection
  27153. if @options.key?(:collection)
  27154. collection = @options[:collection]
  27155. collection.respond_to?(:to_ary) ? collection.to_ary : []
  27156. end
  27157. end
  27158. def collection_from_object
  27159. @object.to_ary if @object.respond_to?(:to_ary)
  27160. end
  27161. def find_partial
  27162. if path = @path
  27163. find_template(path, @template_keys)
  27164. end
  27165. end
  27166. def find_template(path, locals)
  27167. prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
  27168. @lookup_context.find_template(path, prefixes, true, locals, @details)
  27169. end
  27170. def collection_with_template
  27171. view, locals, template = @view, @locals, @template
  27172. as, counter = @variable, @variable_counter
  27173. if layout = @options[:layout]
  27174. layout = find_template(layout, @template_keys)
  27175. end
  27176. index = -1
  27177. @collection.map do |object|
  27178. locals[as] = object
  27179. locals[counter] = (index += 1)
  27180. content = template.render(view, locals)
  27181. content = layout.render(view, locals) { content } if layout
  27182. content
  27183. end
  27184. end
  27185. def collection_without_template
  27186. view, locals, collection_data = @view, @locals, @collection_data
  27187. cache = {}
  27188. keys = @locals.keys
  27189. index = -1
  27190. @collection.map do |object|
  27191. index += 1
  27192. path, as, counter = collection_data[index]
  27193. locals[as] = object
  27194. locals[counter] = index
  27195. template = (cache[path] ||= find_template(path, keys + [as, counter]))
  27196. template.render(view, locals)
  27197. end
  27198. end
  27199. def partial_path(object = @object)
  27200. object = object.to_model if object.respond_to?(:to_model)
  27201. path = if object.respond_to?(:to_partial_path)
  27202. object.to_partial_path
  27203. else
  27204. raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
  27205. end
  27206. if @view.prefix_partial_path_with_controller_namespace
  27207. prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
  27208. else
  27209. path
  27210. end
  27211. end
  27212. def prefixed_partial_names
  27213. @prefixed_partial_names ||= PREFIXED_PARTIAL_NAMES[@context_prefix]
  27214. end
  27215. def merge_prefix_into_object_path(prefix, object_path)
  27216. if prefix.include?(?/) && object_path.include?(?/)
  27217. prefixes = []
  27218. prefix_array = File.dirname(prefix).split('/')
  27219. object_path_array = object_path.split('/')[0..-3] # skip model dir & partial
  27220. prefix_array.each_with_index do |dir, index|
  27221. break if dir == object_path_array[index]
  27222. prefixes << dir
  27223. end
  27224. (prefixes << object_path).join("/")
  27225. else
  27226. object_path
  27227. end
  27228. end
  27229. def retrieve_template_keys
  27230. keys = @locals.keys
  27231. keys << @variable if @object || @collection
  27232. keys << @variable_counter if @collection
  27233. keys
  27234. end
  27235. def retrieve_variable(path, as)
  27236. variable = as || begin
  27237. base = path[-1] == "/" ? "" : File.basename(path)
  27238. raise_invalid_identifier(path) unless base =~ /\A_?([a-z]\w*)(\.\w+)*\z/
  27239. $1.to_sym
  27240. end
  27241. variable_counter = :"#{variable}_counter" if @collection
  27242. [variable, variable_counter]
  27243. end
  27244. IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " +
  27245. "make sure your partial name starts with a lowercase letter or underscore, " +
  27246. "and is followed by any combination of letters, numbers and underscores."
  27247. def raise_invalid_identifier(path)
  27248. raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path))
  27249. end
  27250. end
  27251. end
  27252. module ActionView
  27253. # This is the main entry point for rendering. It basically delegates
  27254. # to other objects like TemplateRenderer and PartialRenderer which
  27255. # actually renders the template.
  27256. class Renderer
  27257. attr_accessor :lookup_context
  27258. def initialize(lookup_context)
  27259. @lookup_context = lookup_context
  27260. end
  27261. # Main render entry point shared by AV and AC.
  27262. def render(context, options)
  27263. if options.key?(:partial)
  27264. render_partial(context, options)
  27265. else
  27266. render_template(context, options)
  27267. end
  27268. end
  27269. # Render but returns a valid Rack body. If fibers are defined, we return
  27270. # a streaming body that renders the template piece by piece.
  27271. #
  27272. # Note that partials are not supported to be rendered with streaming,
  27273. # so in such cases, we just wrap them in an array.
  27274. def render_body(context, options)
  27275. if options.key?(:partial)
  27276. [render_partial(context, options)]
  27277. else
  27278. StreamingTemplateRenderer.new(@lookup_context).render(context, options)
  27279. end
  27280. end
  27281. # Direct accessor to template rendering.
  27282. def render_template(context, options) #:nodoc:
  27283. TemplateRenderer.new(@lookup_context).render(context, options)
  27284. end
  27285. # Direct access to partial rendering.
  27286. def render_partial(context, options, &block) #:nodoc:
  27287. PartialRenderer.new(@lookup_context).render(context, options, block)
  27288. end
  27289. end
  27290. end
  27291. require 'fiber'
  27292. module ActionView
  27293. # == TODO
  27294. #
  27295. # * Support streaming from child templates, partials and so on.
  27296. # * Integrate exceptions with exceptron
  27297. # * Rack::Cache needs to support streaming bodies
  27298. class StreamingTemplateRenderer < TemplateRenderer #:nodoc:
  27299. # A valid Rack::Body (i.e. it responds to each).
  27300. # It is initialized with a block that, when called, starts
  27301. # rendering the template.
  27302. class Body #:nodoc:
  27303. def initialize(&start)
  27304. @start = start
  27305. end
  27306. def each(&block)
  27307. begin
  27308. @start.call(block)
  27309. rescue Exception => exception
  27310. log_error(exception)
  27311. block.call ActionView::Base.streaming_completion_on_exception
  27312. end
  27313. self
  27314. end
  27315. private
  27316. # This is the same logging logic as in ShowExceptions middleware.
  27317. # TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
  27318. def log_error(exception) #:nodoc:
  27319. logger = ActionView::Base.logger
  27320. return unless logger
  27321. message = "\n#{exception.class} (#{exception.message}):\n"
  27322. message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
  27323. message << " " << exception.backtrace.join("\n ")
  27324. logger.fatal("#{message}\n\n")
  27325. end
  27326. end
  27327. # For streaming, instead of rendering a given a template, we return a Body
  27328. # object that responds to each. This object is initialized with a block
  27329. # that knows how to render the template.
  27330. def render_template(template, layout_name = nil, locals = {}) #:nodoc:
  27331. return [super] unless layout_name && template.supports_streaming?
  27332. locals ||= {}
  27333. layout = layout_name && find_layout(layout_name, locals.keys)
  27334. Body.new do |buffer|
  27335. delayed_render(buffer, template, layout, @view, locals)
  27336. end
  27337. end
  27338. private
  27339. def delayed_render(buffer, template, layout, view, locals)
  27340. # Wrap the given buffer in the StreamingBuffer and pass it to the
  27341. # underlying template handler. Now, everytime something is concatenated
  27342. # to the buffer, it is not appended to an array, but streamed straight
  27343. # to the client.
  27344. output = ActionView::StreamingBuffer.new(buffer)
  27345. yielder = lambda { |*name| view._layout_for(*name) }
  27346. instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
  27347. fiber = Fiber.new do
  27348. if layout
  27349. layout.render(view, locals, output, &yielder)
  27350. else
  27351. # If you don't have a layout, just render the thing
  27352. # and concatenate the final result. This is the same
  27353. # as a layout with just <%= yield %>
  27354. output.safe_concat view._layout_for
  27355. end
  27356. end
  27357. # Set the view flow to support streaming. It will be aware
  27358. # when to stop rendering the layout because it needs to search
  27359. # something in the template and vice-versa.
  27360. view.view_flow = StreamingFlow.new(view, fiber)
  27361. # Yo! Start the fiber!
  27362. fiber.resume
  27363. # If the fiber is still alive, it means we need something
  27364. # from the template, so start rendering it. If not, it means
  27365. # the layout exited without requiring anything from the template.
  27366. if fiber.alive?
  27367. content = template.render(view, locals, &yielder)
  27368. # Once rendering the template is done, sets its content in the :layout key.
  27369. view.view_flow.set(:layout, content)
  27370. # In case the layout continues yielding, we need to resume
  27371. # the fiber until all yields are handled.
  27372. fiber.resume while fiber.alive?
  27373. end
  27374. end
  27375. end
  27376. end
  27377. end
  27378. require 'active_support/core_ext/object/try'
  27379. module ActionView
  27380. class TemplateRenderer < AbstractRenderer #:nodoc:
  27381. def render(context, options)
  27382. @view = context
  27383. @details = extract_details(options)
  27384. template = determine_template(options)
  27385. context = @lookup_context
  27386. prepend_formats(template.formats)
  27387. unless context.rendered_format
  27388. context.rendered_format = template.formats.first || formats.last
  27389. end
  27390. render_template(template, options[:layout], options[:locals])
  27391. end
  27392. # Determine the template to be rendered using the given options.
  27393. def determine_template(options) #:nodoc:
  27394. keys = options.fetch(:locals, {}).keys
  27395. if options.key?(:text)
  27396. Template::Text.new(options[:text], formats.first)
  27397. elsif options.key?(:file)
  27398. with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
  27399. elsif options.key?(:inline)
  27400. handler = Template.handler_for_extension(options[:type] || "erb")
  27401. Template.new(options[:inline], "inline template", handler, :locals => keys)
  27402. elsif options.key?(:template)
  27403. if options[:template].respond_to?(:render)
  27404. options[:template]
  27405. else
  27406. find_template(options[:template], options[:prefixes], false, keys, @details)
  27407. end
  27408. else
  27409. raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option."
  27410. end
  27411. end
  27412. # Renders the given template. A string representing the layout can be
  27413. # supplied as well.
  27414. def render_template(template, layout_name = nil, locals = nil) #:nodoc:
  27415. view, locals = @view, locals || {}
  27416. render_with_layout(layout_name, locals) do |layout|
  27417. instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
  27418. template.render(view, locals) { |*name| view._layout_for(*name) }
  27419. end
  27420. end
  27421. end
  27422. def render_with_layout(path, locals) #:nodoc:
  27423. layout = path && find_layout(path, locals.keys)
  27424. content = yield(layout)
  27425. if layout
  27426. view = @view
  27427. view.view_flow.set(:layout, content)
  27428. layout.render(view, locals){ |*name| view._layout_for(*name) }
  27429. else
  27430. content
  27431. end
  27432. end
  27433. # This is the method which actually finds the layout using details in the lookup
  27434. # context object. If no layout is found, it checks if at least a layout with
  27435. # the given name exists across all details before raising the error.
  27436. def find_layout(layout, keys)
  27437. with_layout_format { resolve_layout(layout, keys) }
  27438. end
  27439. def resolve_layout(layout, keys)
  27440. case layout
  27441. when String
  27442. begin
  27443. if layout =~ /^\//
  27444. with_fallbacks { find_template(layout, nil, false, keys, @details) }
  27445. else
  27446. find_template(layout, nil, false, keys, @details)
  27447. end
  27448. rescue ActionView::MissingTemplate
  27449. all_details = @details.merge(:formats => @lookup_context.default_formats)
  27450. raise unless template_exists?(layout, nil, false, keys, all_details)
  27451. end
  27452. when Proc
  27453. resolve_layout(layout.call, keys)
  27454. when FalseClass
  27455. nil
  27456. else
  27457. layout
  27458. end
  27459. end
  27460. end
  27461. end
  27462. module ActionView
  27463. module RoutingUrlFor
  27464. # Returns the URL for the set of +options+ provided. This takes the
  27465. # same options as +url_for+ in Action Controller (see the
  27466. # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
  27467. # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
  27468. # instead of the fully qualified URL like "http://example.com/controller/action".
  27469. #
  27470. # ==== Options
  27471. # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
  27472. # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
  27473. # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
  27474. # is currently not recommended since it breaks caching.
  27475. # * <tt>:host</tt> - Overrides the default (current) host if provided.
  27476. # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
  27477. # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
  27478. # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
  27479. #
  27480. # ==== Relying on named routes
  27481. #
  27482. # Passing a record (like an Active Record) instead of a hash as the options parameter will
  27483. # trigger the named route for that record. The lookup will happen on the name of the class. So passing a
  27484. # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
  27485. # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
  27486. #
  27487. # ==== Implicit Controller Namespacing
  27488. #
  27489. # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
  27490. #
  27491. # ==== Examples
  27492. # <%= url_for(action: 'index') %>
  27493. # # => /blog/
  27494. #
  27495. # <%= url_for(action: 'find', controller: 'books') %>
  27496. # # => /books/find
  27497. #
  27498. # <%= url_for(action: 'login', controller: 'members', only_path: false, protocol: 'https') %>
  27499. # # => https://www.example.com/members/login/
  27500. #
  27501. # <%= url_for(action: 'play', anchor: 'player') %>
  27502. # # => /messages/play/#player
  27503. #
  27504. # <%= url_for(action: 'jump', anchor: 'tax&ship') %>
  27505. # # => /testing/jump/#tax&ship
  27506. #
  27507. # <%= url_for(Workshop.new) %>
  27508. # # relies on Workshop answering a persisted? call (and in this case returning false)
  27509. # # => /workshops
  27510. #
  27511. # <%= url_for(@workshop) %>
  27512. # # calls @workshop.to_param which by default returns the id
  27513. # # => /workshops/5
  27514. #
  27515. # # to_param can be re-defined in a model to provide different URL names:
  27516. # # => /workshops/1-workshop-name
  27517. #
  27518. # <%= url_for("http://www.example.com") %>
  27519. # # => http://www.example.com
  27520. #
  27521. # <%= url_for(:back) %>
  27522. # # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
  27523. # # => http://www.example.com
  27524. #
  27525. # <%= url_for(:back) %>
  27526. # # if request.env["HTTP_REFERER"] is not set or is blank
  27527. # # => javascript:history.back()
  27528. #
  27529. # <%= url_for(action: 'index', controller: 'users') %>
  27530. # # Assuming an "admin" namespace
  27531. # # => /admin/users
  27532. #
  27533. # <%= url_for(action: 'index', controller: '/users') %>
  27534. # # Specify absolute path with beginning slash
  27535. # # => /users
  27536. def url_for(options = nil)
  27537. case options
  27538. when String
  27539. options
  27540. when nil, Hash
  27541. options ||= {}
  27542. options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys)
  27543. super
  27544. when :back
  27545. _back_url
  27546. else
  27547. polymorphic_path(options)
  27548. end
  27549. end
  27550. def url_options #:nodoc:
  27551. return super unless controller.respond_to?(:url_options)
  27552. controller.url_options
  27553. end
  27554. def _routes_context #:nodoc:
  27555. controller
  27556. end
  27557. protected :_routes_context
  27558. def optimize_routes_generation? #:nodoc:
  27559. controller.respond_to?(:optimize_routes_generation?, true) ?
  27560. controller.optimize_routes_generation? : super
  27561. end
  27562. protected :optimize_routes_generation?
  27563. end
  27564. end
  27565. require "active_support/core_ext/enumerable"
  27566. module ActionView
  27567. # = Action View Errors
  27568. class ActionViewError < StandardError #:nodoc:
  27569. end
  27570. class EncodingError < StandardError #:nodoc:
  27571. end
  27572. class MissingRequestError < StandardError #:nodoc:
  27573. end
  27574. class WrongEncodingError < EncodingError #:nodoc:
  27575. def initialize(string, encoding)
  27576. @string, @encoding = string, encoding
  27577. end
  27578. def message
  27579. @string.force_encoding(Encoding::ASCII_8BIT)
  27580. "Your template was not saved as valid #{@encoding}. Please " \
  27581. "either specify #{@encoding} as the encoding for your template " \
  27582. "in your text editor, or mark the template with its " \
  27583. "encoding by inserting the following as the first line " \
  27584. "of the template:\n\n# encoding: <name of correct encoding>.\n\n" \
  27585. "The source of your template was:\n\n#{@string}"
  27586. end
  27587. end
  27588. class MissingTemplate < ActionViewError #:nodoc:
  27589. attr_reader :path
  27590. def initialize(paths, path, prefixes, partial, details, *)
  27591. @path = path
  27592. prefixes = Array(prefixes)
  27593. template_type = if partial
  27594. "partial"
  27595. elsif path =~ /layouts/i
  27596. 'layout'
  27597. else
  27598. 'template'
  27599. end
  27600. searched_paths = prefixes.map { |prefix| [prefix, path].join("/") }
  27601. out = "Missing #{template_type} #{searched_paths.join(", ")} with #{details.inspect}. Searched in:\n"
  27602. out += paths.compact.map { |p| " * #{p.to_s.inspect}\n" }.join
  27603. super out
  27604. end
  27605. end
  27606. class Template
  27607. # The Template::Error exception is raised when the compilation or rendering of the template
  27608. # fails. This exception then gathers a bunch of intimate details and uses it to report a
  27609. # precise exception message.
  27610. class Error < ActionViewError #:nodoc:
  27611. SOURCE_CODE_RADIUS = 3
  27612. attr_reader :original_exception, :backtrace
  27613. def initialize(template, original_exception)
  27614. super(original_exception.message)
  27615. @template, @original_exception = template, original_exception
  27616. @sub_templates = nil
  27617. @backtrace = original_exception.backtrace
  27618. end
  27619. def file_name
  27620. @template.identifier
  27621. end
  27622. def sub_template_message
  27623. if @sub_templates
  27624. "Trace of template inclusion: " +
  27625. @sub_templates.collect { |template| template.inspect }.join(", ")
  27626. else
  27627. ""
  27628. end
  27629. end
  27630. def source_extract(indentation = 0, output = :console)
  27631. return unless num = line_number
  27632. num = num.to_i
  27633. source_code = @template.source.split("\n")
  27634. start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
  27635. end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
  27636. indent = end_on_line.to_s.size + indentation
  27637. return unless source_code = source_code[start_on_line..end_on_line]
  27638. formatted_code_for(source_code, start_on_line, indent, output)
  27639. end
  27640. def sub_template_of(template_path)
  27641. @sub_templates ||= []
  27642. @sub_templates << template_path
  27643. end
  27644. def line_number
  27645. @line_number ||=
  27646. if file_name
  27647. regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
  27648. $1 if message =~ regexp || backtrace.find { |line| line =~ regexp }
  27649. end
  27650. end
  27651. def annoted_source_code
  27652. source_extract(4)
  27653. end
  27654. private
  27655. def source_location
  27656. if line_number
  27657. "on line ##{line_number} of "
  27658. else
  27659. 'in '
  27660. end + file_name
  27661. end
  27662. def formatted_code_for(source_code, line_counter, indent, output)
  27663. start_value = (output == :html) ? {} : ""
  27664. source_code.inject(start_value) do |result, line|
  27665. line_counter += 1
  27666. if output == :html
  27667. result.update(line_counter.to_s => "%#{indent}s %s\n" % ["", line])
  27668. else
  27669. result << "%#{indent}s: %s\n" % [line_counter, line]
  27670. end
  27671. end
  27672. end
  27673. end
  27674. end
  27675. TemplateError = Template::Error
  27676. end
  27677. module ActionView
  27678. module Template::Handlers
  27679. class Builder
  27680. # Default format used by Builder.
  27681. class_attribute :default_format
  27682. self.default_format = :xml
  27683. def call(template)
  27684. require_engine
  27685. "xml = ::Builder::XmlMarkup.new(:indent => 2);" +
  27686. "self.output_buffer = xml.target!;" +
  27687. template.source +
  27688. ";xml.target!;"
  27689. end
  27690. protected
  27691. def require_engine
  27692. @required ||= begin
  27693. require "builder"
  27694. true
  27695. end
  27696. end
  27697. end
  27698. end
  27699. end
  27700. require 'action_dispatch/http/mime_type'
  27701. require 'erubis'
  27702. module ActionView
  27703. class Template
  27704. module Handlers
  27705. class Erubis < ::Erubis::Eruby
  27706. def add_preamble(src)
  27707. src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
  27708. end
  27709. def add_text(src, text)
  27710. return if text.empty?
  27711. src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
  27712. end
  27713. # Erubis toggles <%= and <%== behavior when escaping is enabled.
  27714. # We override to always treat <%== as escaped.
  27715. def add_expr(src, code, indicator)
  27716. case indicator
  27717. when '=='
  27718. add_expr_escaped(src, code)
  27719. else
  27720. super
  27721. end
  27722. end
  27723. BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
  27724. def add_expr_literal(src, code)
  27725. if code =~ BLOCK_EXPR
  27726. src << '@output_buffer.append= ' << code
  27727. else
  27728. src << '@output_buffer.append= (' << code << ');'
  27729. end
  27730. end
  27731. def add_expr_escaped(src, code)
  27732. if code =~ BLOCK_EXPR
  27733. src << "@output_buffer.safe_append= " << code
  27734. else
  27735. src << "@output_buffer.safe_concat((" << code << ").to_s);"
  27736. end
  27737. end
  27738. def add_postamble(src)
  27739. src << '@output_buffer.to_s'
  27740. end
  27741. end
  27742. class ERB
  27743. # Specify trim mode for the ERB compiler. Defaults to '-'.
  27744. # See ERB documentation for suitable values.
  27745. class_attribute :erb_trim_mode
  27746. self.erb_trim_mode = '-'
  27747. # Default implementation used.
  27748. class_attribute :erb_implementation
  27749. self.erb_implementation = Erubis
  27750. # Do not escape templates of these mime types.
  27751. class_attribute :escape_whitelist
  27752. self.escape_whitelist = ["text/plain"]
  27753. ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
  27754. def self.call(template)
  27755. new.call(template)
  27756. end
  27757. def supports_streaming?
  27758. true
  27759. end
  27760. def handles_encoding?
  27761. true
  27762. end
  27763. def call(template)
  27764. # First, convert to BINARY, so in case the encoding is
  27765. # wrong, we can still find an encoding tag
  27766. # (<%# encoding %>) inside the String using a regular
  27767. # expression
  27768. template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
  27769. erb = template_source.gsub(ENCODING_TAG, '')
  27770. encoding = $2
  27771. erb.force_encoding valid_encoding(template.source.dup, encoding)
  27772. # Always make sure we return a String in the default_internal
  27773. erb.encode!
  27774. self.class.erb_implementation.new(
  27775. erb,
  27776. :escape => (self.class.escape_whitelist.include? template.type),
  27777. :trim => (self.class.erb_trim_mode == "-")
  27778. ).src
  27779. end
  27780. private
  27781. def valid_encoding(string, encoding)
  27782. # If a magic encoding comment was found, tag the
  27783. # String with this encoding. This is for a case
  27784. # where the original String was assumed to be,
  27785. # for instance, UTF-8, but a magic comment
  27786. # proved otherwise
  27787. string.force_encoding(encoding) if encoding
  27788. # If the String is valid, return the encoding we found
  27789. return string.encoding if string.valid_encoding?
  27790. # Otherwise, raise an exception
  27791. raise WrongEncodingError.new(string, string.encoding)
  27792. end
  27793. end
  27794. end
  27795. end
  27796. end
  27797. module ActionView
  27798. module Template::Handlers
  27799. class Raw
  27800. def call(template)
  27801. escaped = template.source.gsub(':', '\:')
  27802. '%q:' + escaped + ':;'
  27803. end
  27804. end
  27805. end
  27806. end
  27807. module ActionView #:nodoc:
  27808. # = Action View Template Handlers
  27809. class Template
  27810. module Handlers #:nodoc:
  27811. autoload :ERB, 'action_view/template/handlers/erb'
  27812. autoload :Builder, 'action_view/template/handlers/builder'
  27813. autoload :Raw, 'action_view/template/handlers/raw'
  27814. def self.extended(base)
  27815. base.register_default_template_handler :erb, ERB.new
  27816. base.register_template_handler :builder, Builder.new
  27817. base.register_template_handler :raw, Raw.new
  27818. base.register_template_handler :ruby, :source.to_proc
  27819. end
  27820. @@template_handlers = {}
  27821. @@default_template_handlers = nil
  27822. def self.extensions
  27823. @@template_extensions ||= @@template_handlers.keys
  27824. end
  27825. # Register an object that knows how to handle template files with the given
  27826. # extensions. This can be used to implement new template types.
  27827. # The handler must respond to `:call`, which will be passed the template
  27828. # and should return the rendered template as a String.
  27829. def register_template_handler(*extensions, handler)
  27830. raise(ArgumentError, "Extension is required") if extensions.empty?
  27831. extensions.each do |extension|
  27832. @@template_handlers[extension.to_sym] = handler
  27833. end
  27834. @@template_extensions = nil
  27835. end
  27836. def template_handler_extensions
  27837. @@template_handlers.keys.map {|key| key.to_s }.sort
  27838. end
  27839. def registered_template_handler(extension)
  27840. extension && @@template_handlers[extension.to_sym]
  27841. end
  27842. def register_default_template_handler(extension, klass)
  27843. register_template_handler(extension, klass)
  27844. @@default_template_handlers = klass
  27845. end
  27846. def handler_for_extension(extension)
  27847. registered_template_handler(extension) || @@default_template_handlers
  27848. end
  27849. end
  27850. end
  27851. end
  27852. require "pathname"
  27853. require "active_support/core_ext/class"
  27854. require "active_support/core_ext/class/attribute_accessors"
  27855. require "action_view/template"
  27856. require "thread"
  27857. require "thread_safe"
  27858. module ActionView
  27859. # = Action View Resolver
  27860. class Resolver
  27861. # Keeps all information about view path and builds virtual path.
  27862. class Path
  27863. attr_reader :name, :prefix, :partial, :virtual
  27864. alias_method :partial?, :partial
  27865. def self.build(name, prefix, partial)
  27866. virtual = ""
  27867. virtual << "#{prefix}/" unless prefix.empty?
  27868. virtual << (partial ? "_#{name}" : name)
  27869. new name, prefix, partial, virtual
  27870. end
  27871. def initialize(name, prefix, partial, virtual)
  27872. @name = name
  27873. @prefix = prefix
  27874. @partial = partial
  27875. @virtual = virtual
  27876. end
  27877. def to_str
  27878. @virtual
  27879. end
  27880. alias :to_s :to_str
  27881. end
  27882. # Threadsafe template cache
  27883. class Cache #:nodoc:
  27884. class SmallCache < ThreadSafe::Cache
  27885. def initialize(options = {})
  27886. super(options.merge(:initial_capacity => 2))
  27887. end
  27888. end
  27889. # preallocate all the default blocks for performance/memory consumption reasons
  27890. PARTIAL_BLOCK = lambda {|cache, partial| cache[partial] = SmallCache.new}
  27891. PREFIX_BLOCK = lambda {|cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK)}
  27892. NAME_BLOCK = lambda {|cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK)}
  27893. KEY_BLOCK = lambda {|cache, key| cache[key] = SmallCache.new(&NAME_BLOCK)}
  27894. # usually a majority of template look ups return nothing, use this canonical preallocated array to safe memory
  27895. NO_TEMPLATES = [].freeze
  27896. def initialize
  27897. @data = SmallCache.new(&KEY_BLOCK)
  27898. end
  27899. # Cache the templates returned by the block
  27900. def cache(key, name, prefix, partial, locals)
  27901. if Resolver.caching?
  27902. @data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
  27903. else
  27904. fresh_templates = yield
  27905. cached_templates = @data[key][name][prefix][partial][locals]
  27906. if templates_have_changed?(cached_templates, fresh_templates)
  27907. @data[key][name][prefix][partial][locals] = canonical_no_templates(fresh_templates)
  27908. else
  27909. cached_templates || NO_TEMPLATES
  27910. end
  27911. end
  27912. end
  27913. def clear
  27914. @data.clear
  27915. end
  27916. private
  27917. def canonical_no_templates(templates)
  27918. templates.empty? ? NO_TEMPLATES : templates
  27919. end
  27920. def templates_have_changed?(cached_templates, fresh_templates)
  27921. # if either the old or new template list is empty, we don't need to (and can't)
  27922. # compare modification times, and instead just check whether the lists are different
  27923. if cached_templates.blank? || fresh_templates.blank?
  27924. return fresh_templates.blank? != cached_templates.blank?
  27925. end
  27926. cached_templates_max_updated_at = cached_templates.map(&:updated_at).max
  27927. # if a template has changed, it will be now be newer than all the cached templates
  27928. fresh_templates.any? { |t| t.updated_at > cached_templates_max_updated_at }
  27929. end
  27930. end
  27931. cattr_accessor :caching
  27932. self.caching = true
  27933. class << self
  27934. alias :caching? :caching
  27935. end
  27936. def initialize
  27937. @cache = Cache.new
  27938. end
  27939. def clear_cache
  27940. @cache.clear
  27941. end
  27942. # Normalizes the arguments and passes it on to find_template.
  27943. def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
  27944. cached(key, [name, prefix, partial], details, locals) do
  27945. find_templates(name, prefix, partial, details)
  27946. end
  27947. end
  27948. private
  27949. delegate :caching?, to: :class
  27950. # This is what child classes implement. No defaults are needed
  27951. # because Resolver guarantees that the arguments are present and
  27952. # normalized.
  27953. def find_templates(name, prefix, partial, details)
  27954. raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details) method"
  27955. end
  27956. # Helpers that builds a path. Useful for building virtual paths.
  27957. def build_path(name, prefix, partial)
  27958. Path.build(name, prefix, partial)
  27959. end
  27960. # Handles templates caching. If a key is given and caching is on
  27961. # always check the cache before hitting the resolver. Otherwise,
  27962. # it always hits the resolver but if the key is present, check if the
  27963. # resolver is fresher before returning it.
  27964. def cached(key, path_info, details, locals) #:nodoc:
  27965. name, prefix, partial = path_info
  27966. locals = locals.map { |x| x.to_s }.sort!
  27967. if key
  27968. @cache.cache(key, name, prefix, partial, locals) do
  27969. decorate(yield, path_info, details, locals)
  27970. end
  27971. else
  27972. decorate(yield, path_info, details, locals)
  27973. end
  27974. end
  27975. # Ensures all the resolver information is set in the template.
  27976. def decorate(templates, path_info, details, locals) #:nodoc:
  27977. cached = nil
  27978. templates.each do |t|
  27979. t.locals = locals
  27980. t.formats = details[:formats] || [:html] if t.formats.empty?
  27981. t.virtual_path ||= (cached ||= build_path(*path_info))
  27982. end
  27983. end
  27984. end
  27985. # An abstract class that implements a Resolver with path semantics.
  27986. class PathResolver < Resolver #:nodoc:
  27987. EXTENSIONS = [:locale, :formats, :handlers]
  27988. DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}"
  27989. def initialize(pattern=nil)
  27990. @pattern = pattern || DEFAULT_PATTERN
  27991. super()
  27992. end
  27993. private
  27994. def find_templates(name, prefix, partial, details)
  27995. path = Path.build(name, prefix, partial)
  27996. query(path, details, details[:formats])
  27997. end
  27998. def query(path, details, formats)
  27999. query = build_query(path, details)
  28000. # deals with case-insensitive file systems.
  28001. sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] }
  28002. template_paths = Dir[query].reject { |filename|
  28003. File.directory?(filename) ||
  28004. !sanitizer[File.dirname(filename)].include?(filename)
  28005. }
  28006. template_paths.map { |template|
  28007. handler, format = extract_handler_and_format(template, formats)
  28008. contents = File.binread template
  28009. Template.new(contents, File.expand_path(template), handler,
  28010. :virtual_path => path.virtual,
  28011. :format => format,
  28012. :updated_at => mtime(template))
  28013. }
  28014. end
  28015. # Helper for building query glob string based on resolver's pattern.
  28016. def build_query(path, details)
  28017. query = @pattern.dup
  28018. prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
  28019. query.gsub!(/\:prefix(\/)?/, prefix)
  28020. partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
  28021. query.gsub!(/\:action/, partial)
  28022. details.each do |ext, variants|
  28023. query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
  28024. end
  28025. File.expand_path(query, @path)
  28026. end
  28027. def escape_entry(entry)
  28028. entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
  28029. end
  28030. # Returns the file mtime from the filesystem.
  28031. def mtime(p)
  28032. File.mtime(p)
  28033. end
  28034. # Extract handler and formats from path. If a format cannot be a found neither
  28035. # from the path, or the handler, we should return the array of formats given
  28036. # to the resolver.
  28037. def extract_handler_and_format(path, default_formats)
  28038. pieces = File.basename(path).split(".")
  28039. pieces.shift
  28040. extension = pieces.pop
  28041. unless extension
  28042. message = "The file #{path} did not specify a template handler. The default is currently ERB, " \
  28043. "but will change to RAW in the future."
  28044. ActiveSupport::Deprecation.warn message
  28045. end
  28046. handler = Template.handler_for_extension(extension)
  28047. format = pieces.last && Template::Types[pieces.last]
  28048. [handler, format]
  28049. end
  28050. end
  28051. # A resolver that loads files from the filesystem. It allows setting your own
  28052. # resolving pattern. Such pattern can be a glob string supported by some variables.
  28053. #
  28054. # ==== Examples
  28055. #
  28056. # Default pattern, loads views the same way as previous versions of rails, eg. when you're
  28057. # looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml},}`
  28058. #
  28059. # FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}")
  28060. #
  28061. # This one allows you to keep files with different formats in seperated subdirectories,
  28062. # eg. `users/new.html` will be loaded from `users/html/new.erb` or `users/new.html.erb`,
  28063. # `users/new.js` from `users/js/new.erb` or `users/new.js.erb`, etc.
  28064. #
  28065. # FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{.:handlers,}")
  28066. #
  28067. # If you don't specify a pattern then the default will be used.
  28068. #
  28069. # In order to use any of the customized resolvers above in a Rails application, you just need
  28070. # to configure ActionController::Base.view_paths in an initializer, for example:
  28071. #
  28072. # ActionController::Base.view_paths = FileSystemResolver.new(
  28073. # Rails.root.join("app/views"),
  28074. # ":prefix{/:locale}/:action{.:formats,}{.:handlers,}"
  28075. # )
  28076. #
  28077. # ==== Pattern format and variables
  28078. #
  28079. # Pattern has to be a valid glob string, and it allows you to use the
  28080. # following variables:
  28081. #
  28082. # * <tt>:prefix</tt> - usually the controller path
  28083. # * <tt>:action</tt> - name of the action
  28084. # * <tt>:locale</tt> - possible locale versions
  28085. # * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
  28086. # * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
  28087. #
  28088. class FileSystemResolver < PathResolver
  28089. def initialize(path, pattern=nil)
  28090. raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
  28091. super(pattern)
  28092. @path = File.expand_path(path)
  28093. end
  28094. def to_s
  28095. @path.to_s
  28096. end
  28097. alias :to_path :to_s
  28098. def eql?(resolver)
  28099. self.class.equal?(resolver.class) && to_path == resolver.to_path
  28100. end
  28101. alias :== :eql?
  28102. end
  28103. # An Optimized resolver for Rails' most common case.
  28104. class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
  28105. def build_query(path, details)
  28106. exts = EXTENSIONS.map { |ext| details[ext] }
  28107. query = escape_entry(File.join(@path, path))
  28108. query + exts.map { |ext|
  28109. "{#{ext.compact.uniq.map { |e| ".#{e}," }.join}}"
  28110. }.join
  28111. end
  28112. end
  28113. # The same as FileSystemResolver but does not allow templates to store
  28114. # a virtual path since it is invalid for such resolvers.
  28115. class FallbackFileSystemResolver < FileSystemResolver #:nodoc:
  28116. def self.instances
  28117. [new(""), new("/")]
  28118. end
  28119. def decorate(*)
  28120. super.each { |t| t.virtual_path = nil }
  28121. end
  28122. end
  28123. end
  28124. module ActionView #:nodoc:
  28125. # = Action View Text Template
  28126. class Template
  28127. class Text #:nodoc:
  28128. attr_accessor :type
  28129. def initialize(string, type = nil)
  28130. @string = string.to_s
  28131. @type = Types[type] || type if type
  28132. @type ||= Types[:text]
  28133. end
  28134. def identifier
  28135. 'text template'
  28136. end
  28137. def inspect
  28138. 'text template'
  28139. end
  28140. def to_str
  28141. @string
  28142. end
  28143. def render(*args)
  28144. to_str
  28145. end
  28146. def formats
  28147. [@type.to_sym]
  28148. end
  28149. end
  28150. end
  28151. end
  28152. require 'set'
  28153. require 'active_support/core_ext/class/attribute_accessors'
  28154. module ActionView
  28155. class Template
  28156. class Types
  28157. class Type
  28158. cattr_accessor :types
  28159. self.types = Set.new
  28160. def self.register(*t)
  28161. types.merge(t.map { |type| type.to_s })
  28162. end
  28163. register :html, :text, :js, :css, :xml, :json
  28164. def self.[](type)
  28165. return type if type.is_a?(self)
  28166. if type.is_a?(Symbol) || types.member?(type.to_s)
  28167. new(type)
  28168. end
  28169. end
  28170. attr_reader :symbol
  28171. def initialize(symbol)
  28172. @symbol = symbol.to_sym
  28173. end
  28174. delegate :to_s, :to_sym, :to => :symbol
  28175. alias to_str to_s
  28176. def ref
  28177. to_sym || to_s
  28178. end
  28179. def ==(type)
  28180. return false if type.blank?
  28181. symbol.to_sym == type.to_sym
  28182. end
  28183. end
  28184. cattr_accessor :type_klass
  28185. def self.delegate_to(klass)
  28186. self.type_klass = klass
  28187. end
  28188. delegate_to Type
  28189. def self.[](type)
  28190. type_klass[type]
  28191. end
  28192. end
  28193. end
  28194. end
  28195. require 'active_support/core_ext/object/try'
  28196. require 'active_support/core_ext/kernel/singleton_class'
  28197. require 'thread'
  28198. module ActionView
  28199. # = Action View Template
  28200. class Template
  28201. extend ActiveSupport::Autoload
  28202. # === Encodings in ActionView::Template
  28203. #
  28204. # ActionView::Template is one of a few sources of potential
  28205. # encoding issues in Rails. This is because the source for
  28206. # templates are usually read from disk, and Ruby (like most
  28207. # encoding-aware programming languages) assumes that the
  28208. # String retrieved through File IO is encoded in the
  28209. # <tt>default_external</tt> encoding. In Rails, the default
  28210. # <tt>default_external</tt> encoding is UTF-8.
  28211. #
  28212. # As a result, if a user saves their template as ISO-8859-1
  28213. # (for instance, using a non-Unicode-aware text editor),
  28214. # and uses characters outside of the ASCII range, their
  28215. # users will see diamonds with question marks in them in
  28216. # the browser.
  28217. #
  28218. # For the rest of this documentation, when we say "UTF-8",
  28219. # we mean "UTF-8 or whatever the default_internal encoding
  28220. # is set to". By default, it will be UTF-8.
  28221. #
  28222. # To mitigate this problem, we use a few strategies:
  28223. # 1. If the source is not valid UTF-8, we raise an exception
  28224. # when the template is compiled to alert the user
  28225. # to the problem.
  28226. # 2. The user can specify the encoding using Ruby-style
  28227. # encoding comments in any template engine. If such
  28228. # a comment is supplied, Rails will apply that encoding
  28229. # to the resulting compiled source returned by the
  28230. # template handler.
  28231. # 3. In all cases, we transcode the resulting String to
  28232. # the UTF-8.
  28233. #
  28234. # This means that other parts of Rails can always assume
  28235. # that templates are encoded in UTF-8, even if the original
  28236. # source of the template was not UTF-8.
  28237. #
  28238. # From a user's perspective, the easiest thing to do is
  28239. # to save your templates as UTF-8. If you do this, you
  28240. # do not need to do anything else for things to "just work".
  28241. #
  28242. # === Instructions for template handlers
  28243. #
  28244. # The easiest thing for you to do is to simply ignore
  28245. # encodings. Rails will hand you the template source
  28246. # as the default_internal (generally UTF-8), raising
  28247. # an exception for the user before sending the template
  28248. # to you if it could not determine the original encoding.
  28249. #
  28250. # For the greatest simplicity, you can support only
  28251. # UTF-8 as the <tt>default_internal</tt>. This means
  28252. # that from the perspective of your handler, the
  28253. # entire pipeline is just UTF-8.
  28254. #
  28255. # === Advanced: Handlers with alternate metadata sources
  28256. #
  28257. # If you want to provide an alternate mechanism for
  28258. # specifying encodings (like ERB does via <%# encoding: ... %>),
  28259. # you may indicate that you will handle encodings yourself
  28260. # by implementing <tt>self.handles_encoding?</tt>
  28261. # on your handler.
  28262. #
  28263. # If you do, Rails will not try to encode the String
  28264. # into the default_internal, passing you the unaltered
  28265. # bytes tagged with the assumed encoding (from
  28266. # default_external).
  28267. #
  28268. # In this case, make sure you return a String from
  28269. # your handler encoded in the default_internal. Since
  28270. # you are handling out-of-band metadata, you are
  28271. # also responsible for alerting the user to any
  28272. # problems with converting the user's data to
  28273. # the <tt>default_internal</tt>.
  28274. #
  28275. # To do so, simply raise +WrongEncodingError+ as follows:
  28276. #
  28277. # raise WrongEncodingError.new(
  28278. # problematic_string,
  28279. # expected_encoding
  28280. # )
  28281. eager_autoload do
  28282. autoload :Error
  28283. autoload :Handlers
  28284. autoload :Text
  28285. autoload :Types
  28286. end
  28287. extend Template::Handlers
  28288. attr_accessor :locals, :formats, :virtual_path
  28289. attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
  28290. # This finalizer is needed (and exactly with a proc inside another proc)
  28291. # otherwise templates leak in development.
  28292. Finalizer = proc do |method_name, mod|
  28293. proc do
  28294. mod.module_eval do
  28295. remove_possible_method method_name
  28296. end
  28297. end
  28298. end
  28299. def initialize(source, identifier, handler, details)
  28300. format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
  28301. @source = source
  28302. @identifier = identifier
  28303. @handler = handler
  28304. @compiled = false
  28305. @original_encoding = nil
  28306. @locals = details[:locals] || []
  28307. @virtual_path = details[:virtual_path]
  28308. @updated_at = details[:updated_at] || Time.now
  28309. @formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
  28310. @compile_mutex = Mutex.new
  28311. end
  28312. # Returns if the underlying handler supports streaming. If so,
  28313. # a streaming buffer *may* be passed when it start rendering.
  28314. def supports_streaming?
  28315. handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
  28316. end
  28317. # Render a template. If the template was not compiled yet, it is done
  28318. # exactly before rendering.
  28319. #
  28320. # This method is instrumented as "!render_template.action_view". Notice that
  28321. # we use a bang in this instrumentation because you don't want to
  28322. # consume this in production. This is only slow if it's being listened to.
  28323. def render(view, locals, buffer=nil, &block)
  28324. ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
  28325. compile!(view)
  28326. view.send(method_name, locals, buffer, &block)
  28327. end
  28328. rescue Exception => e
  28329. handle_render_error(view, e)
  28330. end
  28331. def mime_type
  28332. message = 'Template#mime_type is deprecated and will be removed in Rails 4.1. Please use type method instead.'
  28333. ActiveSupport::Deprecation.warn message
  28334. @mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
  28335. end
  28336. def type
  28337. @type ||= Types[@formats.first] if @formats.first
  28338. end
  28339. # Receives a view object and return a template similar to self by using @virtual_path.
  28340. #
  28341. # This method is useful if you have a template object but it does not contain its source
  28342. # anymore since it was already compiled. In such cases, all you need to do is to call
  28343. # refresh passing in the view object.
  28344. #
  28345. # Notice this method raises an error if the template to be refreshed does not have a
  28346. # virtual path set (true just for inline templates).
  28347. def refresh(view)
  28348. raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
  28349. lookup = view.lookup_context
  28350. pieces = @virtual_path.split("/")
  28351. name = pieces.pop
  28352. partial = !!name.sub!(/^_/, "")
  28353. lookup.disable_cache do
  28354. lookup.find_template(name, [ pieces.join('/') ], partial, @locals)
  28355. end
  28356. end
  28357. def inspect
  28358. @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
  28359. end
  28360. # This method is responsible for properly setting the encoding of the
  28361. # source. Until this point, we assume that the source is BINARY data.
  28362. # If no additional information is supplied, we assume the encoding is
  28363. # the same as <tt>Encoding.default_external</tt>.
  28364. #
  28365. # The user can also specify the encoding via a comment on the first
  28366. # line of the template (# encoding: NAME-OF-ENCODING). This will work
  28367. # with any template engine, as we process out the encoding comment
  28368. # before passing the source on to the template engine, leaving a
  28369. # blank line in its stead.
  28370. def encode!
  28371. return unless source.encoding == Encoding::BINARY
  28372. # Look for # encoding: *. If we find one, we'll encode the
  28373. # String in that encoding, otherwise, we'll use the
  28374. # default external encoding.
  28375. if source.sub!(/\A#{ENCODING_FLAG}/, '')
  28376. encoding = magic_encoding = $1
  28377. else
  28378. encoding = Encoding.default_external
  28379. end
  28380. # Tag the source with the default external encoding
  28381. # or the encoding specified in the file
  28382. source.force_encoding(encoding)
  28383. # If the user didn't specify an encoding, and the handler
  28384. # handles encodings, we simply pass the String as is to
  28385. # the handler (with the default_external tag)
  28386. if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
  28387. source
  28388. # Otherwise, if the String is valid in the encoding,
  28389. # encode immediately to default_internal. This means
  28390. # that if a handler doesn't handle encodings, it will
  28391. # always get Strings in the default_internal
  28392. elsif source.valid_encoding?
  28393. source.encode!
  28394. # Otherwise, since the String is invalid in the encoding
  28395. # specified, raise an exception
  28396. else
  28397. raise WrongEncodingError.new(source, encoding)
  28398. end
  28399. end
  28400. protected
  28401. # Compile a template. This method ensures a template is compiled
  28402. # just once and removes the source after it is compiled.
  28403. def compile!(view) #:nodoc:
  28404. return if @compiled
  28405. # Templates can be used concurrently in threaded environments
  28406. # so compilation and any instance variable modification must
  28407. # be synchronized
  28408. @compile_mutex.synchronize do
  28409. # Any thread holding this lock will be compiling the template needed
  28410. # by the threads waiting. So re-check the @compiled flag to avoid
  28411. # re-compilation
  28412. return if @compiled
  28413. if view.is_a?(ActionView::CompiledTemplates)
  28414. mod = ActionView::CompiledTemplates
  28415. else
  28416. mod = view.singleton_class
  28417. end
  28418. compile(view, mod)
  28419. # Just discard the source if we have a virtual path. This
  28420. # means we can get the template back.
  28421. @source = nil if @virtual_path
  28422. @compiled = true
  28423. end
  28424. end
  28425. # Among other things, this method is responsible for properly setting
  28426. # the encoding of the compiled template.
  28427. #
  28428. # If the template engine handles encodings, we send the encoded
  28429. # String to the engine without further processing. This allows
  28430. # the template engine to support additional mechanisms for
  28431. # specifying the encoding. For instance, ERB supports <%# encoding: %>
  28432. #
  28433. # Otherwise, after we figure out the correct encoding, we then
  28434. # encode the source into <tt>Encoding.default_internal</tt>.
  28435. # In general, this means that templates will be UTF-8 inside of Rails,
  28436. # regardless of the original source encoding.
  28437. def compile(view, mod) #:nodoc:
  28438. encode!
  28439. method_name = self.method_name
  28440. code = @handler.call(self)
  28441. # Make sure that the resulting String to be evalled is in the
  28442. # encoding of the code
  28443. source = <<-end_src
  28444. def #{method_name}(local_assigns, output_buffer)
  28445. _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
  28446. ensure
  28447. @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
  28448. end
  28449. end_src
  28450. # Make sure the source is in the encoding of the returned code
  28451. source.force_encoding(code.encoding)
  28452. # In case we get back a String from a handler that is not in
  28453. # BINARY or the default_internal, encode it to the default_internal
  28454. source.encode!
  28455. # Now, validate that the source we got back from the template
  28456. # handler is valid in the default_internal. This is for handlers
  28457. # that handle encoding but screw up
  28458. unless source.valid_encoding?
  28459. raise WrongEncodingError.new(@source, Encoding.default_internal)
  28460. end
  28461. begin
  28462. mod.module_eval(source, identifier, 0)
  28463. ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
  28464. rescue Exception => e # errors from template code
  28465. if logger = (view && view.logger)
  28466. logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
  28467. logger.debug "Function body: #{source}"
  28468. logger.debug "Backtrace: #{e.backtrace.join("\n")}"
  28469. end
  28470. raise ActionView::Template::Error.new(self, e)
  28471. end
  28472. end
  28473. def handle_render_error(view, e) #:nodoc:
  28474. if e.is_a?(Template::Error)
  28475. e.sub_template_of(self)
  28476. raise e
  28477. else
  28478. template = self
  28479. unless template.source
  28480. template = refresh(view)
  28481. template.encode!
  28482. end
  28483. raise Template::Error.new(template, e)
  28484. end
  28485. end
  28486. def locals_code #:nodoc:
  28487. @locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
  28488. end
  28489. def method_name #:nodoc:
  28490. @method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
  28491. end
  28492. def identifier_method_name #:nodoc:
  28493. inspect.gsub(/[^a-z_]/, '_')
  28494. end
  28495. end
  28496. end
  28497. require 'active_support/core_ext/module/remove_method'
  28498. require 'action_controller'
  28499. require 'action_controller/test_case'
  28500. require 'action_view'
  28501. module ActionView
  28502. # = Action View Test Case
  28503. class TestCase < ActiveSupport::TestCase
  28504. class TestController < ActionController::Base
  28505. include ActionDispatch::TestProcess
  28506. attr_accessor :request, :response, :params
  28507. class << self
  28508. attr_writer :controller_path
  28509. end
  28510. def controller_path=(path)
  28511. self.class.controller_path=(path)
  28512. end
  28513. def initialize
  28514. super
  28515. self.class.controller_path = ""
  28516. @request = ActionController::TestRequest.new
  28517. @response = ActionController::TestResponse.new
  28518. @request.env.delete('PATH_INFO')
  28519. @params = {}
  28520. end
  28521. end
  28522. module Behavior
  28523. extend ActiveSupport::Concern
  28524. include ActionDispatch::Assertions, ActionDispatch::TestProcess
  28525. include ActionController::TemplateAssertions
  28526. include ActionView::Context
  28527. include ActionDispatch::Routing::PolymorphicRoutes
  28528. include AbstractController::Helpers
  28529. include ActionView::Helpers
  28530. include ActionView::RecordIdentifier
  28531. include ActionView::RoutingUrlFor
  28532. include ActiveSupport::Testing::ConstantLookup
  28533. delegate :lookup_context, :to => :controller
  28534. attr_accessor :controller, :output_buffer, :rendered
  28535. module ClassMethods
  28536. def tests(helper_class)
  28537. case helper_class
  28538. when String, Symbol
  28539. self.helper_class = "#{helper_class.to_s.underscore}_helper".camelize.safe_constantize
  28540. when Module
  28541. self.helper_class = helper_class
  28542. end
  28543. end
  28544. def determine_default_helper_class(name)
  28545. determine_constant_from_test_name(name) do |constant|
  28546. Module === constant && !(Class === constant)
  28547. end
  28548. end
  28549. def helper_method(*methods)
  28550. # Almost a duplicate from ActionController::Helpers
  28551. methods.flatten.each do |method|
  28552. _helpers.module_eval <<-end_eval
  28553. def #{method}(*args, &block) # def current_user(*args, &block)
  28554. _test_case.send(%(#{method}), *args, &block) # _test_case.send(%(current_user), *args, &block)
  28555. end # end
  28556. end_eval
  28557. end
  28558. end
  28559. attr_writer :helper_class
  28560. def helper_class
  28561. @helper_class ||= determine_default_helper_class(name)
  28562. end
  28563. def new(*)
  28564. include_helper_modules!
  28565. super
  28566. end
  28567. private
  28568. def include_helper_modules!
  28569. helper(helper_class) if helper_class
  28570. include _helpers
  28571. end
  28572. end
  28573. def setup_with_controller
  28574. @controller = ActionView::TestCase::TestController.new
  28575. @request = @controller.request
  28576. @output_buffer = ActiveSupport::SafeBuffer.new
  28577. @rendered = ''
  28578. make_test_case_available_to_view!
  28579. say_no_to_protect_against_forgery!
  28580. end
  28581. def config
  28582. @controller.config if @controller.respond_to?(:config)
  28583. end
  28584. def render(options = {}, local_assigns = {}, &block)
  28585. view.assign(view_assigns)
  28586. @rendered << output = view.render(options, local_assigns, &block)
  28587. output
  28588. end
  28589. def rendered_views
  28590. @_rendered_views ||= RenderedViewsCollection.new
  28591. end
  28592. class RenderedViewsCollection
  28593. def initialize
  28594. @rendered_views ||= Hash.new { |hash, key| hash[key] = [] }
  28595. end
  28596. def add(view, locals)
  28597. @rendered_views[view] ||= []
  28598. @rendered_views[view] << locals
  28599. end
  28600. def locals_for(view)
  28601. @rendered_views[view]
  28602. end
  28603. def rendered_views
  28604. @rendered_views.keys
  28605. end
  28606. def view_rendered?(view, expected_locals)
  28607. locals_for(view).any? do |actual_locals|
  28608. expected_locals.all? {|key, value| value == actual_locals[key] }
  28609. end
  28610. end
  28611. end
  28612. included do
  28613. setup :setup_with_controller
  28614. end
  28615. private
  28616. # Support the selector assertions
  28617. #
  28618. # Need to experiment if this priority is the best one: rendered => output_buffer
  28619. def response_from_page
  28620. HTML::Document.new(@rendered.blank? ? @output_buffer : @rendered).root
  28621. end
  28622. def say_no_to_protect_against_forgery!
  28623. _helpers.module_eval do
  28624. remove_possible_method :protect_against_forgery?
  28625. def protect_against_forgery?
  28626. false
  28627. end
  28628. end
  28629. end
  28630. def make_test_case_available_to_view!
  28631. test_case_instance = self
  28632. _helpers.module_eval do
  28633. unless private_method_defined?(:_test_case)
  28634. define_method(:_test_case) { test_case_instance }
  28635. private :_test_case
  28636. end
  28637. end
  28638. end
  28639. module Locals
  28640. attr_accessor :rendered_views
  28641. def render(options = {}, local_assigns = {})
  28642. case options
  28643. when Hash
  28644. if block_given?
  28645. rendered_views.add options[:layout], options[:locals]
  28646. elsif options.key?(:partial)
  28647. rendered_views.add options[:partial], options[:locals]
  28648. end
  28649. else
  28650. rendered_views.add options, local_assigns
  28651. end
  28652. super
  28653. end
  28654. end
  28655. # The instance of ActionView::Base that is used by +render+.
  28656. def view
  28657. @view ||= begin
  28658. view = @controller.view_context
  28659. view.singleton_class.send :include, _helpers
  28660. view.extend(Locals)
  28661. view.rendered_views = self.rendered_views
  28662. view.output_buffer = self.output_buffer
  28663. view
  28664. end
  28665. end
  28666. alias_method :_view, :view
  28667. INTERNAL_IVARS = [
  28668. :@__name__,
  28669. :@__io__,
  28670. :@_assertion_wrapped,
  28671. :@_assertions,
  28672. :@_result,
  28673. :@_routes,
  28674. :@controller,
  28675. :@_layouts,
  28676. :@_rendered_views,
  28677. :@method_name,
  28678. :@output_buffer,
  28679. :@_partials,
  28680. :@passed,
  28681. :@rendered,
  28682. :@request,
  28683. :@routes,
  28684. :@tagged_logger,
  28685. :@_templates,
  28686. :@options,
  28687. :@test_passed,
  28688. :@view,
  28689. :@view_context_class
  28690. ]
  28691. def _user_defined_ivars
  28692. instance_variables - INTERNAL_IVARS
  28693. end
  28694. # Returns a Hash of instance variables and their values, as defined by
  28695. # the user in the test case, which are then assigned to the view being
  28696. # rendered. This is generally intended for internal use and extension
  28697. # frameworks.
  28698. def view_assigns
  28699. Hash[_user_defined_ivars.map do |ivar|
  28700. [ivar[1..-1].to_sym, instance_variable_get(ivar)]
  28701. end]
  28702. end
  28703. def _routes
  28704. @controller._routes if @controller.respond_to?(:_routes)
  28705. end
  28706. def method_missing(selector, *args)
  28707. if @controller.respond_to?(:_routes) &&
  28708. ( @controller._routes.named_routes.helpers.include?(selector) ||
  28709. @controller._routes.mounted_helpers.method_defined?(selector) )
  28710. @controller.__send__(selector, *args)
  28711. else
  28712. super
  28713. end
  28714. end
  28715. end
  28716. include Behavior
  28717. end
  28718. end
  28719. require 'action_view/template/resolver'
  28720. module ActionView #:nodoc:
  28721. # Use FixtureResolver in your tests to simulate the presence of files on the
  28722. # file system. This is used internally by Rails' own test suite, and is
  28723. # useful for testing extensions that have no way of knowing what the file
  28724. # system will look like at runtime.
  28725. class FixtureResolver < PathResolver
  28726. attr_reader :hash
  28727. def initialize(hash = {}, pattern=nil)
  28728. super(pattern)
  28729. @hash = hash
  28730. end
  28731. def to_s
  28732. @hash.keys.join(', ')
  28733. end
  28734. private
  28735. def query(path, exts, formats)
  28736. query = ""
  28737. EXTENSIONS.each do |ext|
  28738. query << '(' << exts[ext].map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
  28739. end
  28740. query = /^(#{Regexp.escape(path)})#{query}$/
  28741. templates = []
  28742. @hash.each do |_path, array|
  28743. source, updated_at = array
  28744. next unless _path =~ query
  28745. handler, format = extract_handler_and_format(_path, formats)
  28746. templates << Template.new(source, _path, handler,
  28747. :virtual_path => path.virtual, :format => format, :updated_at => updated_at)
  28748. end
  28749. templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
  28750. end
  28751. end
  28752. class NullResolver < PathResolver
  28753. def query(path, exts, formats)
  28754. handler, format = extract_handler_and_format(path, formats)
  28755. [ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format)]
  28756. end
  28757. end
  28758. end
  28759. require 'html/tokenizer'
  28760. require 'html/node'
  28761. require 'html/selector'
  28762. require 'html/sanitizer'
  28763. module HTML #:nodoc:
  28764. # A top-level HTML document. You give it a body of text, and it will parse that
  28765. # text into a tree of nodes.
  28766. class Document #:nodoc:
  28767. # The root of the parsed document.
  28768. attr_reader :root
  28769. # Create a new Document from the given text.
  28770. def initialize(text, strict=false, xml=false)
  28771. tokenizer = Tokenizer.new(text)
  28772. @root = Node.new(nil)
  28773. node_stack = [ @root ]
  28774. while token = tokenizer.next
  28775. node = Node.parse(node_stack.last, tokenizer.line, tokenizer.position, token, strict)
  28776. node_stack.last.children << node unless node.tag? && node.closing == :close
  28777. if node.tag?
  28778. if node_stack.length > 1 && node.closing == :close
  28779. if node_stack.last.name == node.name
  28780. if node_stack.last.children.empty?
  28781. node_stack.last.children << Text.new(node_stack.last, node.line, node.position, "")
  28782. end
  28783. node_stack.pop
  28784. else
  28785. open_start = node_stack.last.position - 20
  28786. open_start = 0 if open_start < 0
  28787. close_start = node.position - 20
  28788. close_start = 0 if close_start < 0
  28789. msg = <<EOF.strip
  28790. ignoring attempt to close #{node_stack.last.name} with #{node.name}
  28791. opened at byte #{node_stack.last.position}, line #{node_stack.last.line}
  28792. closed at byte #{node.position}, line #{node.line}
  28793. attributes at open: #{node_stack.last.attributes.inspect}
  28794. text around open: #{text[open_start,40].inspect}
  28795. text around close: #{text[close_start,40].inspect}
  28796. EOF
  28797. strict ? raise(msg) : warn(msg)
  28798. end
  28799. elsif !node.childless?(xml) && node.closing != :close
  28800. node_stack.push node
  28801. end
  28802. end
  28803. end
  28804. end
  28805. # Search the tree for (and return) the first node that matches the given
  28806. # conditions. The conditions are interpreted differently for different node
  28807. # types, see HTML::Text#find and HTML::Tag#find.
  28808. def find(conditions)
  28809. @root.find(conditions)
  28810. end
  28811. # Search the tree for (and return) all nodes that match the given
  28812. # conditions. The conditions are interpreted differently for different node
  28813. # types, see HTML::Text#find and HTML::Tag#find.
  28814. def find_all(conditions)
  28815. @root.find_all(conditions)
  28816. end
  28817. end
  28818. end
  28819. require 'strscan'
  28820. module HTML #:nodoc:
  28821. class Conditions < Hash #:nodoc:
  28822. def initialize(hash)
  28823. super()
  28824. hash = { :content => hash } unless Hash === hash
  28825. hash = keys_to_symbols(hash)
  28826. hash.each do |k,v|
  28827. case k
  28828. when :tag, :content then
  28829. # keys are valid, and require no further processing
  28830. when :attributes then
  28831. hash[k] = keys_to_strings(v)
  28832. when :parent, :child, :ancestor, :descendant, :sibling, :before,
  28833. :after
  28834. hash[k] = Conditions.new(v)
  28835. when :children
  28836. hash[k] = v = keys_to_symbols(v)
  28837. v.each do |key,value|
  28838. case key
  28839. when :count, :greater_than, :less_than
  28840. # keys are valid, and require no further processing
  28841. when :only
  28842. v[key] = Conditions.new(value)
  28843. else
  28844. raise "illegal key #{key.inspect} => #{value.inspect}"
  28845. end
  28846. end
  28847. else
  28848. raise "illegal key #{k.inspect} => #{v.inspect}"
  28849. end
  28850. end
  28851. update hash
  28852. end
  28853. private
  28854. def keys_to_strings(hash)
  28855. Hash[hash.keys.map {|k| [k.to_s, hash[k]]}]
  28856. end
  28857. def keys_to_symbols(hash)
  28858. Hash[hash.keys.map do |k|
  28859. raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
  28860. [k.to_sym, hash[k]]
  28861. end]
  28862. end
  28863. end
  28864. # The base class of all nodes, textual and otherwise, in an HTML document.
  28865. class Node #:nodoc:
  28866. # The array of children of this node. Not all nodes have children.
  28867. attr_reader :children
  28868. # The parent node of this node. All nodes have a parent, except for the
  28869. # root node.
  28870. attr_reader :parent
  28871. # The line number of the input where this node was begun
  28872. attr_reader :line
  28873. # The byte position in the input where this node was begun
  28874. attr_reader :position
  28875. # Create a new node as a child of the given parent.
  28876. def initialize(parent, line=0, pos=0)
  28877. @parent = parent
  28878. @children = []
  28879. @line, @position = line, pos
  28880. end
  28881. # Return a textual representation of the node.
  28882. def to_s
  28883. @children.join()
  28884. end
  28885. # Return false (subclasses must override this to provide specific matching
  28886. # behavior.) +conditions+ may be of any type.
  28887. def match(conditions)
  28888. false
  28889. end
  28890. # Search the children of this node for the first node for which #find
  28891. # returns non +nil+. Returns the result of the #find call that succeeded.
  28892. def find(conditions)
  28893. conditions = validate_conditions(conditions)
  28894. @children.each do |child|
  28895. node = child.find(conditions)
  28896. return node if node
  28897. end
  28898. nil
  28899. end
  28900. # Search for all nodes that match the given conditions, and return them
  28901. # as an array.
  28902. def find_all(conditions)
  28903. conditions = validate_conditions(conditions)
  28904. matches = []
  28905. matches << self if match(conditions)
  28906. @children.each do |child|
  28907. matches.concat child.find_all(conditions)
  28908. end
  28909. matches
  28910. end
  28911. # Returns +false+. Subclasses may override this if they define a kind of
  28912. # tag.
  28913. def tag?
  28914. false
  28915. end
  28916. def validate_conditions(conditions)
  28917. Conditions === conditions ? conditions : Conditions.new(conditions)
  28918. end
  28919. def ==(node)
  28920. return false unless self.class == node.class && children.size == node.children.size
  28921. equivalent = true
  28922. children.size.times do |i|
  28923. equivalent &&= children[i] == node.children[i]
  28924. end
  28925. equivalent
  28926. end
  28927. class <<self
  28928. def parse(parent, line, pos, content, strict=true)
  28929. if content !~ /^<\S/
  28930. Text.new(parent, line, pos, content)
  28931. else
  28932. scanner = StringScanner.new(content)
  28933. unless scanner.skip(/</)
  28934. if strict
  28935. raise "expected <"
  28936. else
  28937. return Text.new(parent, line, pos, content)
  28938. end
  28939. end
  28940. if scanner.skip(/!\[CDATA\[/)
  28941. unless scanner.skip_until(/\]\]>/)
  28942. if strict
  28943. raise "expected ]]> (got #{scanner.rest.inspect} for #{content})"
  28944. else
  28945. scanner.skip_until(/\Z/)
  28946. end
  28947. end
  28948. return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
  28949. end
  28950. closing = ( scanner.scan(/\//) ? :close : nil )
  28951. return Text.new(parent, line, pos, content) unless name = scanner.scan(/[^\s!>\/]+/)
  28952. name.downcase!
  28953. unless closing
  28954. scanner.skip(/\s*/)
  28955. attributes = {}
  28956. while attr = scanner.scan(/[-\w:]+/)
  28957. value = true
  28958. if scanner.scan(/\s*=\s*/)
  28959. if delim = scanner.scan(/['"]/)
  28960. value = ""
  28961. while text = scanner.scan(/[^#{delim}\\]+|./)
  28962. case text
  28963. when "\\" then
  28964. value << text
  28965. break if scanner.eos?
  28966. value << scanner.getch
  28967. when delim
  28968. break
  28969. else value << text
  28970. end
  28971. end
  28972. else
  28973. value = scanner.scan(/[^\s>\/]+/)
  28974. end
  28975. end
  28976. attributes[attr.downcase] = value
  28977. scanner.skip(/\s*/)
  28978. end
  28979. closing = ( scanner.scan(/\//) ? :self : nil )
  28980. end
  28981. unless scanner.scan(/\s*>/)
  28982. if strict
  28983. raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
  28984. else
  28985. # throw away all text until we find what we're looking for
  28986. scanner.skip_until(/>/) or scanner.terminate
  28987. end
  28988. end
  28989. Tag.new(parent, line, pos, name, attributes, closing)
  28990. end
  28991. end
  28992. end
  28993. end
  28994. # A node that represents text, rather than markup.
  28995. class Text < Node #:nodoc:
  28996. attr_reader :content
  28997. # Creates a new text node as a child of the given parent, with the given
  28998. # content.
  28999. def initialize(parent, line, pos, content)
  29000. super(parent, line, pos)
  29001. @content = content
  29002. end
  29003. # Returns the content of this node.
  29004. def to_s
  29005. @content
  29006. end
  29007. # Returns +self+ if this node meets the given conditions. Text nodes support
  29008. # conditions of the following kinds:
  29009. #
  29010. # * if +conditions+ is a string, it must be a substring of the node's
  29011. # content
  29012. # * if +conditions+ is a regular expression, it must match the node's
  29013. # content
  29014. # * if +conditions+ is a hash, it must contain a <tt>:content</tt> key that
  29015. # is either a string or a regexp, and which is interpreted as described
  29016. # above.
  29017. def find(conditions)
  29018. match(conditions) && self
  29019. end
  29020. # Returns non-+nil+ if this node meets the given conditions, or +nil+
  29021. # otherwise. See the discussion of #find for the valid conditions.
  29022. def match(conditions)
  29023. case conditions
  29024. when String
  29025. @content == conditions
  29026. when Regexp
  29027. @content =~ conditions
  29028. when Hash
  29029. conditions = validate_conditions(conditions)
  29030. # Text nodes only have :content, :parent, :ancestor
  29031. unless (conditions.keys - [:content, :parent, :ancestor]).empty?
  29032. return false
  29033. end
  29034. match(conditions[:content])
  29035. else
  29036. nil
  29037. end
  29038. end
  29039. def ==(node)
  29040. return false unless super
  29041. content == node.content
  29042. end
  29043. end
  29044. # A CDATA node is simply a text node with a specialized way of displaying
  29045. # itself.
  29046. class CDATA < Text #:nodoc:
  29047. def to_s
  29048. "<![CDATA[#{super}]]>"
  29049. end
  29050. end
  29051. # A Tag is any node that represents markup. It may be an opening tag, a
  29052. # closing tag, or a self-closing tag. It has a name, and may have a hash of
  29053. # attributes.
  29054. class Tag < Node #:nodoc:
  29055. # Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
  29056. attr_reader :closing
  29057. # Either +nil+, or a hash of attributes for this node.
  29058. attr_reader :attributes
  29059. # The name of this tag.
  29060. attr_reader :name
  29061. # Create a new node as a child of the given parent, using the given content
  29062. # to describe the node. It will be parsed and the node name, attributes and
  29063. # closing status extracted.
  29064. def initialize(parent, line, pos, name, attributes, closing)
  29065. super(parent, line, pos)
  29066. @name = name
  29067. @attributes = attributes
  29068. @closing = closing
  29069. end
  29070. # A convenience for obtaining an attribute of the node. Returns +nil+ if
  29071. # the node has no attributes.
  29072. def [](attr)
  29073. @attributes ? @attributes[attr] : nil
  29074. end
  29075. # Returns non-+nil+ if this tag can contain child nodes.
  29076. def childless?(xml = false)
  29077. return false if xml && @closing.nil?
  29078. !@closing.nil? ||
  29079. @name =~ /^(img|br|hr|link|meta|area|base|basefont|
  29080. col|frame|input|isindex|param)$/ox
  29081. end
  29082. # Returns a textual representation of the node
  29083. def to_s
  29084. if @closing == :close
  29085. "</#{@name}>"
  29086. else
  29087. s = "<#{@name}"
  29088. @attributes.each do |k,v|
  29089. s << " #{k}"
  29090. s << "=\"#{v}\"" if String === v
  29091. end
  29092. s << " /" if @closing == :self
  29093. s << ">"
  29094. @children.each { |child| s << child.to_s }
  29095. s << "</#{@name}>" if @closing != :self && !@children.empty?
  29096. s
  29097. end
  29098. end
  29099. # If either the node or any of its children meet the given conditions, the
  29100. # matching node is returned. Otherwise, +nil+ is returned. (See the
  29101. # description of the valid conditions in the +match+ method.)
  29102. def find(conditions)
  29103. match(conditions) && self || super
  29104. end
  29105. # Returns +true+, indicating that this node represents an HTML tag.
  29106. def tag?
  29107. true
  29108. end
  29109. # Returns +true+ if the node meets any of the given conditions. The
  29110. # +conditions+ parameter must be a hash of any of the following keys
  29111. # (all are optional):
  29112. #
  29113. # * <tt>:tag</tt>: the node name must match the corresponding value
  29114. # * <tt>:attributes</tt>: a hash. The node's values must match the
  29115. # corresponding values in the hash.
  29116. # * <tt>:parent</tt>: a hash. The node's parent must match the
  29117. # corresponding hash.
  29118. # * <tt>:child</tt>: a hash. At least one of the node's immediate children
  29119. # must meet the criteria described by the hash.
  29120. # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
  29121. # meet the criteria described by the hash.
  29122. # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
  29123. # must meet the criteria described by the hash.
  29124. # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
  29125. # meet the criteria described by the hash.
  29126. # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
  29127. # the criteria described by the hash, and at least one sibling must match.
  29128. # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
  29129. # the criteria described by the hash, and at least one sibling must match.
  29130. # * <tt>:children</tt>: a hash, for counting children of a node. Accepts the
  29131. # keys:
  29132. # ** <tt>:count</tt>: either a number or a range which must equal (or
  29133. # include) the number of children that match.
  29134. # ** <tt>:less_than</tt>: the number of matching children must be less than
  29135. # this number.
  29136. # ** <tt>:greater_than</tt>: the number of matching children must be
  29137. # greater than this number.
  29138. # ** <tt>:only</tt>: another hash consisting of the keys to use
  29139. # to match on the children, and only matching children will be
  29140. # counted.
  29141. #
  29142. # Conditions are matched using the following algorithm:
  29143. #
  29144. # * if the condition is a string, it must be a substring of the value.
  29145. # * if the condition is a regexp, it must match the value.
  29146. # * if the condition is a number, the value must match number.to_s.
  29147. # * if the condition is +true+, the value must not be +nil+.
  29148. # * if the condition is +false+ or +nil+, the value must be +nil+.
  29149. #
  29150. # Usage:
  29151. #
  29152. # # test if the node is a "span" tag
  29153. # node.match tag: "span"
  29154. #
  29155. # # test if the node's parent is a "div"
  29156. # node.match parent: { tag: "div" }
  29157. #
  29158. # # test if any of the node's ancestors are "table" tags
  29159. # node.match ancestor: { tag: "table" }
  29160. #
  29161. # # test if any of the node's immediate children are "em" tags
  29162. # node.match child: { tag: "em" }
  29163. #
  29164. # # test if any of the node's descendants are "strong" tags
  29165. # node.match descendant: { tag: "strong" }
  29166. #
  29167. # # test if the node has between 2 and 4 span tags as immediate children
  29168. # node.match children: { count: 2..4, only: { tag: "span" } }
  29169. #
  29170. # # get funky: test to see if the node is a "div", has a "ul" ancestor
  29171. # # and an "li" parent (with "class" = "enum"), and whether or not it has
  29172. # # a "span" descendant that contains # text matching /hello world/:
  29173. # node.match tag: "div",
  29174. # ancestor: { tag: "ul" },
  29175. # parent: { tag: "li",
  29176. # attributes: { class: "enum" } },
  29177. # descendant: { tag: "span",
  29178. # child: /hello world/ }
  29179. def match(conditions)
  29180. conditions = validate_conditions(conditions)
  29181. # check content of child nodes
  29182. if conditions[:content]
  29183. if children.empty?
  29184. return false unless match_condition("", conditions[:content])
  29185. else
  29186. return false unless children.find { |child| child.match(conditions[:content]) }
  29187. end
  29188. end
  29189. # test the name
  29190. return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]
  29191. # test attributes
  29192. (conditions[:attributes] || {}).each do |key, value|
  29193. return false unless match_condition(self[key], value)
  29194. end
  29195. # test parent
  29196. return false unless parent.match(conditions[:parent]) if conditions[:parent]
  29197. # test children
  29198. return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
  29199. # test ancestors
  29200. if conditions[:ancestor]
  29201. return false unless catch :found do
  29202. p = self
  29203. throw :found, true if p.match(conditions[:ancestor]) while p = p.parent
  29204. end
  29205. end
  29206. # test descendants
  29207. if conditions[:descendant]
  29208. return false unless children.find do |child|
  29209. # test the child
  29210. child.match(conditions[:descendant]) ||
  29211. # test the child's descendants
  29212. child.match(:descendant => conditions[:descendant])
  29213. end
  29214. end
  29215. # count children
  29216. if opts = conditions[:children]
  29217. matches = children.select do |c|
  29218. (c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
  29219. end
  29220. matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
  29221. opts.each do |key, value|
  29222. next if key == :only
  29223. case key
  29224. when :count
  29225. if Integer === value
  29226. return false if matches.length != value
  29227. else
  29228. return false unless value.include?(matches.length)
  29229. end
  29230. when :less_than
  29231. return false unless matches.length < value
  29232. when :greater_than
  29233. return false unless matches.length > value
  29234. else raise "unknown count condition #{key}"
  29235. end
  29236. end
  29237. end
  29238. # test siblings
  29239. if conditions[:sibling] || conditions[:before] || conditions[:after]
  29240. siblings = parent ? parent.children : []
  29241. self_index = siblings.index(self)
  29242. if conditions[:sibling]
  29243. return false unless siblings.detect do |s|
  29244. s != self && s.match(conditions[:sibling])
  29245. end
  29246. end
  29247. if conditions[:before]
  29248. return false unless siblings[self_index+1..-1].detect do |s|
  29249. s != self && s.match(conditions[:before])
  29250. end
  29251. end
  29252. if conditions[:after]
  29253. return false unless siblings[0,self_index].detect do |s|
  29254. s != self && s.match(conditions[:after])
  29255. end
  29256. end
  29257. end
  29258. true
  29259. end
  29260. def ==(node)
  29261. return false unless super
  29262. return false unless closing == node.closing && self.name == node.name
  29263. attributes == node.attributes
  29264. end
  29265. private
  29266. # Match the given value to the given condition.
  29267. def match_condition(value, condition)
  29268. case condition
  29269. when String
  29270. value && value == condition
  29271. when Regexp
  29272. value && value.match(condition)
  29273. when Numeric
  29274. value == condition.to_s
  29275. when true
  29276. !value.nil?
  29277. when false, nil
  29278. value.nil?
  29279. else
  29280. false
  29281. end
  29282. end
  29283. end
  29284. end
  29285. require 'set'
  29286. require 'cgi'
  29287. require 'active_support/core_ext/class/attribute_accessors'
  29288. module HTML
  29289. class Sanitizer
  29290. def sanitize(text, options = {})
  29291. validate_options(options)
  29292. return text unless sanitizeable?(text)
  29293. tokenize(text, options).join
  29294. end
  29295. def sanitizeable?(text)
  29296. !(text.nil? || text.empty? || !text.index("<"))
  29297. end
  29298. protected
  29299. def tokenize(text, options)
  29300. tokenizer = HTML::Tokenizer.new(text)
  29301. result = []
  29302. while token = tokenizer.next
  29303. node = Node.parse(nil, 0, 0, token, false)
  29304. process_node node, result, options
  29305. end
  29306. result
  29307. end
  29308. def process_node(node, result, options)
  29309. result << node.to_s
  29310. end
  29311. def validate_options(options)
  29312. if options[:tags] && !options[:tags].is_a?(Enumerable)
  29313. raise ArgumentError, "You should pass :tags as an Enumerable"
  29314. end
  29315. if options[:attributes] && !options[:attributes].is_a?(Enumerable)
  29316. raise ArgumentError, "You should pass :attributes as an Enumerable"
  29317. end
  29318. end
  29319. end
  29320. class FullSanitizer < Sanitizer
  29321. def sanitize(text, options = {})
  29322. result = super
  29323. # strip any comments, and if they have a newline at the end (ie. line with
  29324. # only a comment) strip that too
  29325. result = result.gsub(/<!--(.*?)-->[\n]?/m, "") if (result && result =~ /<!--(.*?)-->[\n]?/m)
  29326. # Recurse - handle all dirty nested tags
  29327. result == text ? result : sanitize(result, options)
  29328. end
  29329. def process_node(node, result, options)
  29330. result << node.to_s if node.class == HTML::Text
  29331. end
  29332. end
  29333. class LinkSanitizer < FullSanitizer
  29334. cattr_accessor :included_tags, :instance_writer => false
  29335. self.included_tags = Set.new(%w(a href))
  29336. def sanitizeable?(text)
  29337. !(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
  29338. end
  29339. protected
  29340. def process_node(node, result, options)
  29341. result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
  29342. end
  29343. end
  29344. class WhiteListSanitizer < Sanitizer
  29345. [:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
  29346. :allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
  29347. class_attribute attr, :instance_writer => false
  29348. end
  29349. # A regular expression of the valid characters used to separate protocols like
  29350. # the ':' in 'http://foo.com'
  29351. self.protocol_separator = /:|(&#0*58)|(&#x70)|(%|&#37;)3A/
  29352. # Specifies a Set of HTML attributes that can have URIs.
  29353. self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
  29354. # Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
  29355. # to just escaping harmless tags like &lt;font&gt;
  29356. self.bad_tags = Set.new(%w(script))
  29357. # Specifies the default Set of tags that the #sanitize helper will allow unscathed.
  29358. self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
  29359. sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
  29360. acronym a img blockquote del ins))
  29361. # Specifies the default Set of html attributes that the #sanitize helper will leave
  29362. # in the allowed tag.
  29363. self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
  29364. # Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
  29365. self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
  29366. feed svn urn aim rsync tag ssh sftp rtsp afs))
  29367. # Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
  29368. self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
  29369. border-color border-left-color border-right-color border-top-color clear color cursor direction display
  29370. elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
  29371. overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
  29372. speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
  29373. width))
  29374. # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
  29375. self.allowed_css_keywords = Set.new(%w(auto aqua black block blue bold both bottom brown center
  29376. collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
  29377. nowrap olive pointer purple red right solid silver teal top transparent underline white yellow))
  29378. # Specifies the default Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
  29379. self.shorthand_css_properties = Set.new(%w(background border margin padding))
  29380. # Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
  29381. def sanitize_css(style)
  29382. # disallow urls
  29383. style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
  29384. # gauntlet
  29385. if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ ||
  29386. style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*$/
  29387. return ''
  29388. end
  29389. clean = []
  29390. style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
  29391. if allowed_css_properties.include?(prop.downcase)
  29392. clean << prop + ': ' + val + ';'
  29393. elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
  29394. unless val.split().any? do |keyword|
  29395. !allowed_css_keywords.include?(keyword) &&
  29396. keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
  29397. end
  29398. clean << prop + ': ' + val + ';'
  29399. end
  29400. end
  29401. end
  29402. clean.join(' ')
  29403. end
  29404. protected
  29405. def tokenize(text, options)
  29406. options[:parent] = []
  29407. options[:attributes] ||= allowed_attributes
  29408. options[:tags] ||= allowed_tags
  29409. super
  29410. end
  29411. def process_node(node, result, options)
  29412. result << case node
  29413. when HTML::Tag
  29414. if node.closing == :close
  29415. options[:parent].shift
  29416. else
  29417. options[:parent].unshift node.name
  29418. end
  29419. process_attributes_for node, options
  29420. options[:tags].include?(node.name) ? node : nil
  29421. else
  29422. bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "&lt;")
  29423. end
  29424. end
  29425. def process_attributes_for(node, options)
  29426. return unless node.attributes
  29427. node.attributes.keys.each do |attr_name|
  29428. value = node.attributes[attr_name].to_s
  29429. if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
  29430. node.attributes.delete(attr_name)
  29431. else
  29432. node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value))
  29433. end
  29434. end
  29435. end
  29436. def contains_bad_protocols?(attr_name, value)
  29437. uri_attributes.include?(attr_name) &&
  29438. (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip))
  29439. end
  29440. end
  29441. end
  29442. #--
  29443. # Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
  29444. # Under MIT and/or CC By license.
  29445. #++
  29446. module HTML
  29447. # Selects HTML elements using CSS 2 selectors.
  29448. #
  29449. # The +Selector+ class uses CSS selector expressions to match and select
  29450. # HTML elements.
  29451. #
  29452. # For example:
  29453. # selector = HTML::Selector.new "form.login[action=/login]"
  29454. # creates a new selector that matches any +form+ element with the class
  29455. # +login+ and an attribute +action+ with the value <tt>/login</tt>.
  29456. #
  29457. # === Matching Elements
  29458. #
  29459. # Use the #match method to determine if an element matches the selector.
  29460. #
  29461. # For simple selectors, the method returns an array with that element,
  29462. # or +nil+ if the element does not match. For complex selectors (see below)
  29463. # the method returns an array with all matched elements, of +nil+ if no
  29464. # match found.
  29465. #
  29466. # For example:
  29467. # if selector.match(element)
  29468. # puts "Element is a login form"
  29469. # end
  29470. #
  29471. # === Selecting Elements
  29472. #
  29473. # Use the #select method to select all matching elements starting with
  29474. # one element and going through all children in depth-first order.
  29475. #
  29476. # This method returns an array of all matching elements, an empty array
  29477. # if no match is found
  29478. #
  29479. # For example:
  29480. # selector = HTML::Selector.new "input[type=text]"
  29481. # matches = selector.select(element)
  29482. # matches.each do |match|
  29483. # puts "Found text field with name #{match.attributes['name']}"
  29484. # end
  29485. #
  29486. # === Expressions
  29487. #
  29488. # Selectors can match elements using any of the following criteria:
  29489. # * <tt>name</tt> -- Match an element based on its name (tag name).
  29490. # For example, <tt>p</tt> to match a paragraph. You can use <tt>*</tt>
  29491. # to match any element.
  29492. # * <tt>#</tt><tt>id</tt> -- Match an element based on its identifier (the
  29493. # <tt>id</tt> attribute). For example, <tt>#</tt><tt>page</tt>.
  29494. # * <tt>.class</tt> -- Match an element based on its class name, all
  29495. # class names if more than one specified.
  29496. # * <tt>[attr]</tt> -- Match an element that has the specified attribute.
  29497. # * <tt>[attr=value]</tt> -- Match an element that has the specified
  29498. # attribute and value. (More operators are supported see below)
  29499. # * <tt>:pseudo-class</tt> -- Match an element based on a pseudo class,
  29500. # such as <tt>:nth-child</tt> and <tt>:empty</tt>.
  29501. # * <tt>:not(expr)</tt> -- Match an element that does not match the
  29502. # negation expression.
  29503. #
  29504. # When using a combination of the above, the element name comes first
  29505. # followed by identifier, class names, attributes, pseudo classes and
  29506. # negation in any order. Do not separate these parts with spaces!
  29507. # Space separation is used for descendant selectors.
  29508. #
  29509. # For example:
  29510. # selector = HTML::Selector.new "form.login[action=/login]"
  29511. # The matched element must be of type +form+ and have the class +login+.
  29512. # It may have other classes, but the class +login+ is required to match.
  29513. # It must also have an attribute called +action+ with the value
  29514. # <tt>/login</tt>.
  29515. #
  29516. # This selector will match the following element:
  29517. # <form class="login form" method="post" action="/login">
  29518. # but will not match the element:
  29519. # <form method="post" action="/logout">
  29520. #
  29521. # === Attribute Values
  29522. #
  29523. # Several operators are supported for matching attributes:
  29524. # * <tt>name</tt> -- The element must have an attribute with that name.
  29525. # * <tt>name=value</tt> -- The element must have an attribute with that
  29526. # name and value.
  29527. # * <tt>name^=value</tt> -- The attribute value must start with the
  29528. # specified value.
  29529. # * <tt>name$=value</tt> -- The attribute value must end with the
  29530. # specified value.
  29531. # * <tt>name*=value</tt> -- The attribute value must contain the
  29532. # specified value.
  29533. # * <tt>name~=word</tt> -- The attribute value must contain the specified
  29534. # word (space separated).
  29535. # * <tt>name|=word</tt> -- The attribute value must start with specified
  29536. # word.
  29537. #
  29538. # For example, the following two selectors match the same element:
  29539. # #my_id
  29540. # [id=my_id]
  29541. # and so do the following two selectors:
  29542. # .my_class
  29543. # [class~=my_class]
  29544. #
  29545. # === Alternatives, siblings, children
  29546. #
  29547. # Complex selectors use a combination of expressions to match elements:
  29548. # * <tt>expr1 expr2</tt> -- Match any element against the second expression
  29549. # if it has some parent element that matches the first expression.
  29550. # * <tt>expr1 > expr2</tt> -- Match any element against the second expression
  29551. # if it is the child of an element that matches the first expression.
  29552. # * <tt>expr1 + expr2</tt> -- Match any element against the second expression
  29553. # if it immediately follows an element that matches the first expression.
  29554. # * <tt>expr1 ~ expr2</tt> -- Match any element against the second expression
  29555. # that comes after an element that matches the first expression.
  29556. # * <tt>expr1, expr2</tt> -- Match any element against the first expression,
  29557. # or against the second expression.
  29558. #
  29559. # Since children and sibling selectors may match more than one element given
  29560. # the first element, the #match method may return more than one match.
  29561. #
  29562. # === Pseudo classes
  29563. #
  29564. # Pseudo classes were introduced in CSS 3. They are most often used to select
  29565. # elements in a given position:
  29566. # * <tt>:root</tt> -- Match the element only if it is the root element
  29567. # (no parent element).
  29568. # * <tt>:empty</tt> -- Match the element only if it has no child elements,
  29569. # and no text content.
  29570. # * <tt>:content(string)</tt> -- Match the element only if it has <tt>string</tt>
  29571. # as its text content (ignoring leading and trailing whitespace).
  29572. # * <tt>:only-child</tt> -- Match the element if it is the only child (element)
  29573. # of its parent element.
  29574. # * <tt>:only-of-type</tt> -- Match the element if it is the only child (element)
  29575. # of its parent element and its type.
  29576. # * <tt>:first-child</tt> -- Match the element if it is the first child (element)
  29577. # of its parent element.
  29578. # * <tt>:first-of-type</tt> -- Match the element if it is the first child (element)
  29579. # of its parent element of its type.
  29580. # * <tt>:last-child</tt> -- Match the element if it is the last child (element)
  29581. # of its parent element.
  29582. # * <tt>:last-of-type</tt> -- Match the element if it is the last child (element)
  29583. # of its parent element of its type.
  29584. # * <tt>:nth-child(b)</tt> -- Match the element if it is the b-th child (element)
  29585. # of its parent element. The value <tt>b</tt> specifies its index, starting with 1.
  29586. # * <tt>:nth-child(an+b)</tt> -- Match the element if it is the b-th child (element)
  29587. # in each group of <tt>a</tt> child elements of its parent element.
  29588. # * <tt>:nth-child(-an+b)</tt> -- Match the element if it is the first child (element)
  29589. # in each group of <tt>a</tt> child elements, up to the first <tt>b</tt> child
  29590. # elements of its parent element.
  29591. # * <tt>:nth-child(odd)</tt> -- Match element in the odd position (i.e. first, third).
  29592. # Same as <tt>:nth-child(2n+1)</tt>.
  29593. # * <tt>:nth-child(even)</tt> -- Match element in the even position (i.e. second,
  29594. # fourth). Same as <tt>:nth-child(2n+2)</tt>.
  29595. # * <tt>:nth-of-type(..)</tt> -- As above, but only counts elements of its type.
  29596. # * <tt>:nth-last-child(..)</tt> -- As above, but counts from the last child.
  29597. # * <tt>:nth-last-of-type(..)</tt> -- As above, but counts from the last child and
  29598. # only elements of its type.
  29599. # * <tt>:not(selector)</tt> -- Match the element only if the element does not
  29600. # match the simple selector.
  29601. #
  29602. # As you can see, <tt>:nth-child</tt> pseudo class and its variant can get quite
  29603. # tricky and the CSS specification doesn't do a much better job explaining it.
  29604. # But after reading the examples and trying a few combinations, it's easy to
  29605. # figure out.
  29606. #
  29607. # For example:
  29608. # table tr:nth-child(odd)
  29609. # Selects every second row in the table starting with the first one.
  29610. #
  29611. # div p:nth-child(4)
  29612. # Selects the fourth paragraph in the +div+, but not if the +div+ contains
  29613. # other elements, since those are also counted.
  29614. #
  29615. # div p:nth-of-type(4)
  29616. # Selects the fourth paragraph in the +div+, counting only paragraphs, and
  29617. # ignoring all other elements.
  29618. #
  29619. # div p:nth-of-type(-n+4)
  29620. # Selects the first four paragraphs, ignoring all others.
  29621. #
  29622. # And you can always select an element that matches one set of rules but
  29623. # not another using <tt>:not</tt>. For example:
  29624. # p:not(.post)
  29625. # Matches all paragraphs that do not have the class <tt>.post</tt>.
  29626. #
  29627. # === Substitution Values
  29628. #
  29629. # You can use substitution with identifiers, class names and element values.
  29630. # A substitution takes the form of a question mark (<tt>?</tt>) and uses the
  29631. # next value in the argument list following the CSS expression.
  29632. #
  29633. # The substitution value may be a string or a regular expression. All other
  29634. # values are converted to strings.
  29635. #
  29636. # For example:
  29637. # selector = HTML::Selector.new "#?", /^\d+$/
  29638. # matches any element whose identifier consists of one or more digits.
  29639. #
  29640. # See http://www.w3.org/TR/css3-selectors/
  29641. class Selector
  29642. # An invalid selector.
  29643. class InvalidSelectorError < StandardError #:nodoc:
  29644. end
  29645. class << self
  29646. # :call-seq:
  29647. # Selector.for_class(cls) => selector
  29648. #
  29649. # Creates a new selector for the given class name.
  29650. def for_class(cls)
  29651. self.new([".?", cls])
  29652. end
  29653. # :call-seq:
  29654. # Selector.for_id(id) => selector
  29655. #
  29656. # Creates a new selector for the given id.
  29657. def for_id(id)
  29658. self.new(["#?", id])
  29659. end
  29660. end
  29661. # :call-seq:
  29662. # Selector.new(string, [values ...]) => selector
  29663. #
  29664. # Creates a new selector from a CSS 2 selector expression.
  29665. #
  29666. # The first argument is the selector expression. All other arguments
  29667. # are used for value substitution.
  29668. #
  29669. # Throws InvalidSelectorError is the selector expression is invalid.
  29670. def initialize(selector, *values)
  29671. raise ArgumentError, "CSS expression cannot be empty" if selector.empty?
  29672. @source = ""
  29673. values = values[0] if values.size == 1 && values[0].is_a?(Array)
  29674. # We need a copy to determine if we failed to parse, and also
  29675. # preserve the original pass by-ref statement.
  29676. statement = selector.strip.dup
  29677. # Create a simple selector, along with negation.
  29678. simple_selector(statement, values).each { |name, value| instance_variable_set("@#{name}", value) }
  29679. @alternates = []
  29680. @depends = nil
  29681. # Alternative selector.
  29682. if statement.sub!(/^\s*,\s*/, "")
  29683. second = Selector.new(statement, values)
  29684. @alternates << second
  29685. # If there are alternate selectors, we group them in the top selector.
  29686. if alternates = second.instance_variable_get(:@alternates)
  29687. second.instance_variable_set(:@alternates, [])
  29688. @alternates.concat alternates
  29689. end
  29690. @source << " , " << second.to_s
  29691. # Sibling selector: create a dependency into second selector that will
  29692. # match element immediately following this one.
  29693. elsif statement.sub!(/^\s*\+\s*/, "")
  29694. second = next_selector(statement, values)
  29695. @depends = lambda do |element, first|
  29696. if element = next_element(element)
  29697. second.match(element, first)
  29698. end
  29699. end
  29700. @source << " + " << second.to_s
  29701. # Adjacent selector: create a dependency into second selector that will
  29702. # match all elements following this one.
  29703. elsif statement.sub!(/^\s*~\s*/, "")
  29704. second = next_selector(statement, values)
  29705. @depends = lambda do |element, first|
  29706. matches = []
  29707. while element = next_element(element)
  29708. if subset = second.match(element, first)
  29709. if first && !subset.empty?
  29710. matches << subset.first
  29711. break
  29712. else
  29713. matches.concat subset
  29714. end
  29715. end
  29716. end
  29717. matches.empty? ? nil : matches
  29718. end
  29719. @source << " ~ " << second.to_s
  29720. # Child selector: create a dependency into second selector that will
  29721. # match a child element of this one.
  29722. elsif statement.sub!(/^\s*>\s*/, "")
  29723. second = next_selector(statement, values)
  29724. @depends = lambda do |element, first|
  29725. matches = []
  29726. element.children.each do |child|
  29727. if child.tag? && subset = second.match(child, first)
  29728. if first && !subset.empty?
  29729. matches << subset.first
  29730. break
  29731. else
  29732. matches.concat subset
  29733. end
  29734. end
  29735. end
  29736. matches.empty? ? nil : matches
  29737. end
  29738. @source << " > " << second.to_s
  29739. # Descendant selector: create a dependency into second selector that
  29740. # will match all descendant elements of this one. Note,
  29741. elsif statement =~ /^\s+\S+/ && statement != selector
  29742. second = next_selector(statement, values)
  29743. @depends = lambda do |element, first|
  29744. matches = []
  29745. stack = element.children.reverse
  29746. while node = stack.pop
  29747. next unless node.tag?
  29748. if subset = second.match(node, first)
  29749. if first && !subset.empty?
  29750. matches << subset.first
  29751. break
  29752. else
  29753. matches.concat subset
  29754. end
  29755. elsif children = node.children
  29756. stack.concat children.reverse
  29757. end
  29758. end
  29759. matches.empty? ? nil : matches
  29760. end
  29761. @source << " " << second.to_s
  29762. else
  29763. # The last selector is where we check that we parsed
  29764. # all the parts.
  29765. unless statement.empty? || statement.strip.empty?
  29766. raise ArgumentError, "Invalid selector: #{statement}"
  29767. end
  29768. end
  29769. end
  29770. # :call-seq:
  29771. # match(element, first?) => array or nil
  29772. #
  29773. # Matches an element against the selector.
  29774. #
  29775. # For a simple selector this method returns an array with the
  29776. # element if the element matches, nil otherwise.
  29777. #
  29778. # For a complex selector (sibling and descendant) this method
  29779. # returns an array with all matching elements, nil if no match is
  29780. # found.
  29781. #
  29782. # Use +first_only=true+ if you are only interested in the first element.
  29783. #
  29784. # For example:
  29785. # if selector.match(element)
  29786. # puts "Element is a login form"
  29787. # end
  29788. def match(element, first_only = false)
  29789. # Match element if no element name or element name same as element name
  29790. if matched = (!@tag_name || @tag_name == element.name)
  29791. # No match if one of the attribute matches failed
  29792. for attr in @attributes
  29793. if element.attributes[attr[0]] !~ attr[1]
  29794. matched = false
  29795. break
  29796. end
  29797. end
  29798. end
  29799. # Pseudo class matches (nth-child, empty, etc).
  29800. if matched
  29801. for pseudo in @pseudo
  29802. unless pseudo.call(element)
  29803. matched = false
  29804. break
  29805. end
  29806. end
  29807. end
  29808. # Negation. Same rules as above, but we fail if a match is made.
  29809. if matched && @negation
  29810. for negation in @negation
  29811. if negation[:tag_name] == element.name
  29812. matched = false
  29813. else
  29814. for attr in negation[:attributes]
  29815. if element.attributes[attr[0]] =~ attr[1]
  29816. matched = false
  29817. break
  29818. end
  29819. end
  29820. end
  29821. if matched
  29822. for pseudo in negation[:pseudo]
  29823. if pseudo.call(element)
  29824. matched = false
  29825. break
  29826. end
  29827. end
  29828. end
  29829. break unless matched
  29830. end
  29831. end
  29832. # If element matched but depends on another element (child,
  29833. # sibling, etc), apply the dependent matches instead.
  29834. if matched && @depends
  29835. matches = @depends.call(element, first_only)
  29836. else
  29837. matches = matched ? [element] : nil
  29838. end
  29839. # If this selector is part of the group, try all the alternative
  29840. # selectors (unless first_only).
  29841. if !first_only || !matches
  29842. @alternates.each do |alternate|
  29843. break if matches && first_only
  29844. if subset = alternate.match(element, first_only)
  29845. if matches
  29846. matches.concat subset
  29847. else
  29848. matches = subset
  29849. end
  29850. end
  29851. end
  29852. end
  29853. matches
  29854. end
  29855. # :call-seq:
  29856. # select(root) => array
  29857. #
  29858. # Selects and returns an array with all matching elements, beginning
  29859. # with one node and traversing through all children depth-first.
  29860. # Returns an empty array if no match is found.
  29861. #
  29862. # The root node may be any element in the document, or the document
  29863. # itself.
  29864. #
  29865. # For example:
  29866. # selector = HTML::Selector.new "input[type=text]"
  29867. # matches = selector.select(element)
  29868. # matches.each do |match|
  29869. # puts "Found text field with name #{match.attributes['name']}"
  29870. # end
  29871. def select(root)
  29872. matches = []
  29873. stack = [root]
  29874. while node = stack.pop
  29875. if node.tag? && subset = match(node, false)
  29876. subset.each do |match|
  29877. matches << match unless matches.any? { |item| item.equal?(match) }
  29878. end
  29879. elsif children = node.children
  29880. stack.concat children.reverse
  29881. end
  29882. end
  29883. matches
  29884. end
  29885. # Similar to #select but returns the first matching element. Returns +nil+
  29886. # if no element matches the selector.
  29887. def select_first(root)
  29888. stack = [root]
  29889. while node = stack.pop
  29890. if node.tag? && subset = match(node, true)
  29891. return subset.first if !subset.empty?
  29892. elsif children = node.children
  29893. stack.concat children.reverse
  29894. end
  29895. end
  29896. nil
  29897. end
  29898. def to_s #:nodoc:
  29899. @source
  29900. end
  29901. # Return the next element after this one. Skips sibling text nodes.
  29902. #
  29903. # With the +name+ argument, returns the next element with that name,
  29904. # skipping other sibling elements.
  29905. def next_element(element, name = nil)
  29906. if siblings = element.parent.children
  29907. found = false
  29908. siblings.each do |node|
  29909. if node.equal?(element)
  29910. found = true
  29911. elsif found && node.tag?
  29912. return node if (name.nil? || node.name == name)
  29913. end
  29914. end
  29915. end
  29916. nil
  29917. end
  29918. protected
  29919. # Creates a simple selector given the statement and array of
  29920. # substitution values.
  29921. #
  29922. # Returns a hash with the values +tag_name+, +attributes+,
  29923. # +pseudo+ (classes) and +negation+.
  29924. #
  29925. # Called the first time with +can_negate+ true to allow
  29926. # negation. Called a second time with false since negation
  29927. # cannot be negated.
  29928. def simple_selector(statement, values, can_negate = true)
  29929. tag_name = nil
  29930. attributes = []
  29931. pseudo = []
  29932. negation = []
  29933. # Element name. (Note that in negation, this can come at
  29934. # any order, but for simplicity we allow if only first).
  29935. statement.sub!(/^(\*|[[:alpha:]][\w\-]*)/) do |match|
  29936. match.strip!
  29937. tag_name = match.downcase unless match == "*"
  29938. @source << match
  29939. "" # Remove
  29940. end
  29941. # Get identifier, class, attribute name, pseudo or negation.
  29942. while true
  29943. # Element identifier.
  29944. next if statement.sub!(/^#(\?|[\w\-]+)/) do |match|
  29945. id = $1
  29946. if id == "?"
  29947. id = values.shift
  29948. end
  29949. @source << "##{id}"
  29950. id = Regexp.new("^#{Regexp.escape(id.to_s)}$") unless id.is_a?(Regexp)
  29951. attributes << ["id", id]
  29952. "" # Remove
  29953. end
  29954. # Class name.
  29955. next if statement.sub!(/^\.([\w\-]+)/) do |match|
  29956. class_name = $1
  29957. @source << ".#{class_name}"
  29958. class_name = Regexp.new("(^|\s)#{Regexp.escape(class_name)}($|\s)") unless class_name.is_a?(Regexp)
  29959. attributes << ["class", class_name]
  29960. "" # Remove
  29961. end
  29962. # Attribute value.
  29963. next if statement.sub!(/^\[\s*([[:alpha:]][\w\-:]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match|
  29964. name, equality, value = $1, $2, $3
  29965. if value == "?"
  29966. value = values.shift
  29967. else
  29968. # Handle single and double quotes.
  29969. value.strip!
  29970. if (value[0] == ?" || value[0] == ?') && value[0] == value[-1]
  29971. value = value[1..-2]
  29972. end
  29973. end
  29974. @source << "[#{name}#{equality}'#{value}']"
  29975. attributes << [name.downcase.strip, attribute_match(equality, value)]
  29976. "" # Remove
  29977. end
  29978. # Root element only.
  29979. next if statement.sub!(/^:root/) do |match|
  29980. pseudo << lambda do |element|
  29981. element.parent.nil? || !element.parent.tag?
  29982. end
  29983. @source << ":root"
  29984. "" # Remove
  29985. end
  29986. # Nth-child including last and of-type.
  29987. next if statement.sub!(/^:nth-(last-)?(child|of-type)\((odd|even|(\d+|\?)|(-?\d*|\?)?n([+\-]\d+|\?)?)\)/) do |match|
  29988. reverse = $1 == "last-"
  29989. of_type = $2 == "of-type"
  29990. @source << ":nth-#{$1}#{$2}("
  29991. case $3
  29992. when "odd"
  29993. pseudo << nth_child(2, 1, of_type, reverse)
  29994. @source << "odd)"
  29995. when "even"
  29996. pseudo << nth_child(2, 2, of_type, reverse)
  29997. @source << "even)"
  29998. when /^(\d+|\?)$/ # b only
  29999. b = ($1 == "?" ? values.shift : $1).to_i
  30000. pseudo << nth_child(0, b, of_type, reverse)
  30001. @source << "#{b})"
  30002. when /^(-?\d*|\?)?n([+\-]\d+|\?)?$/
  30003. a = ($1 == "?" ? values.shift :
  30004. $1 == "" ? 1 : $1 == "-" ? -1 : $1).to_i
  30005. b = ($2 == "?" ? values.shift : $2).to_i
  30006. pseudo << nth_child(a, b, of_type, reverse)
  30007. @source << (b >= 0 ? "#{a}n+#{b})" : "#{a}n#{b})")
  30008. else
  30009. raise ArgumentError, "Invalid nth-child #{match}"
  30010. end
  30011. "" # Remove
  30012. end
  30013. # First/last child (of type).
  30014. next if statement.sub!(/^:(first|last)-(child|of-type)/) do |match|
  30015. reverse = $1 == "last"
  30016. of_type = $2 == "of-type"
  30017. pseudo << nth_child(0, 1, of_type, reverse)
  30018. @source << ":#{$1}-#{$2}"
  30019. "" # Remove
  30020. end
  30021. # Only child (of type).
  30022. next if statement.sub!(/^:only-(child|of-type)/) do |match|
  30023. of_type = $1 == "of-type"
  30024. pseudo << only_child(of_type)
  30025. @source << ":only-#{$1}"
  30026. "" # Remove
  30027. end
  30028. # Empty: no child elements or meaningful content (whitespaces
  30029. # are ignored).
  30030. next if statement.sub!(/^:empty/) do |match|
  30031. pseudo << lambda do |element|
  30032. empty = true
  30033. for child in element.children
  30034. if child.tag? || !child.content.strip.empty?
  30035. empty = false
  30036. break
  30037. end
  30038. end
  30039. empty
  30040. end
  30041. @source << ":empty"
  30042. "" # Remove
  30043. end
  30044. # Content: match the text content of the element, stripping
  30045. # leading and trailing spaces.
  30046. next if statement.sub!(/^:content\(\s*(\?|'[^']*'|"[^"]*"|[^)]*)\s*\)/) do |match|
  30047. content = $1
  30048. if content == "?"
  30049. content = values.shift
  30050. elsif (content[0] == ?" || content[0] == ?') && content[0] == content[-1]
  30051. content = content[1..-2]
  30052. end
  30053. @source << ":content('#{content}')"
  30054. content = Regexp.new("^#{Regexp.escape(content.to_s)}$") unless content.is_a?(Regexp)
  30055. pseudo << lambda do |element|
  30056. text = ""
  30057. for child in element.children
  30058. unless child.tag?
  30059. text << child.content
  30060. end
  30061. end
  30062. text.strip =~ content
  30063. end
  30064. "" # Remove
  30065. end
  30066. # Negation. Create another simple selector to handle it.
  30067. if statement.sub!(/^:not\(\s*/, "")
  30068. raise ArgumentError, "Double negatives are not missing feature" unless can_negate
  30069. @source << ":not("
  30070. negation << simple_selector(statement, values, false)
  30071. raise ArgumentError, "Negation not closed" unless statement.sub!(/^\s*\)/, "")
  30072. @source << ")"
  30073. next
  30074. end
  30075. # No match: moving on.
  30076. break
  30077. end
  30078. # Return hash. The keys are mapped to instance variables.
  30079. {:tag_name=>tag_name, :attributes=>attributes, :pseudo=>pseudo, :negation=>negation}
  30080. end
  30081. # Create a regular expression to match an attribute value based
  30082. # on the equality operator (=, ^=, |=, etc).
  30083. def attribute_match(equality, value)
  30084. regexp = value.is_a?(Regexp) ? value : Regexp.escape(value.to_s)
  30085. case equality
  30086. when "=" then
  30087. # Match the attribute value in full
  30088. Regexp.new("^#{regexp}$")
  30089. when "~=" then
  30090. # Match a space-separated word within the attribute value
  30091. Regexp.new("(^|\s)#{regexp}($|\s)")
  30092. when "^="
  30093. # Match the beginning of the attribute value
  30094. Regexp.new("^#{regexp}")
  30095. when "$="
  30096. # Match the end of the attribute value
  30097. Regexp.new("#{regexp}$")
  30098. when "*="
  30099. # Match substring of the attribute value
  30100. regexp.is_a?(Regexp) ? regexp : Regexp.new(regexp)
  30101. when "|=" then
  30102. # Match the first space-separated item of the attribute value
  30103. Regexp.new("^#{regexp}($|\s)")
  30104. else
  30105. raise InvalidSelectorError, "Invalid operation/value" unless value.empty?
  30106. # Match all attributes values (existence check)
  30107. //
  30108. end
  30109. end
  30110. # Returns a lambda that can match an element against the nth-child
  30111. # pseudo class, given the following arguments:
  30112. # * +a+ -- Value of a part.
  30113. # * +b+ -- Value of b part.
  30114. # * +of_type+ -- True to test only elements of this type (of-type).
  30115. # * +reverse+ -- True to count in reverse order (last-).
  30116. def nth_child(a, b, of_type, reverse)
  30117. # a = 0 means select at index b, if b = 0 nothing selected
  30118. return lambda { |element| false } if a == 0 && b == 0
  30119. # a < 0 and b < 0 will never match against an index
  30120. return lambda { |element| false } if a < 0 && b < 0
  30121. b = a + b + 1 if b < 0 # b < 0 just picks last element from each group
  30122. b -= 1 unless b == 0 # b == 0 is same as b == 1, otherwise zero based
  30123. lambda do |element|
  30124. # Element must be inside parent element.
  30125. return false unless element.parent && element.parent.tag?
  30126. index = 0
  30127. # Get siblings, reverse if counting from last.
  30128. siblings = element.parent.children
  30129. siblings = siblings.reverse if reverse
  30130. # Match element name if of-type, otherwise ignore name.
  30131. name = of_type ? element.name : nil
  30132. found = false
  30133. for child in siblings
  30134. # Skip text nodes/comments.
  30135. if child.tag? && (name == nil || child.name == name)
  30136. if a == 0
  30137. # Shortcut when a == 0 no need to go past count
  30138. if index == b
  30139. found = child.equal?(element)
  30140. break
  30141. end
  30142. elsif a < 0
  30143. # Only look for first b elements
  30144. break if index > b
  30145. if child.equal?(element)
  30146. found = (index % a) == 0
  30147. break
  30148. end
  30149. else
  30150. # Otherwise, break if child found and count == an+b
  30151. if child.equal?(element)
  30152. found = (index % a) == b
  30153. break
  30154. end
  30155. end
  30156. index += 1
  30157. end
  30158. end
  30159. found
  30160. end
  30161. end
  30162. # Creates a only child lambda. Pass +of-type+ to only look at
  30163. # elements of its type.
  30164. def only_child(of_type)
  30165. lambda do |element|
  30166. # Element must be inside parent element.
  30167. return false unless element.parent && element.parent.tag?
  30168. name = of_type ? element.name : nil
  30169. other = false
  30170. for child in element.parent.children
  30171. # Skip text nodes/comments.
  30172. if child.tag? && (name == nil || child.name == name)
  30173. unless child.equal?(element)
  30174. other = true
  30175. break
  30176. end
  30177. end
  30178. end
  30179. !other
  30180. end
  30181. end
  30182. # Called to create a dependent selector (sibling, descendant, etc).
  30183. # Passes the remainder of the statement that will be reduced to zero
  30184. # eventually, and array of substitution values.
  30185. #
  30186. # This method is called from four places, so it helps to put it here
  30187. # for reuse. The only logic deals with the need to detect comma
  30188. # separators (alternate) and apply them to the selector group of the
  30189. # top selector.
  30190. def next_selector(statement, values)
  30191. second = Selector.new(statement, values)
  30192. # If there are alternate selectors, we group them in the top selector.
  30193. if alternates = second.instance_variable_get(:@alternates)
  30194. second.instance_variable_set(:@alternates, [])
  30195. @alternates.concat alternates
  30196. end
  30197. second
  30198. end
  30199. end
  30200. # See HTML::Selector.new
  30201. def self.selector(statement, *values)
  30202. Selector.new(statement, *values)
  30203. end
  30204. class Tag
  30205. def select(selector, *values)
  30206. selector = HTML::Selector.new(selector, values)
  30207. selector.select(self)
  30208. end
  30209. end
  30210. end
  30211. require 'strscan'
  30212. module HTML #:nodoc:
  30213. # A simple HTML tokenizer. It simply breaks a stream of text into tokens, where each
  30214. # token is a string. Each string represents either "text", or an HTML element.
  30215. #
  30216. # This currently assumes valid XHTML, which means no free < or > characters.
  30217. #
  30218. # Usage:
  30219. #
  30220. # tokenizer = HTML::Tokenizer.new(text)
  30221. # while token = tokenizer.next
  30222. # p token
  30223. # end
  30224. class Tokenizer #:nodoc:
  30225. # The current (byte) position in the text
  30226. attr_reader :position
  30227. # The current line number
  30228. attr_reader :line
  30229. # Create a new Tokenizer for the given text.
  30230. def initialize(text)
  30231. text.encode!
  30232. @scanner = StringScanner.new(text)
  30233. @position = 0
  30234. @line = 0
  30235. @current_line = 1
  30236. end
  30237. # Return the next token in the sequence, or +nil+ if there are no more tokens in
  30238. # the stream.
  30239. def next
  30240. return nil if @scanner.eos?
  30241. @position = @scanner.pos
  30242. @line = @current_line
  30243. if @scanner.check(/<\S/)
  30244. update_current_line(scan_tag)
  30245. else
  30246. update_current_line(scan_text)
  30247. end
  30248. end
  30249. private
  30250. # Treat the text at the current position as a tag, and scan it. Supports
  30251. # comments, doctype tags, and regular tags, and ignores less-than and
  30252. # greater-than characters within quoted strings.
  30253. def scan_tag
  30254. tag = @scanner.getch
  30255. if @scanner.scan(/!--/) # comment
  30256. tag << @scanner.matched
  30257. tag << (@scanner.scan_until(/--\s*>/) || @scanner.scan_until(/\Z/))
  30258. elsif @scanner.scan(/!\[CDATA\[/)
  30259. tag << @scanner.matched
  30260. tag << (@scanner.scan_until(/\]\]>/) || @scanner.scan_until(/\Z/))
  30261. elsif @scanner.scan(/!/) # doctype
  30262. tag << @scanner.matched
  30263. tag << consume_quoted_regions
  30264. else
  30265. tag << consume_quoted_regions
  30266. end
  30267. tag
  30268. end
  30269. # Scan all text up to the next < character and return it.
  30270. def scan_text
  30271. "#{@scanner.getch}#{@scanner.scan(/[^<]*/)}"
  30272. end
  30273. # Counts the number of newlines in the text and updates the current line
  30274. # accordingly.
  30275. def update_current_line(text)
  30276. text.scan(/\r?\n/) { @current_line += 1 }
  30277. end
  30278. # Skips over quoted strings, so that less-than and greater-than characters
  30279. # within the strings are ignored.
  30280. def consume_quoted_regions
  30281. text = ""
  30282. loop do
  30283. match = @scanner.scan_until(/['"<>]/) or break
  30284. delim = @scanner.matched
  30285. if delim == "<"
  30286. match = match.chop
  30287. @scanner.pos -= 1
  30288. end
  30289. text << match
  30290. break if delim == "<" || delim == ">"
  30291. # consume the quoted region
  30292. while match = @scanner.scan_until(/[\\#{delim}]/)
  30293. text << match
  30294. break if @scanner.matched == delim
  30295. break if @scanner.eos?
  30296. text << @scanner.getch # skip the escaped character
  30297. end
  30298. end
  30299. text
  30300. end
  30301. end
  30302. end
  30303. module HTML #:nodoc:
  30304. module Version #:nodoc:
  30305. MAJOR = 0
  30306. MINOR = 5
  30307. TINY = 3
  30308. STRING = [ MAJOR, MINOR, TINY ].join(".")
  30309. end
  30310. end
  30311. $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/html-scanner"
  30312. module HTML
  30313. extend ActiveSupport::Autoload
  30314. eager_autoload do
  30315. autoload :CDATA, 'html/node'
  30316. autoload :Document, 'html/document'
  30317. autoload :FullSanitizer, 'html/sanitizer'
  30318. autoload :LinkSanitizer, 'html/sanitizer'
  30319. autoload :Node, 'html/node'
  30320. autoload :Sanitizer, 'html/sanitizer'
  30321. autoload :Selector, 'html/selector'
  30322. autoload :Tag, 'html/node'
  30323. autoload :Text, 'html/node'
  30324. autoload :Tokenizer, 'html/tokenizer'
  30325. autoload :Version, 'html/version'
  30326. autoload :WhiteListSanitizer, 'html/sanitizer'
  30327. end
  30328. end
  30329. #--
  30330. # Copyright (c) 2004-2013 David Heinemeier Hansson
  30331. #
  30332. # Permission is hereby granted, free of charge, to any person obtaining
  30333. # a copy of this software and associated documentation files (the
  30334. # "Software"), to deal in the Software without restriction, including
  30335. # without limitation the rights to use, copy, modify, merge, publish,
  30336. # distribute, sublicense, and/or sell copies of the Software, and to
  30337. # permit persons to whom the Software is furnished to do so, subject to
  30338. # the following conditions:
  30339. #
  30340. # The above copyright notice and this permission notice shall be
  30341. # included in all copies or substantial portions of the Software.
  30342. #
  30343. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  30344. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  30345. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  30346. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  30347. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30348. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  30349. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30350. #++
  30351. require 'active_support'
  30352. require 'active_support/rails'
  30353. require 'action_pack'
  30354. module ActionView
  30355. extend ActiveSupport::Autoload
  30356. eager_autoload do
  30357. autoload :Base
  30358. autoload :Context
  30359. autoload :CompiledTemplates, "action_view/context"
  30360. autoload :Digestor
  30361. autoload :Helpers
  30362. autoload :LookupContext
  30363. autoload :PathSet
  30364. autoload :RecordIdentifier
  30365. autoload :RoutingUrlFor
  30366. autoload :Template
  30367. autoload_under "renderer" do
  30368. autoload :Renderer
  30369. autoload :AbstractRenderer
  30370. autoload :PartialRenderer
  30371. autoload :TemplateRenderer
  30372. autoload :StreamingTemplateRenderer
  30373. end
  30374. autoload_at "action_view/template/resolver" do
  30375. autoload :Resolver
  30376. autoload :PathResolver
  30377. autoload :FileSystemResolver
  30378. autoload :OptimizedFileSystemResolver
  30379. autoload :FallbackFileSystemResolver
  30380. end
  30381. autoload_at "action_view/buffers" do
  30382. autoload :OutputBuffer
  30383. autoload :StreamingBuffer
  30384. end
  30385. autoload_at "action_view/flows" do
  30386. autoload :OutputFlow
  30387. autoload :StreamingFlow
  30388. end
  30389. autoload_at "action_view/template/error" do
  30390. autoload :MissingTemplate
  30391. autoload :ActionViewError
  30392. autoload :EncodingError
  30393. autoload :MissingRequestError
  30394. autoload :TemplateError
  30395. autoload :WrongEncodingError
  30396. end
  30397. end
  30398. autoload :TestCase
  30399. ENCODING_FLAG = '#.*coding[:=]\s*(\S+)[ \t]*'
  30400. def self.eager_load!
  30401. super
  30402. ActionView::Template.eager_load!
  30403. end
  30404. end
  30405. require 'active_support/core_ext/string/output_safety'
  30406. ActiveSupport.on_load(:i18n) do
  30407. I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
  30408. end
  30409. require 'active_model'
  30410. class Person
  30411. include ActiveModel::Conversion
  30412. include ActiveModel::Validations
  30413. validates_presence_of :name
  30414. attr_accessor :name
  30415. def initialize(attributes = {})
  30416. @name = attributes[:name]
  30417. end
  30418. def persist
  30419. @persisted = true
  30420. end
  30421. def persisted?
  30422. @persisted
  30423. end
  30424. end
  30425. person1 = Person.new
  30426. p person1.valid? # => false
  30427. p person1.errors.messages # => {:name=>["can't be blank"]}
  30428. person2 = Person.new(:name => "matz")
  30429. p person2.valid? # => true
  30430. require 'thread_safe'
  30431. module ActiveModel
  30432. # Raised when an attribute is not defined.
  30433. #
  30434. # class User < ActiveRecord::Base
  30435. # has_many :pets
  30436. # end
  30437. #
  30438. # user = User.first
  30439. # user.pets.select(:id).first.user_id
  30440. # # => ActiveModel::MissingAttributeError: missing attribute: user_id
  30441. class MissingAttributeError < NoMethodError
  30442. end
  30443. # == Active \Model Attribute Methods
  30444. #
  30445. # <tt>ActiveModel::AttributeMethods</tt> provides a way to add prefixes and
  30446. # suffixes to your methods as well as handling the creation of Active Record
  30447. # like class methods such as +table_name+.
  30448. #
  30449. # The requirements to implement ActiveModel::AttributeMethods are to:
  30450. #
  30451. # * <tt>include ActiveModel::AttributeMethods</tt> in your object.
  30452. # * Call each Attribute Method module method you want to add, such as
  30453. # +attribute_method_suffix+ or +attribute_method_prefix+.
  30454. # * Call +define_attribute_methods+ after the other methods are called.
  30455. # * Define the various generic +_attribute+ methods that you have declared.
  30456. #
  30457. # A minimal implementation could be:
  30458. #
  30459. # class Person
  30460. # include ActiveModel::AttributeMethods
  30461. #
  30462. # attribute_method_affix prefix: 'reset_', suffix: '_to_default!'
  30463. # attribute_method_suffix '_contrived?'
  30464. # attribute_method_prefix 'clear_'
  30465. # define_attribute_methods :name
  30466. #
  30467. # attr_accessor :name
  30468. #
  30469. # private
  30470. #
  30471. # def attribute_contrived?(attr)
  30472. # true
  30473. # end
  30474. #
  30475. # def clear_attribute(attr)
  30476. # send("#{attr}=", nil)
  30477. # end
  30478. #
  30479. # def reset_attribute_to_default!(attr)
  30480. # send("#{attr}=", 'Default Name')
  30481. # end
  30482. # end
  30483. #
  30484. # Note that whenever you include ActiveModel::AttributeMethods in your class,
  30485. # it requires you to implement an +attributes+ method which returns a hash
  30486. # with each attribute name in your model as hash key and the attribute value as
  30487. # hash value.
  30488. #
  30489. # Hash keys must be strings.
  30490. module AttributeMethods
  30491. extend ActiveSupport::Concern
  30492. NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
  30493. CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
  30494. included do
  30495. class_attribute :attribute_aliases, :attribute_method_matchers, instance_writer: false
  30496. self.attribute_aliases = {}
  30497. self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new]
  30498. end
  30499. module ClassMethods
  30500. # Declares a method available for all attributes with the given prefix.
  30501. # Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
  30502. #
  30503. # #{prefix}#{attr}(*args, &block)
  30504. #
  30505. # to
  30506. #
  30507. # #{prefix}attribute(#{attr}, *args, &block)
  30508. #
  30509. # An instance method <tt>#{prefix}attribute</tt> must exist and accept
  30510. # at least the +attr+ argument.
  30511. #
  30512. # class Person
  30513. # include ActiveModel::AttributeMethods
  30514. #
  30515. # attr_accessor :name
  30516. # attribute_method_prefix 'clear_'
  30517. # define_attribute_methods :name
  30518. #
  30519. # private
  30520. #
  30521. # def clear_attribute(attr)
  30522. # send("#{attr}=", nil)
  30523. # end
  30524. # end
  30525. #
  30526. # person = Person.new
  30527. # person.name = 'Bob'
  30528. # person.name # => "Bob"
  30529. # person.clear_name
  30530. # person.name # => nil
  30531. def attribute_method_prefix(*prefixes)
  30532. self.attribute_method_matchers += prefixes.map! { |prefix| AttributeMethodMatcher.new prefix: prefix }
  30533. undefine_attribute_methods
  30534. end
  30535. # Declares a method available for all attributes with the given suffix.
  30536. # Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
  30537. #
  30538. # #{attr}#{suffix}(*args, &block)
  30539. #
  30540. # to
  30541. #
  30542. # attribute#{suffix}(#{attr}, *args, &block)
  30543. #
  30544. # An <tt>attribute#{suffix}</tt> instance method must exist and accept at
  30545. # least the +attr+ argument.
  30546. #
  30547. # class Person
  30548. # include ActiveModel::AttributeMethods
  30549. #
  30550. # attr_accessor :name
  30551. # attribute_method_suffix '_short?'
  30552. # define_attribute_methods :name
  30553. #
  30554. # private
  30555. #
  30556. # def attribute_short?(attr)
  30557. # send(attr).length < 5
  30558. # end
  30559. # end
  30560. #
  30561. # person = Person.new
  30562. # person.name = 'Bob'
  30563. # person.name # => "Bob"
  30564. # person.name_short? # => true
  30565. def attribute_method_suffix(*suffixes)
  30566. self.attribute_method_matchers += suffixes.map! { |suffix| AttributeMethodMatcher.new suffix: suffix }
  30567. undefine_attribute_methods
  30568. end
  30569. # Declares a method available for all attributes with the given prefix
  30570. # and suffix. Uses +method_missing+ and <tt>respond_to?</tt> to rewrite
  30571. # the method.
  30572. #
  30573. # #{prefix}#{attr}#{suffix}(*args, &block)
  30574. #
  30575. # to
  30576. #
  30577. # #{prefix}attribute#{suffix}(#{attr}, *args, &block)
  30578. #
  30579. # An <tt>#{prefix}attribute#{suffix}</tt> instance method must exist and
  30580. # accept at least the +attr+ argument.
  30581. #
  30582. # class Person
  30583. # include ActiveModel::AttributeMethods
  30584. #
  30585. # attr_accessor :name
  30586. # attribute_method_affix prefix: 'reset_', suffix: '_to_default!'
  30587. # define_attribute_methods :name
  30588. #
  30589. # private
  30590. #
  30591. # def reset_attribute_to_default!(attr)
  30592. # ...
  30593. # end
  30594. # end
  30595. #
  30596. # person = Person.new
  30597. # person.name # => 'Gem'
  30598. # person.reset_name_to_default!
  30599. # person.name # => 'Gemma'
  30600. def attribute_method_affix(*affixes)
  30601. self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new prefix: affix[:prefix], suffix: affix[:suffix] }
  30602. undefine_attribute_methods
  30603. end
  30604. # Allows you to make aliases for attributes.
  30605. #
  30606. # class Person
  30607. # include ActiveModel::AttributeMethods
  30608. #
  30609. # attr_accessor :name
  30610. # attribute_method_suffix '_short?'
  30611. # define_attribute_methods :name
  30612. #
  30613. # alias_attribute :nickname, :name
  30614. #
  30615. # private
  30616. #
  30617. # def attribute_short?(attr)
  30618. # send(attr).length < 5
  30619. # end
  30620. # end
  30621. #
  30622. # person = Person.new
  30623. # person.name = 'Bob'
  30624. # person.name # => "Bob"
  30625. # person.nickname # => "Bob"
  30626. # person.name_short? # => true
  30627. # person.nickname_short? # => true
  30628. def alias_attribute(new_name, old_name)
  30629. self.attribute_aliases = attribute_aliases.merge(new_name.to_s => old_name.to_s)
  30630. attribute_method_matchers.each do |matcher|
  30631. matcher_new = matcher.method_name(new_name).to_s
  30632. matcher_old = matcher.method_name(old_name).to_s
  30633. define_proxy_call false, self, matcher_new, matcher_old
  30634. end
  30635. end
  30636. # Declares the attributes that should be prefixed and suffixed by
  30637. # ActiveModel::AttributeMethods.
  30638. #
  30639. # To use, pass attribute names (as strings or symbols), be sure to declare
  30640. # +define_attribute_methods+ after you define any prefix, suffix or affix
  30641. # methods, or they will not hook in.
  30642. #
  30643. # class Person
  30644. # include ActiveModel::AttributeMethods
  30645. #
  30646. # attr_accessor :name, :age, :address
  30647. # attribute_method_prefix 'clear_'
  30648. #
  30649. # # Call to define_attribute_methods must appear after the
  30650. # # attribute_method_prefix, attribute_method_suffix or
  30651. # # attribute_method_affix declares.
  30652. # define_attribute_methods :name, :age, :address
  30653. #
  30654. # private
  30655. #
  30656. # def clear_attribute(attr)
  30657. # ...
  30658. # end
  30659. # end
  30660. def define_attribute_methods(*attr_names)
  30661. attr_names.flatten.each { |attr_name| define_attribute_method(attr_name) }
  30662. end
  30663. # Declares an attribute that should be prefixed and suffixed by
  30664. # ActiveModel::AttributeMethods.
  30665. #
  30666. # To use, pass an attribute name (as string or symbol), be sure to declare
  30667. # +define_attribute_method+ after you define any prefix, suffix or affix
  30668. # method, or they will not hook in.
  30669. #
  30670. # class Person
  30671. # include ActiveModel::AttributeMethods
  30672. #
  30673. # attr_accessor :name
  30674. # attribute_method_suffix '_short?'
  30675. #
  30676. # # Call to define_attribute_method must appear after the
  30677. # # attribute_method_prefix, attribute_method_suffix or
  30678. # # attribute_method_affix declares.
  30679. # define_attribute_method :name
  30680. #
  30681. # private
  30682. #
  30683. # def attribute_short?(attr)
  30684. # send(attr).length < 5
  30685. # end
  30686. # end
  30687. #
  30688. # person = Person.new
  30689. # person.name = 'Bob'
  30690. # person.name # => "Bob"
  30691. # person.name_short? # => true
  30692. def define_attribute_method(attr_name)
  30693. attribute_method_matchers.each do |matcher|
  30694. method_name = matcher.method_name(attr_name)
  30695. unless instance_method_already_implemented?(method_name)
  30696. generate_method = "define_method_#{matcher.method_missing_target}"
  30697. if respond_to?(generate_method, true)
  30698. send(generate_method, attr_name)
  30699. else
  30700. define_proxy_call true, generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s
  30701. end
  30702. end
  30703. end
  30704. attribute_method_matchers_cache.clear
  30705. end
  30706. # Removes all the previously dynamically defined methods from the class.
  30707. #
  30708. # class Person
  30709. # include ActiveModel::AttributeMethods
  30710. #
  30711. # attr_accessor :name
  30712. # attribute_method_suffix '_short?'
  30713. # define_attribute_method :name
  30714. #
  30715. # private
  30716. #
  30717. # def attribute_short?(attr)
  30718. # send(attr).length < 5
  30719. # end
  30720. # end
  30721. #
  30722. # person = Person.new
  30723. # person.name = 'Bob'
  30724. # person.name_short? # => true
  30725. #
  30726. # Person.undefine_attribute_methods
  30727. #
  30728. # person.name_short? # => NoMethodError
  30729. def undefine_attribute_methods
  30730. generated_attribute_methods.module_eval do
  30731. instance_methods.each { |m| undef_method(m) }
  30732. end
  30733. attribute_method_matchers_cache.clear
  30734. end
  30735. # Returns true if the attribute methods defined have been generated.
  30736. def generated_attribute_methods #:nodoc:
  30737. @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
  30738. end
  30739. protected
  30740. def instance_method_already_implemented?(method_name) #:nodoc:
  30741. generated_attribute_methods.method_defined?(method_name)
  30742. end
  30743. private
  30744. # The methods +method_missing+ and +respond_to?+ of this module are
  30745. # invoked often in a typical rails, both of which invoke the method
  30746. # +match_attribute_method?+. The latter method iterates through an
  30747. # array doing regular expression matches, which results in a lot of
  30748. # object creations. Most of the times it returns a +nil+ match. As the
  30749. # match result is always the same given a +method_name+, this cache is
  30750. # used to alleviate the GC, which ultimately also speeds up the app
  30751. # significantly (in our case our test suite finishes 10% faster with
  30752. # this cache).
  30753. def attribute_method_matchers_cache #:nodoc:
  30754. @attribute_method_matchers_cache ||= ThreadSafe::Cache.new(:initial_capacity => 4)
  30755. end
  30756. def attribute_method_matcher(method_name) #:nodoc:
  30757. attribute_method_matchers_cache.compute_if_absent(method_name) do
  30758. # Must try to match prefixes/suffixes first, or else the matcher with no prefix/suffix
  30759. # will match every time.
  30760. matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
  30761. match = nil
  30762. matchers.detect { |method| match = method.match(method_name) }
  30763. match
  30764. end
  30765. end
  30766. # Define a method `name` in `mod` that dispatches to `send`
  30767. # using the given `extra` args. This fallbacks `define_method`
  30768. # and `send` if the given names cannot be compiled.
  30769. def define_proxy_call(include_private, mod, name, send, *extra) #:nodoc:
  30770. defn = if name =~ NAME_COMPILABLE_REGEXP
  30771. "def #{name}(*args)"
  30772. else
  30773. "define_method(:'#{name}') do |*args|"
  30774. end
  30775. extra = (extra.map!(&:inspect) << "*args").join(", ")
  30776. target = if send =~ CALL_COMPILABLE_REGEXP
  30777. "#{"self." unless include_private}#{send}(#{extra})"
  30778. else
  30779. "send(:'#{send}', #{extra})"
  30780. end
  30781. mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
  30782. #{defn}
  30783. #{target}
  30784. end
  30785. RUBY
  30786. end
  30787. class AttributeMethodMatcher #:nodoc:
  30788. attr_reader :prefix, :suffix, :method_missing_target
  30789. AttributeMethodMatch = Struct.new(:target, :attr_name, :method_name)
  30790. def initialize(options = {})
  30791. if options[:prefix] == '' || options[:suffix] == ''
  30792. message = "Specifying an empty prefix/suffix for an attribute method is no longer " \
  30793. "necessary. If the un-prefixed/suffixed version of the method has not been " \
  30794. "defined when `define_attribute_methods` is called, it will be defined " \
  30795. "automatically."
  30796. ActiveSupport::Deprecation.warn message
  30797. end
  30798. @prefix, @suffix = options.fetch(:prefix, ''), options.fetch(:suffix, '')
  30799. @regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
  30800. @method_missing_target = "#{@prefix}attribute#{@suffix}"
  30801. @method_name = "#{prefix}%s#{suffix}"
  30802. end
  30803. def match(method_name)
  30804. if @regex =~ method_name
  30805. AttributeMethodMatch.new(method_missing_target, $1, method_name)
  30806. end
  30807. end
  30808. def method_name(attr_name)
  30809. @method_name % attr_name
  30810. end
  30811. def plain?
  30812. prefix.empty? && suffix.empty?
  30813. end
  30814. end
  30815. end
  30816. # Allows access to the object attributes, which are held in the
  30817. # <tt>@attributes</tt> hash, as though they were first-class methods. So a
  30818. # Person class with a name attribute can use Person#name and Person#name=
  30819. # and never directly use the attributes hash -- except for multiple assigns
  30820. # with ActiveRecord#attributes=. A Milestone class can also ask
  30821. # Milestone#completed? to test that the completed attribute is not +nil+
  30822. # or 0.
  30823. #
  30824. # It's also possible to instantiate related objects, so a Client class
  30825. # belonging to the clients table with a +master_id+ foreign key can
  30826. # instantiate master through Client#master.
  30827. def method_missing(method, *args, &block)
  30828. if respond_to_without_attributes?(method, true)
  30829. super
  30830. else
  30831. match = match_attribute_method?(method.to_s)
  30832. match ? attribute_missing(match, *args, &block) : super
  30833. end
  30834. end
  30835. # attribute_missing is like method_missing, but for attributes. When method_missing is
  30836. # called we check to see if there is a matching attribute method. If so, we call
  30837. # attribute_missing to dispatch the attribute. This method can be overloaded to
  30838. # customize the behavior.
  30839. def attribute_missing(match, *args, &block)
  30840. __send__(match.target, match.attr_name, *args, &block)
  30841. end
  30842. # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
  30843. # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
  30844. # which will all return +true+.
  30845. alias :respond_to_without_attributes? :respond_to?
  30846. def respond_to?(method, include_private_methods = false)
  30847. if super
  30848. true
  30849. elsif !include_private_methods && super(method, true)
  30850. # If we're here then we haven't found among non-private methods
  30851. # but found among all methods. Which means that the given method is private.
  30852. false
  30853. else
  30854. !match_attribute_method?(method.to_s).nil?
  30855. end
  30856. end
  30857. protected
  30858. def attribute_method?(attr_name) #:nodoc:
  30859. respond_to_without_attributes?(:attributes) && attributes.include?(attr_name)
  30860. end
  30861. private
  30862. # Returns a struct representing the matching attribute method.
  30863. # The struct's attributes are prefix, base and suffix.
  30864. def match_attribute_method?(method_name)
  30865. match = self.class.send(:attribute_method_matcher, method_name)
  30866. match if match && attribute_method?(match.attr_name)
  30867. end
  30868. def missing_attribute(attr_name, stack)
  30869. raise ActiveModel::MissingAttributeError, "missing attribute: #{attr_name}", stack
  30870. end
  30871. end
  30872. end
  30873. require 'active_support/core_ext/array/extract_options'
  30874. module ActiveModel
  30875. # == Active \Model \Callbacks
  30876. #
  30877. # Provides an interface for any class to have Active Record like callbacks.
  30878. #
  30879. # Like the Active Record methods, the callback chain is aborted as soon as
  30880. # one of the methods in the chain returns +false+.
  30881. #
  30882. # First, extend ActiveModel::Callbacks from the class you are creating:
  30883. #
  30884. # class MyModel
  30885. # extend ActiveModel::Callbacks
  30886. # end
  30887. #
  30888. # Then define a list of methods that you want callbacks attached to:
  30889. #
  30890. # define_model_callbacks :create, :update
  30891. #
  30892. # This will provide all three standard callbacks (before, around and after)
  30893. # for both the <tt>:create</tt> and <tt>:update</tt> methods. To implement,
  30894. # you need to wrap the methods you want callbacks on in a block so that the
  30895. # callbacks get a chance to fire:
  30896. #
  30897. # def create
  30898. # run_callbacks :create do
  30899. # # Your create action methods here
  30900. # end
  30901. # end
  30902. #
  30903. # Then in your class, you can use the +before_create+, +after_create+ and
  30904. # +around_create+ methods, just as you would in an Active Record module.
  30905. #
  30906. # before_create :action_before_create
  30907. #
  30908. # def action_before_create
  30909. # # Your code here
  30910. # end
  30911. #
  30912. # When defining an around callback remember to yield to the block, otherwise
  30913. # it won't be executed:
  30914. #
  30915. # around_create :log_status
  30916. #
  30917. # def log_status
  30918. # puts 'going to call the block...'
  30919. # yield
  30920. # puts 'block successfully called.'
  30921. # end
  30922. #
  30923. # You can choose not to have all three callbacks by passing a hash to the
  30924. # +define_model_callbacks+ method.
  30925. #
  30926. # define_model_callbacks :create, only: [:after, :before]
  30927. #
  30928. # Would only create the +after_create+ and +before_create+ callback methods in
  30929. # your class.
  30930. module Callbacks
  30931. def self.extended(base) #:nodoc:
  30932. base.class_eval do
  30933. include ActiveSupport::Callbacks
  30934. end
  30935. end
  30936. # define_model_callbacks accepts the same options +define_callbacks+ does,
  30937. # in case you want to overwrite a default. Besides that, it also accepts an
  30938. # <tt>:only</tt> option, where you can choose if you want all types (before,
  30939. # around or after) or just some.
  30940. #
  30941. # define_model_callbacks :initializer, only: :after
  30942. #
  30943. # Note, the <tt>only: <type></tt> hash will apply to all callbacks defined
  30944. # on that method call. To get around this you can call the define_model_callbacks
  30945. # method as many times as you need.
  30946. #
  30947. # define_model_callbacks :create, only: :after
  30948. # define_model_callbacks :update, only: :before
  30949. # define_model_callbacks :destroy, only: :around
  30950. #
  30951. # Would create +after_create+, +before_update+ and +around_destroy+ methods
  30952. # only.
  30953. #
  30954. # You can pass in a class to before_<type>, after_<type> and around_<type>,
  30955. # in which case the callback will call that class's <action>_<type> method
  30956. # passing the object that the callback is being called on.
  30957. #
  30958. # class MyModel
  30959. # extend ActiveModel::Callbacks
  30960. # define_model_callbacks :create
  30961. #
  30962. # before_create AnotherClass
  30963. # end
  30964. #
  30965. # class AnotherClass
  30966. # def self.before_create( obj )
  30967. # # obj is the MyModel instance that the callback is being called on
  30968. # end
  30969. # end
  30970. def define_model_callbacks(*callbacks)
  30971. options = callbacks.extract_options!
  30972. options = {
  30973. :terminator => "result == false",
  30974. :skip_after_callbacks_if_terminated => true,
  30975. :scope => [:kind, :name],
  30976. :only => [:before, :around, :after]
  30977. }.merge!(options)
  30978. types = Array(options.delete(:only))
  30979. callbacks.each do |callback|
  30980. define_callbacks(callback, options)
  30981. types.each do |type|
  30982. send("_define_#{type}_model_callback", self, callback)
  30983. end
  30984. end
  30985. end
  30986. private
  30987. def _define_before_model_callback(klass, callback) #:nodoc:
  30988. klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
  30989. def self.before_#{callback}(*args, &block)
  30990. set_callback(:#{callback}, :before, *args, &block)
  30991. end
  30992. CALLBACK
  30993. end
  30994. def _define_around_model_callback(klass, callback) #:nodoc:
  30995. klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
  30996. def self.around_#{callback}(*args, &block)
  30997. set_callback(:#{callback}, :around, *args, &block)
  30998. end
  30999. CALLBACK
  31000. end
  31001. def _define_after_model_callback(klass, callback) #:nodoc:
  31002. klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
  31003. def self.after_#{callback}(*args, &block)
  31004. options = args.extract_options!
  31005. options[:prepend] = true
  31006. options[:if] = Array(options[:if]) << "value != false"
  31007. set_callback(:#{callback}, :after, *(args << options), &block)
  31008. end
  31009. CALLBACK
  31010. end
  31011. end
  31012. end
  31013. module ActiveModel
  31014. # == Active \Model Conversions
  31015. #
  31016. # Handles default conversions: to_model, to_key, to_param, and to_partial_path.
  31017. #
  31018. # Let's take for example this non-persisted object.
  31019. #
  31020. # class ContactMessage
  31021. # include ActiveModel::Conversion
  31022. #
  31023. # # ContactMessage are never persisted in the DB
  31024. # def persisted?
  31025. # false
  31026. # end
  31027. # end
  31028. #
  31029. # cm = ContactMessage.new
  31030. # cm.to_model == cm # => true
  31031. # cm.to_key # => nil
  31032. # cm.to_param # => nil
  31033. # cm.to_partial_path # => "contact_messages/contact_message"
  31034. module Conversion
  31035. extend ActiveSupport::Concern
  31036. # If your object is already designed to implement all of the Active Model
  31037. # you can use the default <tt>:to_model</tt> implementation, which simply
  31038. # returns +self+.
  31039. #
  31040. # class Person
  31041. # include ActiveModel::Conversion
  31042. # end
  31043. #
  31044. # person = Person.new
  31045. # person.to_model == person # => true
  31046. #
  31047. # If your model does not act like an Active Model object, then you should
  31048. # define <tt>:to_model</tt> yourself returning a proxy object that wraps
  31049. # your object with Active Model compliant methods.
  31050. def to_model
  31051. self
  31052. end
  31053. # Returns an Enumerable of all key attributes if any is set, regardless if
  31054. # the object is persisted or not. If there no key attributes, returns +nil+.
  31055. #
  31056. # class Person < ActiveRecord::Base
  31057. # end
  31058. #
  31059. # person = Person.create
  31060. # person.to_key # => [1]
  31061. def to_key
  31062. key = respond_to?(:id) && id
  31063. key ? [key] : nil
  31064. end
  31065. # Returns a +string+ representing the object's key suitable for use in URLs,
  31066. # or +nil+ if <tt>persisted?</tt> is +false+.
  31067. #
  31068. # class Person < ActiveRecord::Base
  31069. # end
  31070. #
  31071. # person = Person.create
  31072. # person.to_param # => "1"
  31073. def to_param
  31074. persisted? ? to_key.join('-') : nil
  31075. end
  31076. # Returns a +string+ identifying the path associated with the object.
  31077. # ActionPack uses this to find a suitable partial to represent the object.
  31078. #
  31079. # class Person
  31080. # include ActiveModel::Conversion
  31081. # end
  31082. #
  31083. # person = Person.new
  31084. # person.to_partial_path # => "people/person"
  31085. def to_partial_path
  31086. self.class._to_partial_path
  31087. end
  31088. module ClassMethods #:nodoc:
  31089. # Provide a class level cache for #to_partial_path. This is an
  31090. # internal method and should not be accessed directly.
  31091. def _to_partial_path #:nodoc:
  31092. @_to_partial_path ||= begin
  31093. element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self))
  31094. collection = ActiveSupport::Inflector.tableize(self)
  31095. "#{collection}/#{element}".freeze
  31096. end
  31097. end
  31098. end
  31099. end
  31100. end
  31101. module ActiveModel
  31102. module DeprecatedMassAssignmentSecurity # :nodoc:
  31103. extend ActiveSupport::Concern
  31104. module ClassMethods # :nodoc:
  31105. def attr_protected(*args)
  31106. raise "`attr_protected` is extracted out of Rails into a gem. " \
  31107. "Please use new recommended protection model for params" \
  31108. "(strong_parameters) or add `protected_attributes` to your " \
  31109. "Gemfile to use old one."
  31110. end
  31111. def attr_accessible(*args)
  31112. raise "`attr_accessible` is extracted out of Rails into a gem. " \
  31113. "Please use new recommended protection model for params" \
  31114. "(strong_parameters) or add `protected_attributes` to your " \
  31115. "Gemfile to use old one."
  31116. end
  31117. end
  31118. end
  31119. end
  31120. require 'active_support/hash_with_indifferent_access'
  31121. require 'active_support/core_ext/object/duplicable'
  31122. module ActiveModel
  31123. # == Active \Model \Dirty
  31124. #
  31125. # Provides a way to track changes in your object in the same way as
  31126. # Active Record does.
  31127. #
  31128. # The requirements for implementing ActiveModel::Dirty are:
  31129. #
  31130. # * <tt>include ActiveModel::Dirty</tt> in your object.
  31131. # * Call <tt>define_attribute_methods</tt> passing each method you want to
  31132. # track.
  31133. # * Call <tt>attr_name_will_change!</tt> before each change to the tracked
  31134. # attribute.
  31135. #
  31136. # If you wish to also track previous changes on save or update, you need to
  31137. # add:
  31138. #
  31139. # @previously_changed = changes
  31140. #
  31141. # inside of your save or update method.
  31142. #
  31143. # A minimal implementation could be:
  31144. #
  31145. # class Person
  31146. # include ActiveModel::Dirty
  31147. #
  31148. # define_attribute_methods :name
  31149. #
  31150. # def name
  31151. # @name
  31152. # end
  31153. #
  31154. # def name=(val)
  31155. # name_will_change! unless val == @name
  31156. # @name = val
  31157. # end
  31158. #
  31159. # def save
  31160. # @previously_changed = changes
  31161. # @changed_attributes.clear
  31162. # end
  31163. # end
  31164. #
  31165. # A newly instantiated object is unchanged:
  31166. #
  31167. # person = Person.find_by_name('Uncle Bob')
  31168. # person.changed? # => false
  31169. #
  31170. # Change the name:
  31171. #
  31172. # person.name = 'Bob'
  31173. # person.changed? # => true
  31174. # person.name_changed? # => true
  31175. # person.name_was # => "Uncle Bob"
  31176. # person.name_change # => ["Uncle Bob", "Bob"]
  31177. # person.name = 'Bill'
  31178. # person.name_change # => ["Uncle Bob", "Bill"]
  31179. #
  31180. # Save the changes:
  31181. #
  31182. # person.save
  31183. # person.changed? # => false
  31184. # person.name_changed? # => false
  31185. #
  31186. # Assigning the same value leaves the attribute unchanged:
  31187. #
  31188. # person.name = 'Bill'
  31189. # person.name_changed? # => false
  31190. # person.name_change # => nil
  31191. #
  31192. # Which attributes have changed?
  31193. #
  31194. # person.name = 'Bob'
  31195. # person.changed # => ["name"]
  31196. # person.changes # => {"name" => ["Bill", "Bob"]}
  31197. #
  31198. # If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
  31199. # to mark that the attribute is changing. Otherwise ActiveModel can't track
  31200. # changes to in-place attributes.
  31201. #
  31202. # person.name_will_change!
  31203. # person.name_change # => ["Bill", "Bill"]
  31204. # person.name << 'y'
  31205. # person.name_change # => ["Bill", "Billy"]
  31206. module Dirty
  31207. extend ActiveSupport::Concern
  31208. include ActiveModel::AttributeMethods
  31209. included do
  31210. attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
  31211. attribute_method_affix :prefix => 'reset_', :suffix => '!'
  31212. end
  31213. # Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
  31214. #
  31215. # person.changed? # => false
  31216. # person.name = 'bob'
  31217. # person.changed? # => true
  31218. def changed?
  31219. changed_attributes.present?
  31220. end
  31221. # Returns an array with the name of the attributes with unsaved changes.
  31222. #
  31223. # person.changed # => []
  31224. # person.name = 'bob'
  31225. # person.changed # => ["name"]
  31226. def changed
  31227. changed_attributes.keys
  31228. end
  31229. # Returns a hash of changed attributes indicating their original
  31230. # and new values like <tt>attr => [original value, new value]</tt>.
  31231. #
  31232. # person.changes # => {}
  31233. # person.name = 'bob'
  31234. # person.changes # => { "name" => ["bill", "bob"] }
  31235. def changes
  31236. ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
  31237. end
  31238. # Returns a hash of attributes that were changed before the model was saved.
  31239. #
  31240. # person.name # => "bob"
  31241. # person.name = 'robert'
  31242. # person.save
  31243. # person.previous_changes # => {"name" => ["bob", "robert"]}
  31244. def previous_changes
  31245. @previously_changed
  31246. end
  31247. # Returns a hash of the attributes with unsaved changes indicating their original
  31248. # values like <tt>attr => original value</tt>.
  31249. #
  31250. # person.name # => "bob"
  31251. # person.name = 'robert'
  31252. # person.changed_attributes # => {"name" => "bob"}
  31253. def changed_attributes
  31254. @changed_attributes ||= {}
  31255. end
  31256. private
  31257. # Handle <tt>*_changed?</tt> for +method_missing+.
  31258. def attribute_changed?(attr)
  31259. changed_attributes.include?(attr)
  31260. end
  31261. # Handle <tt>*_change</tt> for +method_missing+.
  31262. def attribute_change(attr)
  31263. [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
  31264. end
  31265. # Handle <tt>*_was</tt> for +method_missing+.
  31266. def attribute_was(attr)
  31267. attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
  31268. end
  31269. # Handle <tt>*_will_change!</tt> for +method_missing+.
  31270. def attribute_will_change!(attr)
  31271. return if attribute_changed?(attr)
  31272. begin
  31273. value = __send__(attr)
  31274. value = value.duplicable? ? value.clone : value
  31275. rescue TypeError, NoMethodError
  31276. end
  31277. changed_attributes[attr] = value
  31278. end
  31279. # Handle <tt>reset_*!</tt> for +method_missing+.
  31280. def reset_attribute!(attr)
  31281. if attribute_changed?(attr)
  31282. __send__("#{attr}=", changed_attributes[attr])
  31283. changed_attributes.delete(attr)
  31284. end
  31285. end
  31286. end
  31287. end
  31288. # -*- coding: utf-8 -*-
  31289. require 'active_support/core_ext/array/conversions'
  31290. require 'active_support/core_ext/string/inflections'
  31291. module ActiveModel
  31292. # == Active \Model \Errors
  31293. #
  31294. # Provides a modified +Hash+ that you can include in your object
  31295. # for handling error messages and interacting with Action Pack helpers.
  31296. #
  31297. # A minimal implementation could be:
  31298. #
  31299. # class Person
  31300. #
  31301. # # Required dependency for ActiveModel::Errors
  31302. # extend ActiveModel::Naming
  31303. #
  31304. # def initialize
  31305. # @errors = ActiveModel::Errors.new(self)
  31306. # end
  31307. #
  31308. # attr_accessor :name
  31309. # attr_reader :errors
  31310. #
  31311. # def validate!
  31312. # errors.add(:name, "can not be nil") if name == nil
  31313. # end
  31314. #
  31315. # # The following methods are needed to be minimally implemented
  31316. #
  31317. # def read_attribute_for_validation(attr)
  31318. # send(attr)
  31319. # end
  31320. #
  31321. # def Person.human_attribute_name(attr, options = {})
  31322. # attr
  31323. # end
  31324. #
  31325. # def Person.lookup_ancestors
  31326. # [self]
  31327. # end
  31328. #
  31329. # end
  31330. #
  31331. # The last three methods are required in your object for Errors to be
  31332. # able to generate error messages correctly and also handle multiple
  31333. # languages. Of course, if you extend your object with ActiveModel::Translation
  31334. # you will not need to implement the last two. Likewise, using
  31335. # ActiveModel::Validations will handle the validation related methods
  31336. # for you.
  31337. #
  31338. # The above allows you to do:
  31339. #
  31340. # p = Person.new
  31341. # person.validate! # => ["can not be nil"]
  31342. # person.errors.full_messages # => ["name can not be nil"]
  31343. # # etc..
  31344. class Errors
  31345. include Enumerable
  31346. CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
  31347. attr_reader :messages
  31348. # Pass in the instance of the object that is using the errors object.
  31349. #
  31350. # class Person
  31351. # def initialize
  31352. # @errors = ActiveModel::Errors.new(self)
  31353. # end
  31354. # end
  31355. def initialize(base)
  31356. @base = base
  31357. @messages = {}
  31358. end
  31359. def initialize_dup(other) # :nodoc:
  31360. @messages = other.messages.dup
  31361. super
  31362. end
  31363. # Clear the error messages.
  31364. #
  31365. # person.errors.full_messages # => ["name can not be nil"]
  31366. # person.errors.clear
  31367. # person.errors.full_messages # => []
  31368. def clear
  31369. messages.clear
  31370. end
  31371. # Returns +true+ if the error messages include an error for the given key
  31372. # +attribute+, +false+ otherwise.
  31373. #
  31374. # person.errors.messages # => {:name=>["can not be nil"]}
  31375. # person.errors.include?(:name) # => true
  31376. # person.errors.include?(:age) # => false
  31377. def include?(attribute)
  31378. (v = messages[attribute]) && v.any?
  31379. end
  31380. # aliases include?
  31381. alias :has_key? :include?
  31382. # Get messages for +key+.
  31383. #
  31384. # person.errors.messages # => {:name=>["can not be nil"]}
  31385. # person.errors.get(:name) # => ["can not be nil"]
  31386. # person.errors.get(:age) # => nil
  31387. def get(key)
  31388. messages[key]
  31389. end
  31390. # Set messages for +key+ to +value+.
  31391. #
  31392. # person.errors.get(:name) # => ["can not be nil"]
  31393. # person.errors.set(:name, ["can't be nil"])
  31394. # person.errors.get(:name) # => ["can't be nil"]
  31395. def set(key, value)
  31396. messages[key] = value
  31397. end
  31398. # Delete messages for +key+. Returns the deleted messages.
  31399. #
  31400. # person.errors.get(:name) # => ["can not be nil"]
  31401. # person.errors.delete(:name) # => ["can not be nil"]
  31402. # person.errors.get(:name) # => nil
  31403. def delete(key)
  31404. messages.delete(key)
  31405. end
  31406. # When passed a symbol or a name of a method, returns an array of errors
  31407. # for the method.
  31408. #
  31409. # person.errors[:name] # => ["can not be nil"]
  31410. # person.errors['name'] # => ["can not be nil"]
  31411. def [](attribute)
  31412. get(attribute.to_sym) || set(attribute.to_sym, [])
  31413. end
  31414. # Adds to the supplied attribute the supplied error message.
  31415. #
  31416. # person.errors[:name] = "must be set"
  31417. # person.errors[:name] # => ['must be set']
  31418. def []=(attribute, error)
  31419. self[attribute] << error
  31420. end
  31421. # Iterates through each error key, value pair in the error messages hash.
  31422. # Yields the attribute and the error for that attribute. If the attribute
  31423. # has more than one error message, yields once for each error message.
  31424. #
  31425. # person.errors.add(:name, "can't be blank")
  31426. # person.errors.each do |attribute, error|
  31427. # # Will yield :name and "can't be blank"
  31428. # end
  31429. #
  31430. # person.errors.add(:name, "must be specified")
  31431. # person.errors.each do |attribute, error|
  31432. # # Will yield :name and "can't be blank"
  31433. # # then yield :name and "must be specified"
  31434. # end
  31435. def each
  31436. messages.each_key do |attribute|
  31437. self[attribute].each { |error| yield attribute, error }
  31438. end
  31439. end
  31440. # Returns the number of error messages.
  31441. #
  31442. # person.errors.add(:name, "can't be blank")
  31443. # person.errors.size # => 1
  31444. # person.errors.add(:name, "must be specified")
  31445. # person.errors.size # => 2
  31446. def size
  31447. values.flatten.size
  31448. end
  31449. # Returns all message values.
  31450. #
  31451. # person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
  31452. # person.errors.values # => [["can not be nil", "must be specified"]]
  31453. def values
  31454. messages.values
  31455. end
  31456. # Returns all message keys.
  31457. #
  31458. # person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
  31459. # person.errors.keys # => [:name]
  31460. def keys
  31461. messages.keys
  31462. end
  31463. # Returns an array of error messages, with the attribute name included.
  31464. #
  31465. # person.errors.add(:name, "can't be blank")
  31466. # person.errors.add(:name, "must be specified")
  31467. # person.errors.to_a # => ["name can't be blank", "name must be specified"]
  31468. def to_a
  31469. full_messages
  31470. end
  31471. # Returns the number of error messages.
  31472. #
  31473. # person.errors.add(:name, "can't be blank")
  31474. # person.errors.count # => 1
  31475. # person.errors.add(:name, "must be specified")
  31476. # person.errors.count # => 2
  31477. def count
  31478. to_a.size
  31479. end
  31480. # Returns +true+ if no errors are found, +false+ otherwise.
  31481. # If the error message is a string it can be empty.
  31482. #
  31483. # person.errors.full_messages # => ["name can not be nil"]
  31484. # person.errors.empty? # => false
  31485. def empty?
  31486. all? { |k, v| v && v.empty? && !v.is_a?(String) }
  31487. end
  31488. # aliases empty?
  31489. alias_method :blank?, :empty?
  31490. # Returns an xml formatted representation of the Errors hash.
  31491. #
  31492. # person.errors.add(:name, "can't be blank")
  31493. # person.errors.add(:name, "must be specified")
  31494. # person.errors.to_xml
  31495. # # =>
  31496. # # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
  31497. # # <errors>
  31498. # # <error>name can't be blank</error>
  31499. # # <error>name must be specified</error>
  31500. # # </errors>
  31501. def to_xml(options={})
  31502. to_a.to_xml({ :root => "errors", :skip_types => true }.merge!(options))
  31503. end
  31504. # Returns a Hash that can be used as the JSON representation for this
  31505. # object. You can pass the <tt>:full_messages</tt> option. This determines
  31506. # if the json object should contain full messages or not (false by default).
  31507. #
  31508. # person.as_json # => {:name=>["can not be nil"]}
  31509. # person.as_json(full_messages: true) # => {:name=>["name can not be nil"]}
  31510. def as_json(options=nil)
  31511. to_hash(options && options[:full_messages])
  31512. end
  31513. # Returns a Hash of attributes with their error messages. If +full_messages+
  31514. # is +true+, it will contain full messages (see +full_message+).
  31515. #
  31516. # person.to_hash # => {:name=>["can not be nil"]}
  31517. # person.to_hash(true) # => {:name=>["name can not be nil"]}
  31518. def to_hash(full_messages = false)
  31519. if full_messages
  31520. messages = {}
  31521. self.messages.each do |attribute, array|
  31522. messages[attribute] = array.map { |message| full_message(attribute, message) }
  31523. end
  31524. messages
  31525. else
  31526. self.messages.dup
  31527. end
  31528. end
  31529. # Adds +message+ to the error messages on +attribute+. More than one error
  31530. # can be added to the same +attribute+. If no +message+ is supplied,
  31531. # <tt>:invalid</tt> is assumed.
  31532. #
  31533. # person.errors.add(:name)
  31534. # # => ["is invalid"]
  31535. # person.errors.add(:name, 'must be implemented')
  31536. # # => ["is invalid", "must be implemented"]
  31537. #
  31538. # person.errors.messages
  31539. # # => {:name=>["must be implemented", "is invalid"]}
  31540. #
  31541. # If +message+ is a symbol, it will be translated using the appropriate
  31542. # scope (see +generate_message+).
  31543. #
  31544. # If +message+ is a proc, it will be called, allowing for things like
  31545. # <tt>Time.now</tt> to be used within an error.
  31546. #
  31547. # If the <tt>:strict</tt> option is set to true will raise
  31548. # ActiveModel::StrictValidationFailed instead of adding the error.
  31549. # <tt>:strict</tt> option can also be set to any other exception.
  31550. #
  31551. # person.errors.add(:name, nil, strict: true)
  31552. # # => ActiveModel::StrictValidationFailed: name is invalid
  31553. # person.errors.add(:name, nil, strict: NameIsInvalid)
  31554. # # => NameIsInvalid: name is invalid
  31555. #
  31556. # person.errors.messages # => {}
  31557. def add(attribute, message = nil, options = {})
  31558. message = normalize_message(attribute, message, options)
  31559. if exception = options[:strict]
  31560. exception = ActiveModel::StrictValidationFailed if exception == true
  31561. raise exception, full_message(attribute, message)
  31562. end
  31563. self[attribute] << message
  31564. end
  31565. # Will add an error message to each of the attributes in +attributes+
  31566. # that is empty.
  31567. #
  31568. # person.errors.add_on_empty(:name)
  31569. # person.errors.messages
  31570. # # => {:name=>["can't be empty"]}
  31571. def add_on_empty(attributes, options = {})
  31572. Array(attributes).each do |attribute|
  31573. value = @base.send(:read_attribute_for_validation, attribute)
  31574. is_empty = value.respond_to?(:empty?) ? value.empty? : false
  31575. add(attribute, :empty, options) if value.nil? || is_empty
  31576. end
  31577. end
  31578. # Will add an error message to each of the attributes in +attributes+ that
  31579. # is blank (using Object#blank?).
  31580. #
  31581. # person.errors.add_on_blank(:name)
  31582. # person.errors.messages
  31583. # # => {:name=>["can't be blank"]}
  31584. def add_on_blank(attributes, options = {})
  31585. Array(attributes).each do |attribute|
  31586. value = @base.send(:read_attribute_for_validation, attribute)
  31587. add(attribute, :blank, options) if value.blank?
  31588. end
  31589. end
  31590. # Returns +true+ if an error on the attribute with the given message is
  31591. # present, +false+ otherwise. +message+ is treated the same as for +add+.
  31592. #
  31593. # person.errors.add :name, :blank
  31594. # person.errors.added? :name, :blank # => true
  31595. def added?(attribute, message = nil, options = {})
  31596. message = normalize_message(attribute, message, options)
  31597. self[attribute].include? message
  31598. end
  31599. # Returns all the full error messages in an array.
  31600. #
  31601. # class Person
  31602. # validates_presence_of :name, :address, :email
  31603. # validates_length_of :name, in: 5..30
  31604. # end
  31605. #
  31606. # person = Person.create(address: '123 First St.')
  31607. # person.errors.full_messages
  31608. # # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
  31609. def full_messages
  31610. map { |attribute, message| full_message(attribute, message) }
  31611. end
  31612. # Returns a full message for a given attribute.
  31613. #
  31614. # person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
  31615. def full_message(attribute, message)
  31616. return message if attribute == :base
  31617. attr_name = attribute.to_s.tr('.', '_').humanize
  31618. attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
  31619. I18n.t(:"errors.format", {
  31620. :default => "%{attribute} %{message}",
  31621. :attribute => attr_name,
  31622. :message => message
  31623. })
  31624. end
  31625. # Translates an error message in its default scope
  31626. # (<tt>activemodel.errors.messages</tt>).
  31627. #
  31628. # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
  31629. # if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if
  31630. # that is not there also, it returns the translation of the default message
  31631. # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
  31632. # name, translated attribute name and the value are available for
  31633. # interpolation.
  31634. #
  31635. # When using inheritance in your models, it will check all the inherited
  31636. # models too, but only if the model itself hasn't been found. Say you have
  31637. # <tt>class Admin < User; end</tt> and you wanted the translation for
  31638. # the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
  31639. # it looks for these translations:
  31640. #
  31641. # * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
  31642. # * <tt>activemodel.errors.models.admin.blank</tt>
  31643. # * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
  31644. # * <tt>activemodel.errors.models.user.blank</tt>
  31645. # * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
  31646. # * <tt>activemodel.errors.messages.blank</tt>
  31647. # * <tt>errors.attributes.title.blank</tt>
  31648. # * <tt>errors.messages.blank</tt>
  31649. def generate_message(attribute, type = :invalid, options = {})
  31650. type = options.delete(:message) if options[:message].is_a?(Symbol)
  31651. if @base.class.respond_to?(:i18n_scope)
  31652. defaults = @base.class.lookup_ancestors.map do |klass|
  31653. [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
  31654. :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
  31655. end
  31656. else
  31657. defaults = []
  31658. end
  31659. defaults << options.delete(:message)
  31660. defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
  31661. defaults << :"errors.attributes.#{attribute}.#{type}"
  31662. defaults << :"errors.messages.#{type}"
  31663. defaults.compact!
  31664. defaults.flatten!
  31665. key = defaults.shift
  31666. value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
  31667. options = {
  31668. :default => defaults,
  31669. :model => @base.class.model_name.human,
  31670. :attribute => @base.class.human_attribute_name(attribute),
  31671. :value => value
  31672. }.merge!(options)
  31673. I18n.translate(key, options)
  31674. end
  31675. private
  31676. def normalize_message(attribute, message, options)
  31677. message ||= :invalid
  31678. case message
  31679. when Symbol
  31680. generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
  31681. when Proc
  31682. message.call
  31683. else
  31684. message
  31685. end
  31686. end
  31687. end
  31688. # Raised when a validation cannot be corrected by end users and are considered
  31689. # exceptional.
  31690. #
  31691. # class Person
  31692. # include ActiveModel::Validations
  31693. #
  31694. # attr_accessor :name
  31695. #
  31696. # validates_presence_of :name, strict: true
  31697. # end
  31698. #
  31699. # person = Person.new
  31700. # person.name = nil
  31701. # person.valid?
  31702. # # => ActiveModel::StrictValidationFailed: Name can't be blank
  31703. class StrictValidationFailed < StandardError
  31704. end
  31705. end
  31706. module ActiveModel
  31707. # Raised when forbidden attributes are used for mass assignment.
  31708. #
  31709. # class Person < ActiveRecord::Base
  31710. # end
  31711. #
  31712. # params = ActionController::Parameters.new(name: 'Bob')
  31713. # Person.new(params)
  31714. # # => ActiveModel::ForbiddenAttributesError
  31715. #
  31716. # params.permit!
  31717. # Person.new(params)
  31718. # # => #<Person id: nil, name: "Bob">
  31719. class ForbiddenAttributesError < StandardError
  31720. end
  31721. module ForbiddenAttributesProtection # :nodoc:
  31722. protected
  31723. def sanitize_for_mass_assignment(attributes)
  31724. if attributes.respond_to?(:permitted?) && !attributes.permitted?
  31725. raise ActiveModel::ForbiddenAttributesError
  31726. else
  31727. attributes
  31728. end
  31729. end
  31730. end
  31731. end
  31732. module ActiveModel
  31733. module Lint
  31734. # == Active \Model \Lint \Tests
  31735. #
  31736. # You can test whether an object is compliant with the Active \Model API by
  31737. # including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will
  31738. # include tests that tell you whether your object is fully compliant,
  31739. # or if not, which aspects of the API are not implemented.
  31740. #
  31741. # Note an object is not required to implement all APIs in order to work
  31742. # with Action Pack. This module only intends to provide guidance in case
  31743. # you want all features out of the box.
  31744. #
  31745. # These tests do not attempt to determine the semantic correctness of the
  31746. # returned values. For instance, you could implement <tt>valid?</tt> to
  31747. # always return true, and the tests would pass. It is up to you to ensure
  31748. # that the values are semantically meaningful.
  31749. #
  31750. # Objects you pass in are expected to return a compliant object from a call
  31751. # to <tt>to_model</tt>. It is perfectly fine for <tt>to_model</tt> to return
  31752. # +self+.
  31753. module Tests
  31754. # == Responds to <tt>to_key</tt>
  31755. #
  31756. # Returns an Enumerable of all (primary) key attributes
  31757. # or nil if <tt>model.persisted?</tt> is false. This is used by
  31758. # <tt>dom_id</tt> to generate unique ids for the object.
  31759. def test_to_key
  31760. assert model.respond_to?(:to_key), "The model should respond to to_key"
  31761. def model.persisted?() false end
  31762. assert model.to_key.nil?, "to_key should return nil when `persisted?` returns false"
  31763. end
  31764. # == Responds to <tt>to_param</tt>
  31765. #
  31766. # Returns a string representing the object's key suitable for use in URLs
  31767. # or +nil+ if <tt>model.persisted?</tt> is +false+.
  31768. #
  31769. # Implementers can decide to either raise an exception or provide a
  31770. # default in case the record uses a composite primary key. There are no
  31771. # tests for this behavior in lint because it doesn't make sense to force
  31772. # any of the possible implementation strategies on the implementer.
  31773. # However, if the resource is not persisted?, then <tt>to_param</tt>
  31774. # should always return +nil+.
  31775. def test_to_param
  31776. assert model.respond_to?(:to_param), "The model should respond to to_param"
  31777. def model.to_key() [1] end
  31778. def model.persisted?() false end
  31779. assert model.to_param.nil?, "to_param should return nil when `persisted?` returns false"
  31780. end
  31781. # == Responds to <tt>to_partial_path</tt>
  31782. #
  31783. # Returns a string giving a relative path. This is used for looking up
  31784. # partials. For example, a BlogPost model might return "blog_posts/blog_post"
  31785. def test_to_partial_path
  31786. assert model.respond_to?(:to_partial_path), "The model should respond to to_partial_path"
  31787. assert_kind_of String, model.to_partial_path
  31788. end
  31789. # == Responds to <tt>persisted?</tt>
  31790. #
  31791. # Returns a boolean that specifies whether the object has been persisted
  31792. # yet. This is used when calculating the URL for an object. If the object
  31793. # is not persisted, a form for that object, for instance, will route to
  31794. # the create action. If it is persisted, a form for the object will routes
  31795. # to the update action.
  31796. def test_persisted?
  31797. assert model.respond_to?(:persisted?), "The model should respond to persisted?"
  31798. assert_boolean model.persisted?, "persisted?"
  31799. end
  31800. # == \Naming
  31801. #
  31802. # Model.model_name must return a string with some convenience methods:
  31803. # <tt>:human</tt>, <tt>:singular</tt> and <tt>:plural</tt>. Check
  31804. # ActiveModel::Naming for more information.
  31805. def test_model_naming
  31806. assert model.class.respond_to?(:model_name), "The model should respond to model_name"
  31807. model_name = model.class.model_name
  31808. assert model_name.respond_to?(:to_str)
  31809. assert model_name.human.respond_to?(:to_str)
  31810. assert model_name.singular.respond_to?(:to_str)
  31811. assert model_name.plural.respond_to?(:to_str)
  31812. end
  31813. # == \Errors Testing
  31814. #
  31815. # Returns an object that implements [](attribute) defined which returns an
  31816. # Array of Strings that are the errors for the attribute in question.
  31817. # If localization is used, the Strings should be localized for the current
  31818. # locale. If no error is present, this method should return an empty Array.
  31819. def test_errors_aref
  31820. assert model.respond_to?(:errors), "The model should respond to errors"
  31821. assert model.errors[:hello].is_a?(Array), "errors#[] should return an Array"
  31822. end
  31823. private
  31824. def model
  31825. assert @model.respond_to?(:to_model), "The object should respond_to to_model"
  31826. @model.to_model
  31827. end
  31828. def assert_boolean(result, name)
  31829. assert result == true || result == false, "#{name} should be a boolean"
  31830. end
  31831. end
  31832. end
  31833. end
  31834. module ActiveModel
  31835. # == Active \Model Basic \Model
  31836. #
  31837. # Includes the required interface for an object to interact with
  31838. # <tt>ActionPack</tt>, using different <tt>ActiveModel</tt> modules.
  31839. # It includes model name introspections, conversions, translations and
  31840. # validations. Besides that, it allows you to initialize the object with a
  31841. # hash of attributes, pretty much like <tt>ActiveRecord</tt> does.
  31842. #
  31843. # A minimal implementation could be:
  31844. #
  31845. # class Person
  31846. # include ActiveModel::Model
  31847. # attr_accessor :name, :age
  31848. # end
  31849. #
  31850. # person = Person.new(name: 'bob', age: '18')
  31851. # person.name # => 'bob'
  31852. # person.age # => 18
  31853. #
  31854. # Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt>
  31855. # to return +false+, which is the most common case. You may want to override
  31856. # it in your class to simulate a different scenario:
  31857. #
  31858. # class Person
  31859. # include ActiveModel::Model
  31860. # attr_accessor :id, :name
  31861. #
  31862. # def persisted?
  31863. # self.id == 1
  31864. # end
  31865. # end
  31866. #
  31867. # person = Person.new(id: 1, name: 'bob')
  31868. # person.persisted? # => true
  31869. #
  31870. # Also, if for some reason you need to run code on <tt>initialize</tt>, make
  31871. # sure you call +super+ if you want the attributes hash initialization to
  31872. # happen.
  31873. #
  31874. # class Person
  31875. # include ActiveModel::Model
  31876. # attr_accessor :id, :name, :omg
  31877. #
  31878. # def initialize(attributes={})
  31879. # super
  31880. # @omg ||= true
  31881. # end
  31882. # end
  31883. #
  31884. # person = Person.new(id: 1, name: 'bob')
  31885. # person.omg # => true
  31886. #
  31887. # For more detailed information on other functionalities available, please
  31888. # refer to the specific modules included in <tt>ActiveModel::Model</tt>
  31889. # (see below).
  31890. module Model
  31891. def self.included(base) #:nodoc:
  31892. base.class_eval do
  31893. extend ActiveModel::Naming
  31894. extend ActiveModel::Translation
  31895. include ActiveModel::Validations
  31896. include ActiveModel::Conversion
  31897. end
  31898. end
  31899. # Initializes a new model with the given +params+.
  31900. #
  31901. # class Person
  31902. # include ActiveModel::Model
  31903. # attr_accessor :name, :age
  31904. # end
  31905. #
  31906. # person = Person.new(name: 'bob', age: '18')
  31907. # person.name # => "bob"
  31908. # person.age # => 18
  31909. def initialize(params={})
  31910. params.each do |attr, value|
  31911. self.public_send("#{attr}=", value)
  31912. end if params
  31913. end
  31914. # Indicates if the model is persisted. Default is +false+.
  31915. #
  31916. # class Person
  31917. # include ActiveModel::Model
  31918. # attr_accessor :id, :name
  31919. # end
  31920. #
  31921. # person = Person.new(id: 1, name: 'bob')
  31922. # person.persisted? # => false
  31923. def persisted?
  31924. false
  31925. end
  31926. end
  31927. end
  31928. require 'active_support/core_ext/hash/except'
  31929. require 'active_support/core_ext/module/introspection'
  31930. module ActiveModel
  31931. class Name
  31932. include Comparable
  31933. attr_reader :singular, :plural, :element, :collection,
  31934. :singular_route_key, :route_key, :param_key, :i18n_key,
  31935. :name
  31936. alias_method :cache_key, :collection
  31937. ##
  31938. # :method: ==
  31939. #
  31940. # :call-seq:
  31941. # ==(other)
  31942. #
  31943. # Equivalent to <tt>String#==</tt>. Returns +true+ if the class name and
  31944. # +other+ are equal, otherwise +false+.
  31945. #
  31946. # class BlogPost
  31947. # extend ActiveModel::Naming
  31948. # end
  31949. #
  31950. # BlogPost.model_name == 'BlogPost' # => true
  31951. # BlogPost.model_name == 'Blog Post' # => false
  31952. ##
  31953. # :method: ===
  31954. #
  31955. # :call-seq:
  31956. # ===(other)
  31957. #
  31958. # Equivalent to <tt>#==</tt>.
  31959. #
  31960. # class BlogPost
  31961. # extend ActiveModel::Naming
  31962. # end
  31963. #
  31964. # BlogPost.model_name === 'BlogPost' # => true
  31965. # BlogPost.model_name === 'Blog Post' # => false
  31966. ##
  31967. # :method: <=>
  31968. #
  31969. # :call-seq:
  31970. # ==(other)
  31971. #
  31972. # Equivalent to <tt>String#<=></tt>.
  31973. #
  31974. # class BlogPost
  31975. # extend ActiveModel::Naming
  31976. # end
  31977. #
  31978. # BlogPost.model_name <=> 'BlogPost' # => 0
  31979. # BlogPost.model_name <=> 'Blog' # => 1
  31980. # BlogPost.model_name <=> 'BlogPosts' # => -1
  31981. ##
  31982. # :method: =~
  31983. #
  31984. # :call-seq:
  31985. # =~(regexp)
  31986. #
  31987. # Equivalent to <tt>String#=~</tt>. Match the class name against the given
  31988. # regexp. Returns the position where the match starts or +nil+ if there is
  31989. # no match.
  31990. #
  31991. # class BlogPost
  31992. # extend ActiveModel::Naming
  31993. # end
  31994. #
  31995. # BlogPost.model_name =~ /Post/ # => 4
  31996. # BlogPost.model_name =~ /\d/ # => nil
  31997. ##
  31998. # :method: !~
  31999. #
  32000. # :call-seq:
  32001. # !~(regexp)
  32002. #
  32003. # Equivalent to <tt>String#!~</tt>. Match the class name against the given
  32004. # regexp. Returns +true+ if there is no match, otherwise +false+.
  32005. #
  32006. # class BlogPost
  32007. # extend ActiveModel::Naming
  32008. # end
  32009. #
  32010. # BlogPost.model_name !~ /Post/ # => false
  32011. # BlogPost.model_name !~ /\d/ # => true
  32012. ##
  32013. # :method: eql?
  32014. #
  32015. # :call-seq:
  32016. # eql?(other)
  32017. #
  32018. # Equivalent to <tt>String#eql?</tt>. Returns +true+ if the class name and
  32019. # +other+ have the same length and content, otherwise +false+.
  32020. #
  32021. # class BlogPost
  32022. # extend ActiveModel::Naming
  32023. # end
  32024. #
  32025. # BlogPost.model_name.eql?('BlogPost') # => true
  32026. # BlogPost.model_name.eql?('Blog Post') # => false
  32027. ##
  32028. # :method: to_s
  32029. #
  32030. # :call-seq:
  32031. # to_s()
  32032. #
  32033. # Returns the class name.
  32034. #
  32035. # class BlogPost
  32036. # extend ActiveModel::Naming
  32037. # end
  32038. #
  32039. # BlogPost.model_name.to_s # => "BlogPost"
  32040. ##
  32041. # :method: to_str
  32042. #
  32043. # :call-seq:
  32044. # to_str()
  32045. #
  32046. # Equivalent to +to_s+.
  32047. delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
  32048. :to_str, :to => :name
  32049. # Returns a new ActiveModel::Name instance. By default, the +namespace+
  32050. # and +name+ option will take the namespace and name of the given class
  32051. # respectively.
  32052. #
  32053. # module Foo
  32054. # class Bar
  32055. # end
  32056. # end
  32057. #
  32058. # ActiveModel::Name.new(Foo::Bar).to_s
  32059. # # => "Foo::Bar"
  32060. def initialize(klass, namespace = nil, name = nil)
  32061. @name = name || klass.name
  32062. raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
  32063. @unnamespaced = @name.sub(/^#{namespace.name}::/, '') if namespace
  32064. @klass = klass
  32065. @singular = _singularize(@name)
  32066. @plural = ActiveSupport::Inflector.pluralize(@singular)
  32067. @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@name))
  32068. @human = ActiveSupport::Inflector.humanize(@element)
  32069. @collection = ActiveSupport::Inflector.tableize(@name)
  32070. @param_key = (namespace ? _singularize(@unnamespaced) : @singular)
  32071. @i18n_key = @name.underscore.to_sym
  32072. @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
  32073. @singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
  32074. @route_key << "_index" if @plural == @singular
  32075. end
  32076. # Transform the model name into a more humane format, using I18n. By default,
  32077. # it will underscore then humanize the class name.
  32078. #
  32079. # class BlogPost
  32080. # extend ActiveModel::Naming
  32081. # end
  32082. #
  32083. # BlogPost.model_name.human # => "Blog post"
  32084. #
  32085. # Specify +options+ with additional translating options.
  32086. def human(options={})
  32087. return @human unless @klass.respond_to?(:lookup_ancestors) &&
  32088. @klass.respond_to?(:i18n_scope)
  32089. defaults = @klass.lookup_ancestors.map do |klass|
  32090. klass.model_name.i18n_key
  32091. end
  32092. defaults << options[:default] if options[:default]
  32093. defaults << @human
  32094. options = { :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults }.merge!(options.except(:default))
  32095. I18n.translate(defaults.shift, options)
  32096. end
  32097. private
  32098. def _singularize(string, replacement='_')
  32099. ActiveSupport::Inflector.underscore(string).tr('/', replacement)
  32100. end
  32101. end
  32102. # == Active \Model \Naming
  32103. #
  32104. # Creates a +model_name+ method on your object.
  32105. #
  32106. # To implement, just extend ActiveModel::Naming in your object:
  32107. #
  32108. # class BookCover
  32109. # extend ActiveModel::Naming
  32110. # end
  32111. #
  32112. # BookCover.model_name # => "BookCover"
  32113. # BookCover.model_name.human # => "Book cover"
  32114. #
  32115. # BookCover.model_name.i18n_key # => :book_cover
  32116. # BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
  32117. #
  32118. # Providing the functionality that ActiveModel::Naming provides in your object
  32119. # is required to pass the Active Model Lint test. So either extending the
  32120. # provided method below, or rolling your own is required.
  32121. module Naming
  32122. # Returns an ActiveModel::Name object for module. It can be
  32123. # used to retrieve all kinds of naming-related information
  32124. # (See ActiveModel::Name for more information).
  32125. #
  32126. # class Person < ActiveModel::Model
  32127. # end
  32128. #
  32129. # Person.model_name # => Person
  32130. # Person.model_name.class # => ActiveModel::Name
  32131. # Person.model_name.singular # => "person"
  32132. # Person.model_name.plural # => "people"
  32133. def model_name
  32134. @_model_name ||= begin
  32135. namespace = self.parents.detect do |n|
  32136. n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
  32137. end
  32138. ActiveModel::Name.new(self, namespace)
  32139. end
  32140. end
  32141. # Returns the plural class name of a record or class.
  32142. #
  32143. # ActiveModel::Naming.plural(post) # => "posts"
  32144. # ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
  32145. def self.plural(record_or_class)
  32146. model_name_from_record_or_class(record_or_class).plural
  32147. end
  32148. # Returns the singular class name of a record or class.
  32149. #
  32150. # ActiveModel::Naming.singular(post) # => "post"
  32151. # ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
  32152. def self.singular(record_or_class)
  32153. model_name_from_record_or_class(record_or_class).singular
  32154. end
  32155. # Identifies whether the class name of a record or class is uncountable.
  32156. #
  32157. # ActiveModel::Naming.uncountable?(Sheep) # => true
  32158. # ActiveModel::Naming.uncountable?(Post) # => false
  32159. def self.uncountable?(record_or_class)
  32160. plural(record_or_class) == singular(record_or_class)
  32161. end
  32162. # Returns string to use while generating route names. It differs for
  32163. # namespaced models regarding whether it's inside isolated engine.
  32164. #
  32165. # # For isolated engine:
  32166. # ActiveModel::Naming.singular_route_key(Blog::Post) #=> post
  32167. #
  32168. # # For shared engine:
  32169. # ActiveModel::Naming.singular_route_key(Blog::Post) #=> blog_post
  32170. def self.singular_route_key(record_or_class)
  32171. model_name_from_record_or_class(record_or_class).singular_route_key
  32172. end
  32173. # Returns string to use while generating route names. It differs for
  32174. # namespaced models regarding whether it's inside isolated engine.
  32175. #
  32176. # # For isolated engine:
  32177. # ActiveModel::Naming.route_key(Blog::Post) #=> posts
  32178. #
  32179. # # For shared engine:
  32180. # ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
  32181. #
  32182. # The route key also considers if the noun is uncountable and, in
  32183. # such cases, automatically appends _index.
  32184. def self.route_key(record_or_class)
  32185. model_name_from_record_or_class(record_or_class).route_key
  32186. end
  32187. # Returns string to use for params names. It differs for
  32188. # namespaced models regarding whether it's inside isolated engine.
  32189. #
  32190. # # For isolated engine:
  32191. # ActiveModel::Naming.param_key(Blog::Post) #=> post
  32192. #
  32193. # # For shared engine:
  32194. # ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
  32195. def self.param_key(record_or_class)
  32196. model_name_from_record_or_class(record_or_class).param_key
  32197. end
  32198. def self.model_name_from_record_or_class(record_or_class) #:nodoc:
  32199. if record_or_class.respond_to?(:model_name)
  32200. record_or_class.model_name
  32201. elsif record_or_class.respond_to?(:to_model)
  32202. record_or_class.to_model.class.model_name
  32203. else
  32204. record_or_class.class.model_name
  32205. end
  32206. end
  32207. private_class_method :model_name_from_record_or_class
  32208. end
  32209. end
  32210. require "active_model"
  32211. require "rails"
  32212. module ActiveModel
  32213. class Railtie < Rails::Railtie # :nodoc:
  32214. config.eager_load_namespaces << ActiveModel
  32215. initializer "active_model.secure_password" do
  32216. ActiveModel::SecurePassword.min_cost = Rails.env.test?
  32217. end
  32218. end
  32219. end
  32220. module ActiveModel
  32221. module SecurePassword
  32222. extend ActiveSupport::Concern
  32223. class << self; attr_accessor :min_cost; end
  32224. self.min_cost = false
  32225. module ClassMethods
  32226. # Adds methods to set and authenticate against a BCrypt password.
  32227. # This mechanism requires you to have a password_digest attribute.
  32228. #
  32229. # Validations for presence of password on create, confirmation of password
  32230. # (using a +password_confirmation+ attribute) are automatically added. If
  32231. # you wish to turn off validations, pass <tt>validations: false</tt> as an
  32232. # argument. You can add more validations by hand if need be.
  32233. #
  32234. # If you don't need the confirmation validation, just don't set any
  32235. # value to the password_confirmation attribute and the the validation
  32236. # will not be triggered.
  32237. #
  32238. # You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use #has_secure_password:
  32239. #
  32240. # gem 'bcrypt-ruby', '~> 3.0.0'
  32241. #
  32242. # Example using Active Record (which automatically includes ActiveModel::SecurePassword):
  32243. #
  32244. # # Schema: User(name:string, password_digest:string)
  32245. # class User < ActiveRecord::Base
  32246. # has_secure_password
  32247. # end
  32248. #
  32249. # user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
  32250. # user.save # => false, password required
  32251. # user.password = 'mUc3m00RsqyRe'
  32252. # user.save # => false, confirmation doesn't match
  32253. # user.password_confirmation = 'mUc3m00RsqyRe'
  32254. # user.save # => true
  32255. # user.authenticate('notright') # => false
  32256. # user.authenticate('mUc3m00RsqyRe') # => user
  32257. # User.find_by_name('david').try(:authenticate, 'notright') # => false
  32258. # User.find_by_name('david').try(:authenticate, 'mUc3m00RsqyRe') # => user
  32259. def has_secure_password(options = {})
  32260. # Load bcrypt-ruby only when has_secure_password is used.
  32261. # This is to avoid ActiveModel (and by extension the entire framework)
  32262. # being dependent on a binary library.
  32263. gem 'bcrypt-ruby', '~> 3.0.0'
  32264. require 'bcrypt'
  32265. attr_reader :password
  32266. if options.fetch(:validations, true)
  32267. validates_confirmation_of :password
  32268. validates_presence_of :password, :on => :create
  32269. before_create { raise "Password digest missing on new record" if password_digest.blank? }
  32270. end
  32271. include InstanceMethodsOnActivation
  32272. if respond_to?(:attributes_protected_by_default)
  32273. def self.attributes_protected_by_default #:nodoc:
  32274. super + ['password_digest']
  32275. end
  32276. end
  32277. end
  32278. end
  32279. module InstanceMethodsOnActivation
  32280. # Returns +self+ if the password is correct, otherwise +false+.
  32281. #
  32282. # class User < ActiveRecord::Base
  32283. # has_secure_password validations: false
  32284. # end
  32285. #
  32286. # user = User.new(name: 'david', password: 'mUc3m00RsqyRe')
  32287. # user.save
  32288. # user.authenticate('notright') # => false
  32289. # user.authenticate('mUc3m00RsqyRe') # => user
  32290. def authenticate(unencrypted_password)
  32291. BCrypt::Password.new(password_digest) == unencrypted_password && self
  32292. end
  32293. # Encrypts the password into the +password_digest+ attribute, only if the
  32294. # new password is not blank.
  32295. #
  32296. # class User < ActiveRecord::Base
  32297. # has_secure_password validations: false
  32298. # end
  32299. #
  32300. # user = User.new
  32301. # user.password = nil
  32302. # user.password_digest # => nil
  32303. # user.password = 'mUc3m00RsqyRe'
  32304. # user.password_digest # => "$2a$10$4LEA7r4YmNHtvlAvHhsYAeZmk/xeUVtMTYqwIvYY76EW5GUqDiP4."
  32305. def password=(unencrypted_password)
  32306. unless unencrypted_password.blank?
  32307. @password = unencrypted_password
  32308. cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine::DEFAULT_COST
  32309. self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
  32310. end
  32311. end
  32312. end
  32313. end
  32314. end
  32315. require 'active_support/core_ext/hash/except'
  32316. require 'active_support/core_ext/hash/slice'
  32317. module ActiveModel
  32318. # == Active \Model \Serialization
  32319. #
  32320. # Provides a basic serialization to a serializable_hash for your object.
  32321. #
  32322. # A minimal implementation could be:
  32323. #
  32324. # class Person
  32325. # include ActiveModel::Serialization
  32326. #
  32327. # attr_accessor :name
  32328. #
  32329. # def attributes
  32330. # {'name' => nil}
  32331. # end
  32332. # end
  32333. #
  32334. # Which would provide you with:
  32335. #
  32336. # person = Person.new
  32337. # person.serializable_hash # => {"name"=>nil}
  32338. # person.name = "Bob"
  32339. # person.serializable_hash # => {"name"=>"Bob"}
  32340. #
  32341. # You need to declare an attributes hash which contains the attributes you
  32342. # want to serialize. Attributes must be strings, not symbols. When called,
  32343. # serializable hash will use instance methods that match the name of the
  32344. # attributes hash's keys. In order to override this behavior, take a look at
  32345. # the private method +read_attribute_for_serialization+.
  32346. #
  32347. # Most of the time though, you will want to include the JSON or XML
  32348. # serializations. Both of these modules automatically include the
  32349. # <tt>ActiveModel::Serialization</tt> module, so there is no need to
  32350. # explicitly include it.
  32351. #
  32352. # A minimal implementation including XML and JSON would be:
  32353. #
  32354. # class Person
  32355. # include ActiveModel::Serializers::JSON
  32356. # include ActiveModel::Serializers::Xml
  32357. #
  32358. # attr_accessor :name
  32359. #
  32360. # def attributes
  32361. # {'name' => nil}
  32362. # end
  32363. # end
  32364. #
  32365. # Which would provide you with:
  32366. #
  32367. # person = Person.new
  32368. # person.serializable_hash # => {"name"=>nil}
  32369. # person.as_json # => {"name"=>nil}
  32370. # person.to_json # => "{\"name\":null}"
  32371. # person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
  32372. #
  32373. # person.name = "Bob"
  32374. # person.serializable_hash # => {"name"=>"Bob"}
  32375. # person.as_json # => {"name"=>"Bob"}
  32376. # person.to_json # => "{\"name\":\"Bob\"}"
  32377. # person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
  32378. #
  32379. # Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and
  32380. # <tt>:include</tt>. The following are all valid examples:
  32381. #
  32382. # person.serializable_hash(only: 'name')
  32383. # person.serializable_hash(include: :address)
  32384. # person.serializable_hash(include: { address: { only: 'city' }})
  32385. module Serialization
  32386. # Returns a serialized hash of your object.
  32387. #
  32388. # class Person
  32389. # include ActiveModel::Serialization
  32390. #
  32391. # attr_accessor :name, :age
  32392. #
  32393. # def attributes
  32394. # {'name' => nil, 'age' => nil}
  32395. # end
  32396. #
  32397. # def capitalized_name
  32398. # name.capitalize
  32399. # end
  32400. # end
  32401. #
  32402. # person = Person.new
  32403. # person.name = 'bob'
  32404. # person.age = 22
  32405. # person.serializable_hash # => {"name"=>"bob", "age"=>22}
  32406. # person.serializable_hash(only: :name) # => {"name"=>"bob"}
  32407. # person.serializable_hash(except: :name) # => {"age"=>22}
  32408. # person.serializable_hash(methods: :capitalized_name)
  32409. # # => {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}
  32410. def serializable_hash(options = nil)
  32411. options ||= {}
  32412. attribute_names = attributes.keys
  32413. if only = options[:only]
  32414. attribute_names &= Array(only).map(&:to_s)
  32415. elsif except = options[:except]
  32416. attribute_names -= Array(except).map(&:to_s)
  32417. end
  32418. hash = {}
  32419. attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
  32420. Array(options[:methods]).each { |m| hash[m.to_s] = send(m) if respond_to?(m) }
  32421. serializable_add_includes(options) do |association, records, opts|
  32422. hash[association.to_s] = if records.respond_to?(:to_ary)
  32423. records.to_ary.map { |a| a.serializable_hash(opts) }
  32424. else
  32425. records.serializable_hash(opts)
  32426. end
  32427. end
  32428. hash
  32429. end
  32430. private
  32431. # Hook method defining how an attribute value should be retrieved for
  32432. # serialization. By default this is assumed to be an instance named after
  32433. # the attribute. Override this method in subclasses should you need to
  32434. # retrieve the value for a given attribute differently:
  32435. #
  32436. # class MyClass
  32437. # include ActiveModel::Validations
  32438. #
  32439. # def initialize(data = {})
  32440. # @data = data
  32441. # end
  32442. #
  32443. # def read_attribute_for_serialization(key)
  32444. # @data[key]
  32445. # end
  32446. # end
  32447. alias :read_attribute_for_serialization :send
  32448. # Add associations specified via the <tt>:include</tt> option.
  32449. #
  32450. # Expects a block that takes as arguments:
  32451. # +association+ - name of the association
  32452. # +records+ - the association record(s) to be serialized
  32453. # +opts+ - options for the association records
  32454. def serializable_add_includes(options = {}) #:nodoc:
  32455. return unless includes = options[:include]
  32456. unless includes.is_a?(Hash)
  32457. includes = Hash[Array(includes).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
  32458. end
  32459. includes.each do |association, opts|
  32460. if records = send(association)
  32461. yield association, records, opts
  32462. end
  32463. end
  32464. end
  32465. end
  32466. end
  32467. require 'active_support/json'
  32468. module ActiveModel
  32469. module Serializers
  32470. # == Active Model JSON Serializer
  32471. module JSON
  32472. extend ActiveSupport::Concern
  32473. include ActiveModel::Serialization
  32474. included do
  32475. extend ActiveModel::Naming
  32476. class_attribute :include_root_in_json
  32477. self.include_root_in_json = false
  32478. end
  32479. # Returns a hash representing the model. Some configuration can be
  32480. # passed through +options+.
  32481. #
  32482. # The option <tt>include_root_in_json</tt> controls the top-level behavior
  32483. # of +as_json+. If +true+, +as_json+ will emit a single root node named
  32484. # after the object's type. The default value for <tt>include_root_in_json</tt>
  32485. # option is +false+.
  32486. #
  32487. # user = User.find(1)
  32488. # user.as_json
  32489. # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
  32490. # # "created_at" => "2006/08/01", "awesome" => true}
  32491. #
  32492. # ActiveRecord::Base.include_root_in_json = true
  32493. #
  32494. # user.as_json
  32495. # # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
  32496. # # "created_at" => "2006/08/01", "awesome" => true } }
  32497. #
  32498. # This behavior can also be achieved by setting the <tt>:root</tt> option
  32499. # to +true+ as in:
  32500. #
  32501. # user = User.find(1)
  32502. # user.as_json(root: true)
  32503. # # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
  32504. # # "created_at" => "2006/08/01", "awesome" => true } }
  32505. #
  32506. # Without any +options+, the returned Hash will include all the model's
  32507. # attributes.
  32508. #
  32509. # user = User.find(1)
  32510. # user.as_json
  32511. # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
  32512. # # "created_at" => "2006/08/01", "awesome" => true}
  32513. #
  32514. # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
  32515. # the attributes included, and work similar to the +attributes+ method.
  32516. #
  32517. # user.as_json(only: [:id, :name])
  32518. # # => { "id" => 1, "name" => "Konata Izumi" }
  32519. #
  32520. # user.as_json(except: [:id, :created_at, :age])
  32521. # # => { "name" => "Konata Izumi", "awesome" => true }
  32522. #
  32523. # To include the result of some method calls on the model use <tt>:methods</tt>:
  32524. #
  32525. # user.as_json(methods: :permalink)
  32526. # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
  32527. # # "created_at" => "2006/08/01", "awesome" => true,
  32528. # # "permalink" => "1-konata-izumi" }
  32529. #
  32530. # To include associations use <tt>:include</tt>:
  32531. #
  32532. # user.as_json(include: :posts)
  32533. # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
  32534. # # "created_at" => "2006/08/01", "awesome" => true,
  32535. # # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
  32536. # # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
  32537. #
  32538. # Second level and higher order associations work as well:
  32539. #
  32540. # user.as_json(include: { posts: {
  32541. # include: { comments: {
  32542. # only: :body } },
  32543. # only: :title } })
  32544. # # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
  32545. # # "created_at" => "2006/08/01", "awesome" => true,
  32546. # # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
  32547. # # "title" => "Welcome to the weblog" },
  32548. # # { "comments" => [ { "body" => "Don't think too hard" } ],
  32549. # # "title" => "So I was thinking" } ] }
  32550. def as_json(options = nil)
  32551. root = if options && options.key?(:root)
  32552. options[:root]
  32553. else
  32554. include_root_in_json
  32555. end
  32556. if root
  32557. root = self.class.model_name.element if root == true
  32558. { root => serializable_hash(options) }
  32559. else
  32560. serializable_hash(options)
  32561. end
  32562. end
  32563. # Sets the model +attributes+ from a JSON string. Returns +self+.
  32564. #
  32565. # class Person
  32566. # include ActiveModel::Serializers::JSON
  32567. #
  32568. # attr_accessor :name, :age, :awesome
  32569. #
  32570. # def attributes=(hash)
  32571. # hash.each do |key, value|
  32572. # instance_variable_set("@#{key}", value)
  32573. # end
  32574. # end
  32575. #
  32576. # def attributes
  32577. # instance_values
  32578. # end
  32579. # end
  32580. #
  32581. # json = { name: 'bob', age: 22, awesome:true }.to_json
  32582. # person = Person.new
  32583. # person.from_json(json) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
  32584. # person.name # => "bob"
  32585. # person.age # => 22
  32586. # person.awesome # => true
  32587. #
  32588. # The default value for +include_root+ is +false+. You can change it to
  32589. # +true+ if the given JSON string includes a single root node.
  32590. #
  32591. # json = { person: { name: 'bob', age: 22, awesome:true } }.to_json
  32592. # person = Person.new
  32593. # person.from_json(json) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
  32594. # person.name # => "bob"
  32595. # person.age # => 22
  32596. # person.awesome # => true
  32597. def from_json(json, include_root=include_root_in_json)
  32598. hash = ActiveSupport::JSON.decode(json)
  32599. hash = hash.values.first if include_root
  32600. self.attributes = hash
  32601. self
  32602. end
  32603. end
  32604. end
  32605. end
  32606. require 'active_support/core_ext/class/attribute_accessors'
  32607. require 'active_support/core_ext/array/conversions'
  32608. require 'active_support/core_ext/hash/conversions'
  32609. require 'active_support/core_ext/hash/slice'
  32610. require 'active_support/core_ext/time/acts_like'
  32611. module ActiveModel
  32612. module Serializers
  32613. # == Active Model XML Serializer
  32614. module Xml
  32615. extend ActiveSupport::Concern
  32616. include ActiveModel::Serialization
  32617. included do
  32618. extend ActiveModel::Naming
  32619. end
  32620. class Serializer #:nodoc:
  32621. class Attribute #:nodoc:
  32622. attr_reader :name, :value, :type
  32623. def initialize(name, serializable, value)
  32624. @name, @serializable = name, serializable
  32625. if value.acts_like?(:time) && value.respond_to?(:in_time_zone)
  32626. value = value.in_time_zone
  32627. end
  32628. @value = value
  32629. @type = compute_type
  32630. end
  32631. def decorations
  32632. decorations = {}
  32633. decorations[:encoding] = 'base64' if type == :binary
  32634. decorations[:type] = (type == :string) ? nil : type
  32635. decorations[:nil] = true if value.nil?
  32636. decorations
  32637. end
  32638. protected
  32639. def compute_type
  32640. return if value.nil?
  32641. type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name]
  32642. type ||= :string if value.respond_to?(:to_str)
  32643. type ||= :yaml
  32644. type
  32645. end
  32646. end
  32647. class MethodAttribute < Attribute #:nodoc:
  32648. end
  32649. attr_reader :options
  32650. def initialize(serializable, options = nil)
  32651. @serializable = serializable
  32652. @options = options ? options.dup : {}
  32653. end
  32654. def serializable_hash
  32655. @serializable.serializable_hash(@options.except(:include))
  32656. end
  32657. def serializable_collection
  32658. methods = Array(options[:methods]).map(&:to_s)
  32659. serializable_hash.map do |name, value|
  32660. name = name.to_s
  32661. if methods.include?(name)
  32662. self.class::MethodAttribute.new(name, @serializable, value)
  32663. else
  32664. self.class::Attribute.new(name, @serializable, value)
  32665. end
  32666. end
  32667. end
  32668. def serialize
  32669. require 'builder' unless defined? ::Builder
  32670. options[:indent] ||= 2
  32671. options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
  32672. @builder = options[:builder]
  32673. @builder.instruct! unless options[:skip_instruct]
  32674. root = (options[:root] || @serializable.class.model_name.element).to_s
  32675. root = ActiveSupport::XmlMini.rename_key(root, options)
  32676. args = [root]
  32677. args << {:xmlns => options[:namespace]} if options[:namespace]
  32678. args << {:type => options[:type]} if options[:type] && !options[:skip_types]
  32679. @builder.tag!(*args) do
  32680. add_attributes_and_methods
  32681. add_includes
  32682. add_extra_behavior
  32683. add_procs
  32684. yield @builder if block_given?
  32685. end
  32686. end
  32687. private
  32688. def add_extra_behavior
  32689. end
  32690. def add_attributes_and_methods
  32691. serializable_collection.each do |attribute|
  32692. key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
  32693. ActiveSupport::XmlMini.to_tag(key, attribute.value,
  32694. options.merge(attribute.decorations))
  32695. end
  32696. end
  32697. def add_includes
  32698. @serializable.send(:serializable_add_includes, options) do |association, records, opts|
  32699. add_associations(association, records, opts)
  32700. end
  32701. end
  32702. # TODO: This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
  32703. def add_associations(association, records, opts)
  32704. merged_options = opts.merge(options.slice(:builder, :indent))
  32705. merged_options[:skip_instruct] = true
  32706. [:skip_types, :dasherize, :camelize].each do |key|
  32707. merged_options[key] = options[key] if merged_options[key].nil? && !options[key].nil?
  32708. end
  32709. if records.respond_to?(:to_ary)
  32710. records = records.to_ary
  32711. tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
  32712. type = options[:skip_types] ? { } : {:type => "array"}
  32713. association_name = association.to_s.singularize
  32714. merged_options[:root] = association_name
  32715. if records.empty?
  32716. @builder.tag!(tag, type)
  32717. else
  32718. @builder.tag!(tag, type) do
  32719. records.each do |record|
  32720. if options[:skip_types]
  32721. record_type = {}
  32722. else
  32723. record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
  32724. record_type = {:type => record_class}
  32725. end
  32726. record.to_xml merged_options.merge(record_type)
  32727. end
  32728. end
  32729. end
  32730. else
  32731. merged_options[:root] = association.to_s
  32732. unless records.class.to_s.underscore == association.to_s
  32733. merged_options[:type] = records.class.name
  32734. end
  32735. records.to_xml merged_options
  32736. end
  32737. end
  32738. def add_procs
  32739. if procs = options.delete(:procs)
  32740. Array(procs).each do |proc|
  32741. if proc.arity == 1
  32742. proc.call(options)
  32743. else
  32744. proc.call(options, @serializable)
  32745. end
  32746. end
  32747. end
  32748. end
  32749. end
  32750. # Returns XML representing the model. Configuration can be
  32751. # passed through +options+.
  32752. #
  32753. # Without any +options+, the returned XML string will include all the
  32754. # model's attributes.
  32755. #
  32756. # user = User.find(1)
  32757. # user.to_xml
  32758. #
  32759. # <?xml version="1.0" encoding="UTF-8"?>
  32760. # <user>
  32761. # <id type="integer">1</id>
  32762. # <name>David</name>
  32763. # <age type="integer">16</age>
  32764. # <created-at type="dateTime">2011-01-30T22:29:23Z</created-at>
  32765. # </user>
  32766. #
  32767. # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
  32768. # attributes included, and work similar to the +attributes+ method.
  32769. #
  32770. # To include the result of some method calls on the model use <tt>:methods</tt>.
  32771. #
  32772. # To include associations use <tt>:include</tt>.
  32773. #
  32774. # For further documentation, see <tt>ActiveRecord::Serialization#to_xml</tt>
  32775. def to_xml(options = {}, &block)
  32776. Serializer.new(self, options).serialize(&block)
  32777. end
  32778. # Sets the model +attributes+ from a JSON string. Returns +self+.
  32779. #
  32780. # class Person
  32781. # include ActiveModel::Serializers::Xml
  32782. #
  32783. # attr_accessor :name, :age, :awesome
  32784. #
  32785. # def attributes=(hash)
  32786. # hash.each do |key, value|
  32787. # instance_variable_set("@#{key}", value)
  32788. # end
  32789. # end
  32790. #
  32791. # def attributes
  32792. # instance_values
  32793. # end
  32794. # end
  32795. #
  32796. # xml = { name: 'bob', age: 22, awesome:true }.to_xml
  32797. # person = Person.new
  32798. # person.from_xml(xml) # => #<Person:0x007fec5e3b3c40 @age=22, @awesome=true, @name="bob">
  32799. # person.name # => "bob"
  32800. # person.age # => 22
  32801. # person.awesome # => true
  32802. def from_xml(xml)
  32803. self.attributes = Hash.from_xml(xml).values.first
  32804. self
  32805. end
  32806. end
  32807. end
  32808. end
  32809. module ActiveModel #:nodoc:
  32810. class TestCase < ActiveSupport::TestCase #:nodoc:
  32811. end
  32812. end
  32813. module ActiveModel
  32814. # == Active \Model \Translation
  32815. #
  32816. # Provides integration between your object and the Rails internationalization
  32817. # (i18n) framework.
  32818. #
  32819. # A minimal implementation could be:
  32820. #
  32821. # class TranslatedPerson
  32822. # extend ActiveModel::Translation
  32823. # end
  32824. #
  32825. # TranslatedPerson.human_attribute_name('my_attribute')
  32826. # # => "My attribute"
  32827. #
  32828. # This also provides the required class methods for hooking into the
  32829. # Rails internationalization API, including being able to define a
  32830. # class based +i18n_scope+ and +lookup_ancestors+ to find translations in
  32831. # parent classes.
  32832. module Translation
  32833. include ActiveModel::Naming
  32834. # Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
  32835. def i18n_scope
  32836. :activemodel
  32837. end
  32838. # When localizing a string, it goes through the lookup returned by this
  32839. # method, which is used in ActiveModel::Name#human,
  32840. # ActiveModel::Errors#full_messages and
  32841. # ActiveModel::Translation#human_attribute_name.
  32842. def lookup_ancestors
  32843. self.ancestors.select { |x| x.respond_to?(:model_name) }
  32844. end
  32845. # Transforms attribute names into a more human format, such as "First name"
  32846. # instead of "first_name".
  32847. #
  32848. # Person.human_attribute_name("first_name") # => "First name"
  32849. #
  32850. # Specify +options+ with additional translating options.
  32851. def human_attribute_name(attribute, options = {})
  32852. options = { :count => 1 }.merge!(options)
  32853. parts = attribute.to_s.split(".")
  32854. attribute = parts.pop
  32855. namespace = parts.join("/") unless parts.empty?
  32856. attributes_scope = "#{self.i18n_scope}.attributes"
  32857. if namespace
  32858. defaults = lookup_ancestors.map do |klass|
  32859. :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
  32860. end
  32861. defaults << :"#{attributes_scope}.#{namespace}.#{attribute}"
  32862. else
  32863. defaults = lookup_ancestors.map do |klass|
  32864. :"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}"
  32865. end
  32866. end
  32867. defaults << :"attributes.#{attribute}"
  32868. defaults << options.delete(:default) if options[:default]
  32869. defaults << attribute.humanize
  32870. options[:default] = defaults
  32871. I18n.translate(defaults.shift, options)
  32872. end
  32873. end
  32874. end
  32875. module ActiveModel
  32876. module Validations
  32877. # == Active Model Absence Validator
  32878. class AbsenceValidator < EachValidator #:nodoc:
  32879. def validate_each(record, attr_name, value)
  32880. record.errors.add(attr_name, :present, options) if value.present?
  32881. end
  32882. end
  32883. module HelperMethods
  32884. # Validates that the specified attributes are blank (as defined by
  32885. # Object#blank?). Happens by default on save.
  32886. #
  32887. # class Person < ActiveRecord::Base
  32888. # validates_absence_of :first_name
  32889. # end
  32890. #
  32891. # The first_name attribute must be in the object and it must be blank.
  32892. #
  32893. # Configuration options:
  32894. # * <tt>:message</tt> - A custom error message (default is: "must be blank").
  32895. #
  32896. # There is also a list of default options supported by every validator:
  32897. # +:if+, +:unless+, +:on+ and +:strict+.
  32898. # See <tt>ActiveModel::Validation#validates</tt> for more information
  32899. def validates_absence_of(*attr_names)
  32900. validates_with AbsenceValidator, _merge_attributes(attr_names)
  32901. end
  32902. end
  32903. end
  32904. end
  32905. module ActiveModel
  32906. module Validations
  32907. class AcceptanceValidator < EachValidator # :nodoc:
  32908. def initialize(options)
  32909. super({ :allow_nil => true, :accept => "1" }.merge!(options))
  32910. end
  32911. def validate_each(record, attribute, value)
  32912. unless value == options[:accept]
  32913. record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
  32914. end
  32915. end
  32916. def setup(klass)
  32917. attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
  32918. attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
  32919. klass.send(:attr_reader, *attr_readers)
  32920. klass.send(:attr_writer, *attr_writers)
  32921. end
  32922. end
  32923. module HelperMethods
  32924. # Encapsulates the pattern of wanting to validate the acceptance of a
  32925. # terms of service check box (or similar agreement).
  32926. #
  32927. # class Person < ActiveRecord::Base
  32928. # validates_acceptance_of :terms_of_service
  32929. # validates_acceptance_of :eula, message: 'must be abided'
  32930. # end
  32931. #
  32932. # If the database column does not exist, the +terms_of_service+ attribute
  32933. # is entirely virtual. This check is performed only if +terms_of_service+
  32934. # is not +nil+ and by default on save.
  32935. #
  32936. # Configuration options:
  32937. # * <tt>:message</tt> - A custom error message (default is: "must be
  32938. # accepted").
  32939. # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default
  32940. # is +true+).
  32941. # * <tt>:accept</tt> - Specifies value that is considered accepted.
  32942. # The default value is a string "1", which makes it easy to relate to
  32943. # an HTML checkbox. This should be set to +true+ if you are validating
  32944. # a database column, since the attribute is typecast from "1" to +true+
  32945. # before validation.
  32946. #
  32947. # There is also a list of default options supported by every validator:
  32948. # +:if+, +:unless+, +:on+ and +:strict+.
  32949. # See <tt>ActiveModel::Validation#validates</tt> for more information
  32950. def validates_acceptance_of(*attr_names)
  32951. validates_with AcceptanceValidator, _merge_attributes(attr_names)
  32952. end
  32953. end
  32954. end
  32955. end
  32956. module ActiveModel
  32957. module Validations
  32958. # == Active \Model Validation Callbacks
  32959. #
  32960. # Provides an interface for any class to have +before_validation+ and
  32961. # +after_validation+ callbacks.
  32962. #
  32963. # First, include ActiveModel::Validations::Callbacks from the class you are
  32964. # creating:
  32965. #
  32966. # class MyModel
  32967. # include ActiveModel::Validations::Callbacks
  32968. #
  32969. # before_validation :do_stuff_before_validation
  32970. # after_validation :do_stuff_after_validation
  32971. # end
  32972. #
  32973. # Like other <tt>before_*</tt> callbacks if +before_validation+ returns
  32974. # +false+ then <tt>valid?</tt> will not be called.
  32975. module Callbacks
  32976. extend ActiveSupport::Concern
  32977. included do
  32978. include ActiveSupport::Callbacks
  32979. define_callbacks :validation, :terminator => "result == false", :skip_after_callbacks_if_terminated => true, :scope => [:kind, :name]
  32980. end
  32981. module ClassMethods
  32982. # Defines a callback that will get called right before validation
  32983. # happens.
  32984. #
  32985. # class Person
  32986. # include ActiveModel::Validations
  32987. # include ActiveModel::Validations::Callbacks
  32988. #
  32989. # attr_accessor :name
  32990. #
  32991. # validates_length_of :name, maximum: 6
  32992. #
  32993. # before_validation :remove_whitespaces
  32994. #
  32995. # private
  32996. #
  32997. # def remove_whitespaces
  32998. # name.strip!
  32999. # end
  33000. # end
  33001. #
  33002. # person = Person.new
  33003. # person.name = ' bob '
  33004. # person.valid? # => true
  33005. # person.name # => "bob"
  33006. def before_validation(*args, &block)
  33007. options = args.last
  33008. if options.is_a?(Hash) && options[:on]
  33009. options[:if] = Array(options[:if])
  33010. options[:on] = Array(options[:on])
  33011. options[:if].unshift("#{options[:on]}.include? self.validation_context")
  33012. end
  33013. set_callback(:validation, :before, *args, &block)
  33014. end
  33015. # Defines a callback that will get called right after validation
  33016. # happens.
  33017. #
  33018. # class Person
  33019. # include ActiveModel::Validations
  33020. # include ActiveModel::Validations::Callbacks
  33021. #
  33022. # attr_accessor :name, :status
  33023. #
  33024. # validates_presence_of :name
  33025. #
  33026. # after_validation :set_status
  33027. #
  33028. # private
  33029. #
  33030. # def set_status
  33031. # self.status = errors.empty?
  33032. # end
  33033. # end
  33034. #
  33035. # person = Person.new
  33036. # person.name = ''
  33037. # person.valid? # => false
  33038. # person.status # => false
  33039. # person.name = 'bob'
  33040. # person.valid? # => true
  33041. # person.status # => true
  33042. def after_validation(*args, &block)
  33043. options = args.extract_options!
  33044. options[:prepend] = true
  33045. options[:if] = Array(options[:if])
  33046. if options[:on]
  33047. options[:on] = Array(options[:on])
  33048. options[:if].unshift("#{options[:on]}.include? self.validation_context")
  33049. end
  33050. set_callback(:validation, :after, *(args << options), &block)
  33051. end
  33052. end
  33053. protected
  33054. # Overwrite run validations to include callbacks.
  33055. def run_validations! #:nodoc:
  33056. run_callbacks(:validation) { super }
  33057. end
  33058. end
  33059. end
  33060. end
  33061. require 'active_support/core_ext/range'
  33062. module ActiveModel
  33063. module Validations
  33064. module Clusivity #:nodoc:
  33065. ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " \
  33066. "and must be supplied as the :in (or :within) option of the configuration hash"
  33067. def check_validity!
  33068. unless delimiter.respond_to?(:include?) || delimiter.respond_to?(:call) || delimiter.respond_to?(:to_sym)
  33069. raise ArgumentError, ERROR_MESSAGE
  33070. end
  33071. end
  33072. private
  33073. def include?(record, value)
  33074. exclusions = if delimiter.respond_to?(:call)
  33075. delimiter.call(record)
  33076. elsif delimiter.respond_to?(:to_sym)
  33077. record.send(delimiter)
  33078. else
  33079. delimiter
  33080. end
  33081. exclusions.send(inclusion_method(exclusions), value)
  33082. end
  33083. def delimiter
  33084. @delimiter ||= options[:in] || options[:within]
  33085. end
  33086. # In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
  33087. # range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
  33088. # uses the previous logic of comparing a value with the range endpoints.
  33089. def inclusion_method(enumerable)
  33090. enumerable.is_a?(Range) ? :cover? : :include?
  33091. end
  33092. end
  33093. end
  33094. end
  33095. module ActiveModel
  33096. module Validations
  33097. class ConfirmationValidator < EachValidator # :nodoc:
  33098. def validate_each(record, attribute, value)
  33099. if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
  33100. human_attribute_name = record.class.human_attribute_name(attribute)
  33101. record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(:attribute => human_attribute_name))
  33102. end
  33103. end
  33104. def setup(klass)
  33105. klass.send(:attr_accessor, *attributes.map do |attribute|
  33106. :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
  33107. end.compact)
  33108. end
  33109. end
  33110. module HelperMethods
  33111. # Encapsulates the pattern of wanting to validate a password or email
  33112. # address field with a confirmation.
  33113. #
  33114. # Model:
  33115. # class Person < ActiveRecord::Base
  33116. # validates_confirmation_of :user_name, :password
  33117. # validates_confirmation_of :email_address,
  33118. # message: 'should match confirmation'
  33119. # end
  33120. #
  33121. # View:
  33122. # <%= password_field "person", "password" %>
  33123. # <%= password_field "person", "password_confirmation" %>
  33124. #
  33125. # The added +password_confirmation+ attribute is virtual; it exists only
  33126. # as an in-memory attribute for validating the password. To achieve this,
  33127. # the validation adds accessors to the model for the confirmation
  33128. # attribute.
  33129. #
  33130. # NOTE: This check is performed only if +password_confirmation+ is not
  33131. # +nil+. To require confirmation, make sure to add a presence check for
  33132. # the confirmation attribute:
  33133. #
  33134. # validates_presence_of :password_confirmation, if: :password_changed?
  33135. #
  33136. # Configuration options:
  33137. # * <tt>:message</tt> - A custom error message (default is: "doesn't match
  33138. # confirmation").
  33139. #
  33140. # There is also a list of default options supported by every validator:
  33141. # +:if+, +:unless+, +:on+ and +:strict+.
  33142. # See <tt>ActiveModel::Validation#validates</tt> for more information
  33143. def validates_confirmation_of(*attr_names)
  33144. validates_with ConfirmationValidator, _merge_attributes(attr_names)
  33145. end
  33146. end
  33147. end
  33148. end
  33149. require "active_model/validations/clusivity"
  33150. module ActiveModel
  33151. module Validations
  33152. class ExclusionValidator < EachValidator # :nodoc:
  33153. include Clusivity
  33154. def validate_each(record, attribute, value)
  33155. if include?(record, value)
  33156. record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(:value => value))
  33157. end
  33158. end
  33159. end
  33160. module HelperMethods
  33161. # Validates that the value of the specified attribute is not in a
  33162. # particular enumerable object.
  33163. #
  33164. # class Person < ActiveRecord::Base
  33165. # validates_exclusion_of :username, in: %w( admin superuser ), message: "You don't belong here"
  33166. # validates_exclusion_of :age, in: 30..60, message: 'This site is only for under 30 and over 60'
  33167. # validates_exclusion_of :format, in: %w( mov avi ), message: "extension %{value} is not allowed"
  33168. # validates_exclusion_of :password, in: ->(person) { [person.username, person.first_name] },
  33169. # message: 'should not be the same as your username or first name'
  33170. # validates_exclusion_of :karma, in: :reserved_karmas
  33171. # end
  33172. #
  33173. # Configuration options:
  33174. # * <tt>:in</tt> - An enumerable object of items that the value shouldn't
  33175. # be part of. This can be supplied as a proc, lambda or symbol which returns an
  33176. # enumerable. If the enumerable is a range the test is performed with
  33177. # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
  33178. # <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
  33179. # * <tt>:message</tt> - Specifies a custom error message (default is: "is
  33180. # reserved").
  33181. # * <tt>:allow_nil</tt> - If set to true, skips this validation if the
  33182. # attribute is +nil+ (default is +false+).
  33183. # * <tt>:allow_blank</tt> - If set to true, skips this validation if the
  33184. # attribute is blank(default is +false+).
  33185. #
  33186. # There is also a list of default options supported by every validator:
  33187. # +:if+, +:unless+, +:on+ and +:strict+.
  33188. # See <tt>ActiveModel::Validation#validates</tt> for more information
  33189. def validates_exclusion_of(*attr_names)
  33190. validates_with ExclusionValidator, _merge_attributes(attr_names)
  33191. end
  33192. end
  33193. end
  33194. end
  33195. module ActiveModel
  33196. module Validations
  33197. class FormatValidator < EachValidator # :nodoc:
  33198. def validate_each(record, attribute, value)
  33199. if options[:with]
  33200. regexp = option_call(record, :with)
  33201. record_error(record, attribute, :with, value) if value.to_s !~ regexp
  33202. elsif options[:without]
  33203. regexp = option_call(record, :without)
  33204. record_error(record, attribute, :without, value) if value.to_s =~ regexp
  33205. end
  33206. end
  33207. def check_validity!
  33208. unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
  33209. raise ArgumentError, "Either :with or :without must be supplied (but not both)"
  33210. end
  33211. check_options_validity(options, :with)
  33212. check_options_validity(options, :without)
  33213. end
  33214. private
  33215. def option_call(record, name)
  33216. option = options[name]
  33217. option.respond_to?(:call) ? option.call(record) : option
  33218. end
  33219. def record_error(record, attribute, name, value)
  33220. record.errors.add(attribute, :invalid, options.except(name).merge!(:value => value))
  33221. end
  33222. def regexp_using_multiline_anchors?(regexp)
  33223. regexp.source.start_with?("^") ||
  33224. (regexp.source.end_with?("$") && !regexp.source.end_with?("\\$"))
  33225. end
  33226. def check_options_validity(options, name)
  33227. option = options[name]
  33228. if option && !option.is_a?(Regexp) && !option.respond_to?(:call)
  33229. raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
  33230. elsif option && option.is_a?(Regexp) &&
  33231. regexp_using_multiline_anchors?(option) && options[:multiline] != true
  33232. raise ArgumentError, "The provided regular expression is using multiline anchors (^ or $), " \
  33233. "which may present a security risk. Did you mean to use \\A and \\z, or forgot to add the " \
  33234. ":multiline => true option?"
  33235. end
  33236. end
  33237. end
  33238. module HelperMethods
  33239. # Validates whether the value of the specified attribute is of the correct
  33240. # form, going by the regular expression provided.You can require that the
  33241. # attribute matches the regular expression:
  33242. #
  33243. # class Person < ActiveRecord::Base
  33244. # validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create
  33245. # end
  33246. #
  33247. # Alternatively, you can require that the specified attribute does _not_
  33248. # match the regular expression:
  33249. #
  33250. # class Person < ActiveRecord::Base
  33251. # validates_format_of :email, without: /NOSPAM/
  33252. # end
  33253. #
  33254. # You can also provide a proc or lambda which will determine the regular
  33255. # expression that will be used to validate the attribute.
  33256. #
  33257. # class Person < ActiveRecord::Base
  33258. # # Admin can have number as a first letter in their screen name
  33259. # validates_format_of :screen_name,
  33260. # with: ->(person) { person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\z/i : /\A[a-z][a-z0-9_\-]*\z/i }
  33261. # end
  33262. #
  33263. # Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the
  33264. # string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
  33265. #
  33266. # Due to frequent misuse of <tt>^</tt> and <tt>$</tt>, you need to pass
  33267. # the <tt>multiline: true</tt> option in case you use any of these two
  33268. # anchors in the provided regular expression. In most cases, you should be
  33269. # using <tt>\A</tt> and <tt>\z</tt>.
  33270. #
  33271. # You must pass either <tt>:with</tt> or <tt>:without</tt> as an option.
  33272. # In addition, both must be a regular expression or a proc or lambda, or
  33273. # else an exception will be raised.
  33274. #
  33275. # Configuration options:
  33276. # * <tt>:message</tt> - A custom error message (default is: "is invalid").
  33277. # * <tt>:allow_nil</tt> - If set to true, skips this validation if the
  33278. # attribute is +nil+ (default is +false+).
  33279. # * <tt>:allow_blank</tt> - If set to true, skips this validation if the
  33280. # attribute is blank (default is +false+).
  33281. # * <tt>:with</tt> - Regular expression that if the attribute matches will
  33282. # result in a successful validation. This can be provided as a proc or
  33283. # lambda returning regular expression which will be called at runtime.
  33284. # * <tt>:without</tt> - Regular expression that if the attribute does not
  33285. # match will result in a successful validation. This can be provided as
  33286. # a proc or lambda returning regular expression which will be called at
  33287. # runtime.
  33288. # * <tt>:multiline</tt> - Set to true if your regular expression contains
  33289. # anchors that match the beginning or end of lines as opposed to the
  33290. # beginning or end of the string. These anchors are <tt>^</tt> and <tt>$</tt>.
  33291. #
  33292. # There is also a list of default options supported by every validator:
  33293. # +:if+, +:unless+, +:on+ and +:strict+.
  33294. # See <tt>ActiveModel::Validation#validates</tt> for more information
  33295. def validates_format_of(*attr_names)
  33296. validates_with FormatValidator, _merge_attributes(attr_names)
  33297. end
  33298. end
  33299. end
  33300. end
  33301. require "active_model/validations/clusivity"
  33302. module ActiveModel
  33303. module Validations
  33304. class InclusionValidator < EachValidator # :nodoc:
  33305. include Clusivity
  33306. def validate_each(record, attribute, value)
  33307. unless include?(record, value)
  33308. record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(:value => value))
  33309. end
  33310. end
  33311. end
  33312. module HelperMethods
  33313. # Validates whether the value of the specified attribute is available in a
  33314. # particular enumerable object.
  33315. #
  33316. # class Person < ActiveRecord::Base
  33317. # validates_inclusion_of :gender, in: %w( m f )
  33318. # validates_inclusion_of :age, in: 0..99
  33319. # validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
  33320. # validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
  33321. # validates_inclusion_of :karma, in: :available_karmas
  33322. # end
  33323. #
  33324. # Configuration options:
  33325. # * <tt>:in</tt> - An enumerable object of available items. This can be
  33326. # supplied as a proc, lambda or symbol which returns an enumerable. If the
  33327. # enumerable is a range the test is performed with <tt>Range#cover?</tt>,
  33328. # otherwise with <tt>include?</tt>.
  33329. # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
  33330. # * <tt>:message</tt> - Specifies a custom error message (default is: "is
  33331. # not included in the list").
  33332. # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
  33333. # attribute is +nil+ (default is +false+).
  33334. # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
  33335. # attribute is blank (default is +false+).
  33336. #
  33337. # There is also a list of default options supported by every validator:
  33338. # +:if+, +:unless+, +:on+ and +:strict+.
  33339. # See <tt>ActiveModel::Validation#validates</tt> for more information
  33340. def validates_inclusion_of(*attr_names)
  33341. validates_with InclusionValidator, _merge_attributes(attr_names)
  33342. end
  33343. end
  33344. end
  33345. end
  33346. module ActiveModel
  33347. # == Active \Model Length \Validator
  33348. module Validations
  33349. class LengthValidator < EachValidator # :nodoc:
  33350. MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
  33351. CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
  33352. RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
  33353. def initialize(options)
  33354. if range = (options.delete(:in) || options.delete(:within))
  33355. raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
  33356. options[:minimum], options[:maximum] = range.min, range.max
  33357. end
  33358. if options[:allow_blank] == false && options[:minimum].nil? && options[:is].nil?
  33359. options[:minimum] = 1
  33360. end
  33361. super
  33362. end
  33363. def check_validity!
  33364. keys = CHECKS.keys & options.keys
  33365. if keys.empty?
  33366. raise ArgumentError, 'Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option.'
  33367. end
  33368. keys.each do |key|
  33369. value = options[key]
  33370. unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY
  33371. raise ArgumentError, ":#{key} must be a nonnegative Integer or Infinity"
  33372. end
  33373. end
  33374. end
  33375. def validate_each(record, attribute, value)
  33376. value = tokenize(value)
  33377. value_length = value.respond_to?(:length) ? value.length : value.to_s.length
  33378. errors_options = options.except(*RESERVED_OPTIONS)
  33379. CHECKS.each do |key, validity_check|
  33380. next unless check_value = options[key]
  33381. if !value.nil? || skip_nil_check?(key)
  33382. next if value_length.send(validity_check, check_value)
  33383. end
  33384. errors_options[:count] = check_value
  33385. default_message = options[MESSAGES[key]]
  33386. errors_options[:message] ||= default_message if default_message
  33387. record.errors.add(attribute, MESSAGES[key], errors_options)
  33388. end
  33389. end
  33390. private
  33391. def tokenize(value)
  33392. if options[:tokenizer] && value.kind_of?(String)
  33393. options[:tokenizer].call(value)
  33394. end || value
  33395. end
  33396. def skip_nil_check?(key)
  33397. key == :maximum && options[:allow_nil].nil? && options[:allow_blank].nil?
  33398. end
  33399. end
  33400. module HelperMethods
  33401. # Validates that the specified attribute matches the length restrictions
  33402. # supplied. Only one option can be used at a time:
  33403. #
  33404. # class Person < ActiveRecord::Base
  33405. # validates_length_of :first_name, maximum: 30
  33406. # validates_length_of :last_name, maximum: 30, message: "less than 30 if you don't mind"
  33407. # validates_length_of :fax, in: 7..32, allow_nil: true
  33408. # validates_length_of :phone, in: 7..32, allow_blank: true
  33409. # validates_length_of :user_name, within: 6..20, too_long: 'pick a shorter name', too_short: 'pick a longer name'
  33410. # validates_length_of :zip_code, minimum: 5, too_short: 'please enter at least 5 characters'
  33411. # validates_length_of :smurf_leader, is: 4, message: "papa is spelled with 4 characters... don't play me."
  33412. # validates_length_of :essay, minimum: 100, too_short: 'Your essay must be at least 100 words.',
  33413. # tokenizer: ->(str) { str.scan(/\w+/) }
  33414. # end
  33415. #
  33416. # Configuration options:
  33417. # * <tt>:minimum</tt> - The minimum size of the attribute.
  33418. # * <tt>:maximum</tt> - The maximum size of the attribute. Allows +nil+ by
  33419. # default if not used with :minimum.
  33420. # * <tt>:is</tt> - The exact size of the attribute.
  33421. # * <tt>:within</tt> - A range specifying the minimum and maximum size of
  33422. # the attribute.
  33423. # * <tt>:in</tt> - A synonym (or alias) for <tt>:within</tt>.
  33424. # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
  33425. # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
  33426. # * <tt>:too_long</tt> - The error message if the attribute goes over the
  33427. # maximum (default is: "is too long (maximum is %{count} characters)").
  33428. # * <tt>:too_short</tt> - The error message if the attribute goes under the
  33429. # minimum (default is: "is too short (min is %{count} characters)").
  33430. # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt>
  33431. # method and the attribute is the wrong size (default is: "is the wrong
  33432. # length (should be %{count} characters)").
  33433. # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
  33434. # <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
  33435. # <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
  33436. # * <tt>:tokenizer</tt> - Specifies how to split up the attribute string.
  33437. # (e.g. <tt>tokenizer: ->(str) { str.scan(/\w+/) }</tt> to count words
  33438. # as in above example). Defaults to <tt>->(value) { value.split(//) }</tt>
  33439. # which counts individual characters.
  33440. #
  33441. # There is also a list of default options supported by every validator:
  33442. # +:if+, +:unless+, +:on+ and +:strict+.
  33443. # See <tt>ActiveModel::Validation#validates</tt> for more information
  33444. def validates_length_of(*attr_names)
  33445. validates_with LengthValidator, _merge_attributes(attr_names)
  33446. end
  33447. alias_method :validates_size_of, :validates_length_of
  33448. end
  33449. end
  33450. end
  33451. module ActiveModel
  33452. module Validations
  33453. class NumericalityValidator < EachValidator # :nodoc:
  33454. CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
  33455. :equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
  33456. :odd => :odd?, :even => :even?, :other_than => :!= }.freeze
  33457. RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
  33458. def check_validity!
  33459. keys = CHECKS.keys - [:odd, :even]
  33460. options.slice(*keys).each do |option, value|
  33461. next if value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
  33462. raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
  33463. end
  33464. end
  33465. def validate_each(record, attr_name, value)
  33466. before_type_cast = :"#{attr_name}_before_type_cast"
  33467. raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast)
  33468. raw_value ||= value
  33469. return if options[:allow_nil] && raw_value.nil?
  33470. unless value = parse_raw_value_as_a_number(raw_value)
  33471. record.errors.add(attr_name, :not_a_number, filtered_options(raw_value))
  33472. return
  33473. end
  33474. if options[:only_integer]
  33475. unless value = parse_raw_value_as_an_integer(raw_value)
  33476. record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
  33477. return
  33478. end
  33479. end
  33480. options.slice(*CHECKS.keys).each do |option, option_value|
  33481. case option
  33482. when :odd, :even
  33483. unless value.to_i.send(CHECKS[option])
  33484. record.errors.add(attr_name, option, filtered_options(value))
  33485. end
  33486. else
  33487. option_value = option_value.call(record) if option_value.is_a?(Proc)
  33488. option_value = record.send(option_value) if option_value.is_a?(Symbol)
  33489. unless value.send(CHECKS[option], option_value)
  33490. record.errors.add(attr_name, option, filtered_options(value).merge(:count => option_value))
  33491. end
  33492. end
  33493. end
  33494. end
  33495. protected
  33496. def parse_raw_value_as_a_number(raw_value)
  33497. case raw_value
  33498. when /\A0[xX]/
  33499. nil
  33500. else
  33501. begin
  33502. Kernel.Float(raw_value)
  33503. rescue ArgumentError, TypeError
  33504. nil
  33505. end
  33506. end
  33507. end
  33508. def parse_raw_value_as_an_integer(raw_value)
  33509. raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
  33510. end
  33511. def filtered_options(value)
  33512. options.except(*RESERVED_OPTIONS).merge!(:value => value)
  33513. end
  33514. end
  33515. module HelperMethods
  33516. # Validates whether the value of the specified attribute is numeric by
  33517. # trying to convert it to a float with Kernel.Float (if <tt>only_integer</tt>
  33518. # is +false+) or applying it to the regular expression <tt>/\A[\+\-]?\d+\Z/</tt>
  33519. # (if <tt>only_integer</tt> is set to +true+).
  33520. #
  33521. # class Person < ActiveRecord::Base
  33522. # validates_numericality_of :value, on: :create
  33523. # end
  33524. #
  33525. # Configuration options:
  33526. # * <tt>:message</tt> - A custom error message (default is: "is not a number").
  33527. # * <tt>:only_integer</tt> - Specifies whether the value has to be an
  33528. # integer, e.g. an integral value (default is +false+).
  33529. # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
  33530. # +false+). Notice that for fixnum and float columns empty strings are
  33531. # converted to +nil+.
  33532. # * <tt>:greater_than</tt> - Specifies the value must be greater than the
  33533. # supplied value.
  33534. # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be
  33535. # greater than or equal the supplied value.
  33536. # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied
  33537. # value.
  33538. # * <tt>:less_than</tt> - Specifies the value must be less than the
  33539. # supplied value.
  33540. # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less
  33541. # than or equal the supplied value.
  33542. # * <tt>:other_than</tt> - Specifies the value must be other than the
  33543. # supplied value.
  33544. # * <tt>:odd</tt> - Specifies the value must be an odd number.
  33545. # * <tt>:even</tt> - Specifies the value must be an even number.
  33546. #
  33547. # There is also a list of default options supported by every validator:
  33548. # +:if+, +:unless+, +:on+ and +:strict+ .
  33549. # See <tt>ActiveModel::Validation#validates</tt> for more information
  33550. #
  33551. # The following checks can also be supplied with a proc or a symbol which
  33552. # corresponds to a method:
  33553. #
  33554. # * <tt>:greater_than</tt>
  33555. # * <tt>:greater_than_or_equal_to</tt>
  33556. # * <tt>:equal_to</tt>
  33557. # * <tt>:less_than</tt>
  33558. # * <tt>:less_than_or_equal_to</tt>
  33559. #
  33560. # For example:
  33561. #
  33562. # class Person < ActiveRecord::Base
  33563. # validates_numericality_of :width, less_than: ->(person) { person.height }
  33564. # validates_numericality_of :width, greater_than: :minimum_weight
  33565. # end
  33566. def validates_numericality_of(*attr_names)
  33567. validates_with NumericalityValidator, _merge_attributes(attr_names)
  33568. end
  33569. end
  33570. end
  33571. end
  33572. module ActiveModel
  33573. module Validations
  33574. class PresenceValidator < EachValidator # :nodoc:
  33575. def validate_each(record, attr_name, value)
  33576. record.errors.add(attr_name, :blank, options) if value.blank?
  33577. end
  33578. end
  33579. module HelperMethods
  33580. # Validates that the specified attributes are not blank (as defined by
  33581. # Object#blank?). Happens by default on save.
  33582. #
  33583. # class Person < ActiveRecord::Base
  33584. # validates_presence_of :first_name
  33585. # end
  33586. #
  33587. # The first_name attribute must be in the object and it cannot be blank.
  33588. #
  33589. # If you want to validate the presence of a boolean field (where the real
  33590. # values are +true+ and +false+), you will want to use
  33591. # <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
  33592. #
  33593. # This is due to the way Object#blank? handles boolean values:
  33594. # <tt>false.blank? # => true</tt>.
  33595. #
  33596. # Configuration options:
  33597. # * <tt>:message</tt> - A custom error message (default is: "can't be blank").
  33598. #
  33599. # There is also a list of default options supported by every validator:
  33600. # +:if+, +:unless+, +:on+ and +:strict+.
  33601. # See <tt>ActiveModel::Validation#validates</tt> for more information
  33602. def validates_presence_of(*attr_names)
  33603. validates_with PresenceValidator, _merge_attributes(attr_names)
  33604. end
  33605. end
  33606. end
  33607. end
  33608. require 'active_support/core_ext/hash/slice'
  33609. module ActiveModel
  33610. module Validations
  33611. module ClassMethods
  33612. # This method is a shortcut to all default validators and any custom
  33613. # validator classes ending in 'Validator'. Note that Rails default
  33614. # validators can be overridden inside specific classes by creating
  33615. # custom validator classes in their place such as PresenceValidator.
  33616. #
  33617. # Examples of using the default rails validators:
  33618. #
  33619. # validates :terms, acceptance: true
  33620. # validates :password, confirmation: true
  33621. # validates :username, exclusion: { in: %w(admin superuser) }
  33622. # validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, on: :create }
  33623. # validates :age, inclusion: { in: 0..9 }
  33624. # validates :first_name, length: { maximum: 30 }
  33625. # validates :age, numericality: true
  33626. # validates :username, presence: true
  33627. # validates :username, uniqueness: true
  33628. #
  33629. # The power of the +validates+ method comes when using custom validators
  33630. # and default validators in one call for a given attribute.
  33631. #
  33632. # class EmailValidator < ActiveModel::EachValidator
  33633. # def validate_each(record, attribute, value)
  33634. # record.errors.add attribute, (options[:message] || "is not an email") unless
  33635. # value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
  33636. # end
  33637. # end
  33638. #
  33639. # class Person
  33640. # include ActiveModel::Validations
  33641. # attr_accessor :name, :email
  33642. #
  33643. # validates :name, presence: true, uniqueness: true, length: { maximum: 100 }
  33644. # validates :email, presence: true, email: true
  33645. # end
  33646. #
  33647. # Validator classes may also exist within the class being validated
  33648. # allowing custom modules of validators to be included as needed.
  33649. #
  33650. # class Film
  33651. # include ActiveModel::Validations
  33652. #
  33653. # class TitleValidator < ActiveModel::EachValidator
  33654. # def validate_each(record, attribute, value)
  33655. # record.errors.add attribute, "must start with 'the'" unless value =~ /\Athe/i
  33656. # end
  33657. # end
  33658. #
  33659. # validates :name, title: true
  33660. # end
  33661. #
  33662. # Additionally validator classes may be in another namespace and still
  33663. # used within any class.
  33664. #
  33665. # validates :name, :'film/title' => true
  33666. #
  33667. # The validators hash can also handle regular expressions, ranges, arrays
  33668. # and strings in shortcut form.
  33669. #
  33670. # validates :email, format: /@/
  33671. # validates :gender, inclusion: %w(male female)
  33672. # validates :password, length: 6..20
  33673. #
  33674. # When using shortcut form, ranges and arrays are passed to your
  33675. # validator's initializer as <tt>options[:in]</tt> while other types
  33676. # including regular expressions and strings are passed as <tt>options[:with]</tt>.
  33677. #
  33678. # There is also a list of options that could be used along with validators:
  33679. #
  33680. # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
  33681. # validation contexts by default (+nil+), other options are <tt>:create</tt>
  33682. # and <tt>:update</tt>.
  33683. # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
  33684. # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
  33685. # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
  33686. # proc or string should return or evaluate to a +true+ or +false+ value.
  33687. # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
  33688. # if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
  33689. # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
  33690. # method, proc or string should return or evaluate to a +true+ or
  33691. # +false+ value.
  33692. # * <tt>:strict</tt> - if the <tt>:strict</tt> option is set to true
  33693. # will raise ActiveModel::StrictValidationFailed instead of adding the error.
  33694. # <tt>:strict</tt> option can also be set to any other exception.
  33695. #
  33696. # Example:
  33697. #
  33698. # validates :password, presence: true, confirmation: true, if: :password_required?
  33699. # validates :token, uniqueness: true, strict: TokenGenerationException
  33700. #
  33701. #
  33702. # Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+, +:strict+
  33703. # and +:message+ can be given to one specific validator, as a hash:
  33704. #
  33705. # validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true
  33706. def validates(*attributes)
  33707. defaults = attributes.extract_options!.dup
  33708. validations = defaults.slice!(*_validates_default_keys)
  33709. raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
  33710. raise ArgumentError, "You need to supply at least one validation" if validations.empty?
  33711. defaults[:attributes] = attributes
  33712. validations.each do |key, options|
  33713. next unless options
  33714. key = "#{key.to_s.camelize}Validator"
  33715. begin
  33716. validator = key.include?('::') ? key.constantize : const_get(key)
  33717. rescue NameError
  33718. raise ArgumentError, "Unknown validator: '#{key}'"
  33719. end
  33720. validates_with(validator, defaults.merge(_parse_validates_options(options)))
  33721. end
  33722. end
  33723. # This method is used to define validations that cannot be corrected by end
  33724. # users and are considered exceptional. So each validator defined with bang
  33725. # or <tt>:strict</tt> option set to <tt>true</tt> will always raise
  33726. # <tt>ActiveModel::StrictValidationFailed</tt> instead of adding error
  33727. # when validation fails. See <tt>validates</tt> for more information about
  33728. # the validation itself.
  33729. #
  33730. # class Person
  33731. # include ActiveModel::Validations
  33732. #
  33733. # attr_accessor :name
  33734. # validates! :name, presence: true
  33735. # end
  33736. #
  33737. # person = Person.new
  33738. # person.name = ''
  33739. # person.valid?
  33740. # # => ActiveModel::StrictValidationFailed: Name can't be blank
  33741. def validates!(*attributes)
  33742. options = attributes.extract_options!
  33743. options[:strict] = true
  33744. validates(*(attributes << options))
  33745. end
  33746. protected
  33747. # When creating custom validators, it might be useful to be able to specify
  33748. # additional default keys. This can be done by overwriting this method.
  33749. def _validates_default_keys # :nodoc:
  33750. [:if, :unless, :on, :allow_blank, :allow_nil , :strict]
  33751. end
  33752. def _parse_validates_options(options) # :nodoc:
  33753. case options
  33754. when TrueClass
  33755. {}
  33756. when Hash
  33757. options
  33758. when Range, Array
  33759. { :in => options }
  33760. else
  33761. { :with => options }
  33762. end
  33763. end
  33764. end
  33765. end
  33766. end
  33767. module ActiveModel
  33768. module Validations
  33769. module HelperMethods
  33770. private
  33771. def _merge_attributes(attr_names)
  33772. options = attr_names.extract_options!.symbolize_keys
  33773. attr_names.flatten!
  33774. options[:attributes] = attr_names
  33775. options
  33776. end
  33777. end
  33778. class WithValidator < EachValidator # :nodoc:
  33779. def validate_each(record, attr, val)
  33780. method_name = options[:with]
  33781. if record.method(method_name).arity == 0
  33782. record.send method_name
  33783. else
  33784. record.send method_name, attr
  33785. end
  33786. end
  33787. end
  33788. module ClassMethods
  33789. # Passes the record off to the class or classes specified and allows them
  33790. # to add errors based on more complex conditions.
  33791. #
  33792. # class Person
  33793. # include ActiveModel::Validations
  33794. # validates_with MyValidator
  33795. # end
  33796. #
  33797. # class MyValidator < ActiveModel::Validator
  33798. # def validate(record)
  33799. # if some_complex_logic
  33800. # record.errors.add :base, 'This record is invalid'
  33801. # end
  33802. # end
  33803. #
  33804. # private
  33805. # def some_complex_logic
  33806. # # ...
  33807. # end
  33808. # end
  33809. #
  33810. # You may also pass it multiple classes, like so:
  33811. #
  33812. # class Person
  33813. # include ActiveModel::Validations
  33814. # validates_with MyValidator, MyOtherValidator, on: :create
  33815. # end
  33816. #
  33817. # Configuration options:
  33818. # * <tt>:on</tt> - Specifies when this validation is active
  33819. # (<tt>:create</tt> or <tt>:update</tt>.
  33820. # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
  33821. # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
  33822. # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>).
  33823. # The method, proc or string should return or evaluate to a +true+ or
  33824. # +false+ value.
  33825. # * <tt>:unless</tt> - Specifies a method, proc or string to call to
  33826. # determine if the validation should not occur
  33827. # (e.g. <tt>unless: :skip_validation</tt>, or
  33828. # <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>).
  33829. # The method, proc or string should return or evaluate to a +true+ or
  33830. # +false+ value.
  33831. # * <tt>:strict</tt> - Specifies whether validation should be strict.
  33832. # See <tt>ActiveModel::Validation#validates!</tt> for more information.
  33833. #
  33834. # If you pass any additional configuration options, they will be passed
  33835. # to the class and available as +options+:
  33836. #
  33837. # class Person
  33838. # include ActiveModel::Validations
  33839. # validates_with MyValidator, my_custom_key: 'my custom value'
  33840. # end
  33841. #
  33842. # class MyValidator < ActiveModel::Validator
  33843. # def validate(record)
  33844. # options[:my_custom_key] # => "my custom value"
  33845. # end
  33846. # end
  33847. def validates_with(*args, &block)
  33848. options = args.extract_options!
  33849. args.each do |klass|
  33850. validator = klass.new(options, &block)
  33851. validator.setup(self) if validator.respond_to?(:setup)
  33852. if validator.respond_to?(:attributes) && !validator.attributes.empty?
  33853. validator.attributes.each do |attribute|
  33854. _validators[attribute.to_sym] << validator
  33855. end
  33856. else
  33857. _validators[nil] << validator
  33858. end
  33859. validate(validator, options)
  33860. end
  33861. end
  33862. end
  33863. # Passes the record off to the class or classes specified and allows them
  33864. # to add errors based on more complex conditions.
  33865. #
  33866. # class Person
  33867. # include ActiveModel::Validations
  33868. #
  33869. # validate :instance_validations
  33870. #
  33871. # def instance_validations
  33872. # validates_with MyValidator
  33873. # end
  33874. # end
  33875. #
  33876. # Please consult the class method documentation for more information on
  33877. # creating your own validator.
  33878. #
  33879. # You may also pass it multiple classes, like so:
  33880. #
  33881. # class Person
  33882. # include ActiveModel::Validations
  33883. #
  33884. # validate :instance_validations, on: :create
  33885. #
  33886. # def instance_validations
  33887. # validates_with MyValidator, MyOtherValidator
  33888. # end
  33889. # end
  33890. #
  33891. # Standard configuration options (<tt>:on</tt>, <tt>:if</tt> and
  33892. # <tt>:unless</tt>), which are available on the class version of
  33893. # +validates_with+, should instead be placed on the +validates+ method
  33894. # as these are applied and tested in the callback.
  33895. #
  33896. # If you pass any additional configuration options, they will be passed
  33897. # to the class and available as +options+, please refer to the
  33898. # class version of this method for more information.
  33899. def validates_with(*args, &block)
  33900. options = args.extract_options!
  33901. args.each do |klass|
  33902. validator = klass.new(options, &block)
  33903. validator.validate(self)
  33904. end
  33905. end
  33906. end
  33907. end
  33908. require 'active_support/core_ext/array/extract_options'
  33909. require 'active_support/core_ext/hash/keys'
  33910. require 'active_support/core_ext/hash/except'
  33911. module ActiveModel
  33912. # == Active \Model Validations
  33913. #
  33914. # Provides a full validation framework to your objects.
  33915. #
  33916. # A minimal implementation could be:
  33917. #
  33918. # class Person
  33919. # include ActiveModel::Validations
  33920. #
  33921. # attr_accessor :first_name, :last_name
  33922. #
  33923. # validates_each :first_name, :last_name do |record, attr, value|
  33924. # record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
  33925. # end
  33926. # end
  33927. #
  33928. # Which provides you with the full standard validation stack that you
  33929. # know from Active Record:
  33930. #
  33931. # person = Person.new
  33932. # person.valid? # => true
  33933. # person.invalid? # => false
  33934. #
  33935. # person.first_name = 'zoolander'
  33936. # person.valid? # => false
  33937. # person.invalid? # => true
  33938. # person.errors.messages # => {first_name:["starts with z."]}
  33939. #
  33940. # Note that <tt>ActiveModel::Validations</tt> automatically adds an +errors+
  33941. # method to your instances initialized with a new <tt>ActiveModel::Errors</tt>
  33942. # object, so there is no need for you to do this manually.
  33943. module Validations
  33944. extend ActiveSupport::Concern
  33945. included do
  33946. extend ActiveModel::Callbacks
  33947. extend ActiveModel::Translation
  33948. extend HelperMethods
  33949. include HelperMethods
  33950. attr_accessor :validation_context
  33951. define_callbacks :validate, :scope => :name
  33952. class_attribute :_validators
  33953. self._validators = Hash.new { |h,k| h[k] = [] }
  33954. end
  33955. module ClassMethods
  33956. # Validates each attribute against a block.
  33957. #
  33958. # class Person
  33959. # include ActiveModel::Validations
  33960. #
  33961. # attr_accessor :first_name, :last_name
  33962. #
  33963. # validates_each :first_name, :last_name, allow_blank: true do |record, attr, value|
  33964. # record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
  33965. # end
  33966. # end
  33967. #
  33968. # Options:
  33969. # * <tt>:on</tt> - Specifies the context where this validation is active
  33970. # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>)
  33971. # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
  33972. # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
  33973. # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
  33974. # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
  33975. # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
  33976. # proc or string should return or evaluate to a +true+ or +false+ value.
  33977. # * <tt>:unless</tt> - Specifies a method, proc or string to call to
  33978. # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
  33979. # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
  33980. # method, proc or string should return or evaluate to a +true+ or +false+
  33981. # value.
  33982. def validates_each(*attr_names, &block)
  33983. validates_with BlockValidator, _merge_attributes(attr_names), &block
  33984. end
  33985. # Adds a validation method or block to the class. This is useful when
  33986. # overriding the +validate+ instance method becomes too unwieldy and
  33987. # you're looking for more descriptive declaration of your validations.
  33988. #
  33989. # This can be done with a symbol pointing to a method:
  33990. #
  33991. # class Comment
  33992. # include ActiveModel::Validations
  33993. #
  33994. # validate :must_be_friends
  33995. #
  33996. # def must_be_friends
  33997. # errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
  33998. # end
  33999. # end
  34000. #
  34001. # With a block which is passed with the current record to be validated:
  34002. #
  34003. # class Comment
  34004. # include ActiveModel::Validations
  34005. #
  34006. # validate do |comment|
  34007. # comment.must_be_friends
  34008. # end
  34009. #
  34010. # def must_be_friends
  34011. # errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
  34012. # end
  34013. # end
  34014. #
  34015. # Or with a block where self points to the current record to be validated:
  34016. #
  34017. # class Comment
  34018. # include ActiveModel::Validations
  34019. #
  34020. # validate do
  34021. # errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
  34022. # end
  34023. # end
  34024. #
  34025. # Options:
  34026. # * <tt>:on</tt> - Specifies the context where this validation is active
  34027. # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>)
  34028. # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
  34029. # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
  34030. # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
  34031. # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
  34032. # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
  34033. # proc or string should return or evaluate to a +true+ or +false+ value.
  34034. # * <tt>:unless</tt> - Specifies a method, proc or string to call to
  34035. # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
  34036. # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
  34037. # method, proc or string should return or evaluate to a +true+ or +false+
  34038. # value.
  34039. def validate(*args, &block)
  34040. options = args.extract_options!
  34041. if options.key?(:on)
  34042. options = options.dup
  34043. options[:if] = Array(options[:if])
  34044. options[:if].unshift("validation_context == :#{options[:on]}")
  34045. end
  34046. args << options
  34047. set_callback(:validate, *args, &block)
  34048. end
  34049. # List all validators that are being used to validate the model using
  34050. # +validates_with+ method.
  34051. #
  34052. # class Person
  34053. # include ActiveModel::Validations
  34054. #
  34055. # validates_with MyValidator
  34056. # validates_with OtherValidator, on: :create
  34057. # validates_with StrictValidator, strict: true
  34058. # end
  34059. #
  34060. # Person.validators
  34061. # # => [
  34062. # # #<MyValidator:0x007fbff403e808 @options={}>,
  34063. # # #<OtherValidator:0x007fbff403d930 @options={on: :create}>,
  34064. # # #<StrictValidator:0x007fbff3204a30 @options={strict:true}>
  34065. # # ]
  34066. def validators
  34067. _validators.values.flatten.uniq
  34068. end
  34069. # List all validators that are being used to validate a specific attribute.
  34070. #
  34071. # class Person
  34072. # include ActiveModel::Validations
  34073. #
  34074. # attr_accessor :name , :age
  34075. #
  34076. # validates_presence_of :name
  34077. # validates_inclusion_of :age, in: 0..99
  34078. # end
  34079. #
  34080. # Person.validators_on(:name)
  34081. # # => [
  34082. # # #<ActiveModel::Validations::PresenceValidator:0x007fe604914e60 @attributes=[:name], @options={}>,
  34083. # # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={in:0..99}>
  34084. # # ]
  34085. def validators_on(*attributes)
  34086. attributes.flat_map do |attribute|
  34087. _validators[attribute.to_sym]
  34088. end
  34089. end
  34090. # Returns +true+ if +attribute+ is an attribute method, +false+ otherwise.
  34091. #
  34092. # class Person
  34093. # include ActiveModel::Validations
  34094. #
  34095. # attr_accessor :name
  34096. # end
  34097. #
  34098. # User.attribute_method?(:name) # => true
  34099. # User.attribute_method?(:age) # => false
  34100. def attribute_method?(attribute)
  34101. method_defined?(attribute)
  34102. end
  34103. # Copy validators on inheritance.
  34104. def inherited(base) #:nodoc:
  34105. dup = _validators.dup
  34106. base._validators = dup.each { |k, v| dup[k] = v.dup }
  34107. super
  34108. end
  34109. end
  34110. # Clean the +Errors+ object if instance is duped.
  34111. def initialize_dup(other) #:nodoc:
  34112. @errors = nil
  34113. super
  34114. end
  34115. # Returns the +Errors+ object that holds all information about attribute
  34116. # error messages.
  34117. #
  34118. # class Person
  34119. # include ActiveModel::Validations
  34120. #
  34121. # attr_accessor :name
  34122. # validates_presence_of :name
  34123. # end
  34124. #
  34125. # person = Person.new
  34126. # person.valid? # => false
  34127. # person.errors # => #<ActiveModel::Errors:0x007fe603816640 @messages={name:["can't be blank"]}>
  34128. def errors
  34129. @errors ||= Errors.new(self)
  34130. end
  34131. # Runs all the specified validations and returns +true+ if no errors were
  34132. # added otherwise +false+.
  34133. #
  34134. # class Person
  34135. # include ActiveModel::Validations
  34136. #
  34137. # attr_accessor :name
  34138. # validates_presence_of :name
  34139. # end
  34140. #
  34141. # person = Person.new
  34142. # person.name = ''
  34143. # person.valid? # => false
  34144. # person.name = 'david'
  34145. # person.valid? # => true
  34146. #
  34147. # Context can optionally be supplied to define which callbacks to test
  34148. # against (the context is defined on the validations using <tt>:on</tt>).
  34149. #
  34150. # class Person
  34151. # include ActiveModel::Validations
  34152. #
  34153. # attr_accessor :name
  34154. # validates_presence_of :name, on: :new
  34155. # end
  34156. #
  34157. # person = Person.new
  34158. # person.valid? # => true
  34159. # person.valid?(:new) # => false
  34160. def valid?(context = nil)
  34161. current_context, self.validation_context = validation_context, context
  34162. errors.clear
  34163. run_validations!
  34164. ensure
  34165. self.validation_context = current_context
  34166. end
  34167. # Performs the opposite of <tt>valid?</tt>. Returns +true+ if errors were
  34168. # added, +false+ otherwise.
  34169. #
  34170. # class Person
  34171. # include ActiveModel::Validations
  34172. #
  34173. # attr_accessor :name
  34174. # validates_presence_of :name
  34175. # end
  34176. #
  34177. # person = Person.new
  34178. # person.name = ''
  34179. # person.invalid? # => true
  34180. # person.name = 'david'
  34181. # person.invalid? # => false
  34182. #
  34183. # Context can optionally be supplied to define which callbacks to test
  34184. # against (the context is defined on the validations using <tt>:on</tt>).
  34185. #
  34186. # class Person
  34187. # include ActiveModel::Validations
  34188. #
  34189. # attr_accessor :name
  34190. # validates_presence_of :name, on: :new
  34191. # end
  34192. #
  34193. # person = Person.new
  34194. # person.invalid? # => false
  34195. # person.invalid?(:new) # => true
  34196. def invalid?(context = nil)
  34197. !valid?(context)
  34198. end
  34199. # Hook method defining how an attribute value should be retrieved. By default
  34200. # this is assumed to be an instance named after the attribute. Override this
  34201. # method in subclasses should you need to retrieve the value for a given
  34202. # attribute differently:
  34203. #
  34204. # class MyClass
  34205. # include ActiveModel::Validations
  34206. #
  34207. # def initialize(data = {})
  34208. # @data = data
  34209. # end
  34210. #
  34211. # def read_attribute_for_validation(key)
  34212. # @data[key]
  34213. # end
  34214. # end
  34215. alias :read_attribute_for_validation :send
  34216. protected
  34217. def run_validations! #:nodoc:
  34218. run_callbacks :validate
  34219. errors.empty?
  34220. end
  34221. end
  34222. end
  34223. Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
  34224. filename = File.basename(path)
  34225. require "active_model/validations/#{filename}"
  34226. end
  34227. require "active_support/core_ext/module/anonymous"
  34228. module ActiveModel
  34229. # == Active \Model \Validator
  34230. #
  34231. # A simple base class that can be used along with
  34232. # ActiveModel::Validations::ClassMethods.validates_with
  34233. #
  34234. # class Person
  34235. # include ActiveModel::Validations
  34236. # validates_with MyValidator
  34237. # end
  34238. #
  34239. # class MyValidator < ActiveModel::Validator
  34240. # def validate(record)
  34241. # if some_complex_logic
  34242. # record.errors[:base] = "This record is invalid"
  34243. # end
  34244. # end
  34245. #
  34246. # private
  34247. # def some_complex_logic
  34248. # # ...
  34249. # end
  34250. # end
  34251. #
  34252. # Any class that inherits from ActiveModel::Validator must implement a method
  34253. # called +validate+ which accepts a +record+.
  34254. #
  34255. # class Person
  34256. # include ActiveModel::Validations
  34257. # validates_with MyValidator
  34258. # end
  34259. #
  34260. # class MyValidator < ActiveModel::Validator
  34261. # def validate(record)
  34262. # record # => The person instance being validated
  34263. # options # => Any non-standard options passed to validates_with
  34264. # end
  34265. # end
  34266. #
  34267. # To cause a validation error, you must add to the +record+'s errors directly
  34268. # from within the validators message.
  34269. #
  34270. # class MyValidator < ActiveModel::Validator
  34271. # def validate(record)
  34272. # record.errors.add :base, "This is some custom error message"
  34273. # record.errors.add :first_name, "This is some complex validation"
  34274. # # etc...
  34275. # end
  34276. # end
  34277. #
  34278. # To add behavior to the initialize method, use the following signature:
  34279. #
  34280. # class MyValidator < ActiveModel::Validator
  34281. # def initialize(options)
  34282. # super
  34283. # @my_custom_field = options[:field_name] || :first_name
  34284. # end
  34285. # end
  34286. #
  34287. # Note that the validator is initialized only once for the whole application
  34288. # lifecycle, and not on each validation run.
  34289. #
  34290. # The easiest way to add custom validators for validating individual attributes
  34291. # is with the convenient <tt>ActiveModel::EachValidator</tt>.
  34292. #
  34293. # class TitleValidator < ActiveModel::EachValidator
  34294. # def validate_each(record, attribute, value)
  34295. # record.errors.add attribute, 'must be Mr., Mrs., or Dr.' unless %w(Mr. Mrs. Dr.).include?(value)
  34296. # end
  34297. # end
  34298. #
  34299. # This can now be used in combination with the +validates+ method
  34300. # (see <tt>ActiveModel::Validations::ClassMethods.validates</tt> for more on this).
  34301. #
  34302. # class Person
  34303. # include ActiveModel::Validations
  34304. # attr_accessor :title
  34305. #
  34306. # validates :title, presence: true
  34307. # end
  34308. #
  34309. # Validator may also define a +setup+ instance method which will get called
  34310. # with the class that using that validator as its argument. This can be
  34311. # useful when there are prerequisites such as an +attr_accessor+ being present.
  34312. #
  34313. # class MyValidator < ActiveModel::Validator
  34314. # def setup(klass)
  34315. # klass.send :attr_accessor, :custom_attribute
  34316. # end
  34317. # end
  34318. #
  34319. # This setup method is only called when used with validation macros or the
  34320. # class level <tt>validates_with</tt> method.
  34321. class Validator
  34322. attr_reader :options
  34323. # Returns the kind of the validator.
  34324. #
  34325. # PresenceValidator.kind # => :presence
  34326. # UniquenessValidator.kind # => :uniqueness
  34327. def self.kind
  34328. @kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
  34329. end
  34330. # Accepts options that will be made available through the +options+ reader.
  34331. def initialize(options = {})
  34332. @options = options.freeze
  34333. end
  34334. # Return the kind for this validator.
  34335. #
  34336. # PresenceValidator.new.kind # => :presence
  34337. # UniquenessValidator.new.kind # => :uniqueness
  34338. def kind
  34339. self.class.kind
  34340. end
  34341. # Override this method in subclasses with validation logic, adding errors
  34342. # to the records +errors+ array where necessary.
  34343. def validate(record)
  34344. raise NotImplementedError, "Subclasses must implement a validate(record) method."
  34345. end
  34346. end
  34347. # +EachValidator+ is a validator which iterates through the attributes given
  34348. # in the options hash invoking the <tt>validate_each</tt> method passing in the
  34349. # record, attribute and value.
  34350. #
  34351. # All Active Model validations are built on top of this validator.
  34352. class EachValidator < Validator #:nodoc:
  34353. attr_reader :attributes
  34354. # Returns a new validator instance. All options will be available via the
  34355. # +options+ reader, however the <tt>:attributes</tt> option will be removed
  34356. # and instead be made available through the +attributes+ reader.
  34357. def initialize(options)
  34358. @attributes = Array(options.delete(:attributes))
  34359. raise ArgumentError, ":attributes cannot be blank" if @attributes.empty?
  34360. super
  34361. check_validity!
  34362. end
  34363. # Performs validation on the supplied record. By default this will call
  34364. # +validates_each+ to determine validity therefore subclasses should
  34365. # override +validates_each+ with validation logic.
  34366. def validate(record)
  34367. attributes.each do |attribute|
  34368. value = record.read_attribute_for_validation(attribute)
  34369. next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
  34370. validate_each(record, attribute, value)
  34371. end
  34372. end
  34373. # Override this method in subclasses with the validation logic, adding
  34374. # errors to the records +errors+ array where necessary.
  34375. def validate_each(record, attribute, value)
  34376. raise NotImplementedError, "Subclasses must implement a validate_each(record, attribute, value) method"
  34377. end
  34378. # Hook method that gets called by the initializer allowing verification
  34379. # that the arguments supplied are valid. You could for example raise an
  34380. # +ArgumentError+ when invalid options are supplied.
  34381. def check_validity!
  34382. end
  34383. end
  34384. # +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
  34385. # and call this block for each attribute being validated. +validates_each+ uses this validator.
  34386. class BlockValidator < EachValidator #:nodoc:
  34387. def initialize(options, &block)
  34388. @block = block
  34389. super
  34390. end
  34391. private
  34392. def validate_each(record, attribute, value)
  34393. @block.call(record, attribute, value)
  34394. end
  34395. end
  34396. end
  34397. module ActiveModel
  34398. module VERSION #:nodoc:
  34399. MAJOR = 4
  34400. MINOR = 0
  34401. TINY = 0
  34402. PRE = "beta"
  34403. STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
  34404. end
  34405. end
  34406. #--
  34407. # Copyright (c) 2004-2013 David Heinemeier Hansson
  34408. #
  34409. # Permission is hereby granted, free of charge, to any person obtaining
  34410. # a copy of this software and associated documentation files (the
  34411. # "Software"), to deal in the Software without restriction, including
  34412. # without limitation the rights to use, copy, modify, merge, publish,
  34413. # distribute, sublicense, and/or sell copies of the Software, and to
  34414. # permit persons to whom the Software is furnished to do so, subject to
  34415. # the following conditions:
  34416. #
  34417. # The above copyright notice and this permission notice shall be
  34418. # included in all copies or substantial portions of the Software.
  34419. #
  34420. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  34421. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  34422. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  34423. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  34424. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  34425. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  34426. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  34427. #++
  34428. require 'active_support'
  34429. require 'active_support/rails'
  34430. require 'active_model/version'
  34431. module ActiveModel
  34432. extend ActiveSupport::Autoload
  34433. autoload :AttributeMethods
  34434. autoload :BlockValidator, 'active_model/validator'
  34435. autoload :Callbacks
  34436. autoload :Conversion
  34437. autoload :Dirty
  34438. autoload :EachValidator, 'active_model/validator'
  34439. autoload :ForbiddenAttributesProtection
  34440. autoload :Lint
  34441. autoload :Model
  34442. autoload :DeprecatedMassAssignmentSecurity
  34443. autoload :Name, 'active_model/naming'
  34444. autoload :Naming
  34445. autoload :SecurePassword
  34446. autoload :Serialization
  34447. autoload :TestCase
  34448. autoload :Translation
  34449. autoload :Validations
  34450. autoload :Validator
  34451. eager_autoload do
  34452. autoload :Errors
  34453. end
  34454. module Serializers
  34455. extend ActiveSupport::Autoload
  34456. eager_autoload do
  34457. autoload :JSON
  34458. autoload :Xml
  34459. end
  34460. end
  34461. def eager_load!
  34462. super
  34463. ActiveModel::Serializer.eager_load!
  34464. end
  34465. end
  34466. ActiveSupport.on_load(:i18n) do
  34467. I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
  34468. end
  34469. require File.expand_path('../../../load_paths', __FILE__)
  34470. require "active_record"
  34471. require 'benchmark/ips'
  34472. TIME = (ENV['BENCHMARK_TIME'] || 20).to_i
  34473. RECORDS = (ENV['BENCHMARK_RECORDS'] || TIME*1000).to_i
  34474. conn = { :adapter => 'sqlite3', :database => ':memory:' }
  34475. ActiveRecord::Base.establish_connection(conn)
  34476. class User < ActiveRecord::Base
  34477. connection.create_table :users, :force => true do |t|
  34478. t.string :name, :email
  34479. t.timestamps
  34480. end
  34481. has_many :exhibits
  34482. end
  34483. class Exhibit < ActiveRecord::Base
  34484. connection.create_table :exhibits, :force => true do |t|
  34485. t.belongs_to :user
  34486. t.string :name
  34487. t.text :notes
  34488. t.timestamps
  34489. end
  34490. belongs_to :user
  34491. def look; attributes end
  34492. def feel; look; user.name end
  34493. def self.with_name
  34494. where("name IS NOT NULL")
  34495. end
  34496. def self.with_notes
  34497. where("notes IS NOT NULL")
  34498. end
  34499. def self.look(exhibits) exhibits.each { |e| e.look } end
  34500. def self.feel(exhibits) exhibits.each { |e| e.feel } end
  34501. end
  34502. puts 'Generating data...'
  34503. module ActiveRecord
  34504. class Faker
  34505. LOREM = %Q{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit.
  34506. Integer consequat tincidunt felis. Etiam non erat dolor. Vivamus imperdiet nibh sit amet diam eleifend id posuere diam malesuada. Mauris at accumsan sem.
  34507. Donec id lorem neque. Fusce erat lorem, ornare eu congue vitae, malesuada quis neque. Maecenas vel urna a velit pretium fermentum. Donec tortor enim,
  34508. tempor venenatis egestas a, tempor sed ipsum. Ut arcu justo, faucibus non imperdiet ac, interdum at diam. Pellentesque ipsum enim, venenatis ut iaculis vitae,
  34509. varius vitae sem. Sed rutrum quam ac elit euismod bibendum. Donec ultricies ultricies magna, at lacinia libero mollis aliquam. Sed ac arcu in tortor elementum
  34510. tincidunt vel interdum sem. Curabitur eget erat arcu. Praesent eget eros leo. Nam magna enim, sollicitudin vehicula scelerisque in, vulputate ut libero.
  34511. Praesent varius tincidunt commodo}.split
  34512. def self.name
  34513. LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join ' '
  34514. end
  34515. def self.email
  34516. LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join('@') + ".com"
  34517. end
  34518. end
  34519. end
  34520. # pre-compute the insert statements and fake data compilation,
  34521. # so the benchmarks below show the actual runtime for the execute
  34522. # method, minus the setup steps
  34523. # Using the same paragraph for all exhibits because it is very slow
  34524. # to generate unique paragraphs for all exhibits.
  34525. notes = ActiveRecord::Faker::LOREM.join ' '
  34526. today = Date.today
  34527. puts "Inserting #{RECORDS} users and exhibits..."
  34528. RECORDS.times do
  34529. user = User.create(
  34530. :created_at => today,
  34531. :name => ActiveRecord::Faker.name,
  34532. :email => ActiveRecord::Faker.email
  34533. )
  34534. Exhibit.create(
  34535. :created_at => today,
  34536. :name => ActiveRecord::Faker.name,
  34537. :user => user,
  34538. :notes => notes
  34539. )
  34540. end
  34541. Benchmark.ips(TIME) do |x|
  34542. ar_obj = Exhibit.find(1)
  34543. attrs = { :name => 'sam' }
  34544. attrs_first = { :name => 'sam' }
  34545. attrs_second = { :name => 'tom' }
  34546. exhibit = {
  34547. :name => ActiveRecord::Faker.name,
  34548. :notes => notes,
  34549. :created_at => Date.today
  34550. }
  34551. x.report("Model#id") do
  34552. ar_obj.id
  34553. end
  34554. x.report 'Model.new (instantiation)' do
  34555. Exhibit.new
  34556. end
  34557. x.report 'Model.new (setting attributes)' do
  34558. Exhibit.new(attrs)
  34559. end
  34560. x.report 'Model.first' do
  34561. Exhibit.first.look
  34562. end
  34563. x.report("Model.all limit(100)") do
  34564. Exhibit.look Exhibit.limit(100)
  34565. end
  34566. x.report "Model.all limit(100) with relationship" do
  34567. Exhibit.feel Exhibit.limit(100).includes(:user)
  34568. end
  34569. x.report "Model.all limit(10,000)" do
  34570. Exhibit.look Exhibit.limit(10000)
  34571. end
  34572. x.report 'Model.named_scope' do
  34573. Exhibit.limit(10).with_name.with_notes
  34574. end
  34575. x.report 'Model.create' do
  34576. Exhibit.create(exhibit)
  34577. end
  34578. x.report 'Resource#attributes=' do
  34579. e = Exhibit.new(attrs_first)
  34580. e.attributes = attrs_second
  34581. end
  34582. x.report 'Resource#update' do
  34583. Exhibit.first.update(name: 'bob')
  34584. end
  34585. x.report 'Resource#destroy' do
  34586. Exhibit.first.destroy
  34587. end
  34588. x.report 'Model.transaction' do
  34589. Exhibit.transaction { Exhibit.new }
  34590. end
  34591. x.report 'Model.find(id)' do
  34592. User.find(1)
  34593. end
  34594. x.report 'Model.find_by_sql' do
  34595. Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first
  34596. end
  34597. x.report "Model.log" do
  34598. Exhibit.connection.send(:log, "hello", "world") {}
  34599. end
  34600. x.report "AR.execute(query)" do
  34601. ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}")
  34602. end
  34603. end
  34604. $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
  34605. require 'active_record'
  34606. class Person < ActiveRecord::Base
  34607. establish_connection :adapter => 'sqlite3', :database => 'foobar.db'
  34608. connection.create_table table_name, :force => true do |t|
  34609. t.string :name
  34610. end
  34611. end
  34612. bob = Person.create!(:name => 'bob')
  34613. puts Person.all.inspect
  34614. bob.destroy
  34615. puts Person.all.inspect
  34616. module ActiveRecord
  34617. # = Active Record Aggregations
  34618. module Aggregations # :nodoc:
  34619. extend ActiveSupport::Concern
  34620. def clear_aggregation_cache #:nodoc:
  34621. @aggregation_cache.clear if persisted?
  34622. end
  34623. # Active Record implements aggregation through a macro-like class method called +composed_of+
  34624. # for representing attributes as value objects. It expresses relationships like "Account [is]
  34625. # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
  34626. # to the macro adds a description of how the value objects are created from the attributes of
  34627. # the entity object (when the entity is initialized either as a new object or from finding an
  34628. # existing object) and how it can be turned back into attributes (when the entity is saved to
  34629. # the database).
  34630. #
  34631. # class Customer < ActiveRecord::Base
  34632. # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
  34633. # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
  34634. # end
  34635. #
  34636. # The customer class now has the following methods to manipulate the value objects:
  34637. # * <tt>Customer#balance, Customer#balance=(money)</tt>
  34638. # * <tt>Customer#address, Customer#address=(address)</tt>
  34639. #
  34640. # These methods will operate with value objects like the ones described below:
  34641. #
  34642. # class Money
  34643. # include Comparable
  34644. # attr_reader :amount, :currency
  34645. # EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
  34646. #
  34647. # def initialize(amount, currency = "USD")
  34648. # @amount, @currency = amount, currency
  34649. # end
  34650. #
  34651. # def exchange_to(other_currency)
  34652. # exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
  34653. # Money.new(exchanged_amount, other_currency)
  34654. # end
  34655. #
  34656. # def ==(other_money)
  34657. # amount == other_money.amount && currency == other_money.currency
  34658. # end
  34659. #
  34660. # def <=>(other_money)
  34661. # if currency == other_money.currency
  34662. # amount <=> other_money.amount
  34663. # else
  34664. # amount <=> other_money.exchange_to(currency).amount
  34665. # end
  34666. # end
  34667. # end
  34668. #
  34669. # class Address
  34670. # attr_reader :street, :city
  34671. # def initialize(street, city)
  34672. # @street, @city = street, city
  34673. # end
  34674. #
  34675. # def close_to?(other_address)
  34676. # city == other_address.city
  34677. # end
  34678. #
  34679. # def ==(other_address)
  34680. # city == other_address.city && street == other_address.street
  34681. # end
  34682. # end
  34683. #
  34684. # Now it's possible to access attributes from the database through the value objects instead. If
  34685. # you choose to name the composition the same as the attribute's name, it will be the only way to
  34686. # access that attribute. That's the case with our +balance+ attribute. You interact with the value
  34687. # objects just like you would with any other attribute:
  34688. #
  34689. # customer.balance = Money.new(20) # sets the Money value object and the attribute
  34690. # customer.balance # => Money value object
  34691. # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
  34692. # customer.balance > Money.new(10) # => true
  34693. # customer.balance == Money.new(20) # => true
  34694. # customer.balance < Money.new(5) # => false
  34695. #
  34696. # Value objects can also be composed of multiple attributes, such as the case of Address. The order
  34697. # of the mappings will determine the order of the parameters.
  34698. #
  34699. # customer.address_street = "Hyancintvej"
  34700. # customer.address_city = "Copenhagen"
  34701. # customer.address # => Address.new("Hyancintvej", "Copenhagen")
  34702. #
  34703. # customer.address_street = "Vesterbrogade"
  34704. # customer.address # => Address.new("Hyancintvej", "Copenhagen")
  34705. # customer.clear_aggregation_cache
  34706. # customer.address # => Address.new("Vesterbrogade", "Copenhagen")
  34707. #
  34708. # customer.address = Address.new("May Street", "Chicago")
  34709. # customer.address_street # => "May Street"
  34710. # customer.address_city # => "Chicago"
  34711. #
  34712. # == Writing value objects
  34713. #
  34714. # Value objects are immutable and interchangeable objects that represent a given value, such as
  34715. # a Money object representing $5. Two Money objects both representing $5 should be equal (through
  34716. # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
  34717. # unlike entity objects where equality is determined by identity. An entity class such as Customer can
  34718. # easily have two different objects that both have an address on Hyancintvej. Entity identity is
  34719. # determined by object or relational unique identifiers (such as primary keys). Normal
  34720. # ActiveRecord::Base classes are entity objects.
  34721. #
  34722. # It's also important to treat the value objects as immutable. Don't allow the Money object to have
  34723. # its amount changed after creation. Create a new Money object with the new value instead. The
  34724. # Money#exchange_to method is an example of this. It returns a new value object instead of changing
  34725. # its own values. Active Record won't persist value objects that have been changed through means
  34726. # other than the writer method.
  34727. #
  34728. # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
  34729. # object. Attempting to change it afterwards will result in a RuntimeError.
  34730. #
  34731. # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
  34732. # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
  34733. #
  34734. # == Custom constructors and converters
  34735. #
  34736. # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
  34737. # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
  34738. # option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
  34739. # a custom constructor to be specified.
  34740. #
  34741. # When a new value is assigned to the value object, the default assumption is that the new value
  34742. # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
  34743. # converted to an instance of value class if necessary.
  34744. #
  34745. # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
  34746. # should be aggregated using the NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor
  34747. # for the value class is called +create+ and it expects a CIDR address string as a parameter. New
  34748. # values can be assigned to the value object using either another NetAddr::CIDR object, a string
  34749. # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
  34750. # these requirements:
  34751. #
  34752. # class NetworkResource < ActiveRecord::Base
  34753. # composed_of :cidr,
  34754. # class_name: 'NetAddr::CIDR',
  34755. # mapping: [ %w(network_address network), %w(cidr_range bits) ],
  34756. # allow_nil: true,
  34757. # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
  34758. # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
  34759. # end
  34760. #
  34761. # # This calls the :constructor
  34762. # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
  34763. #
  34764. # # These assignments will both use the :converter
  34765. # network_resource.cidr = [ '192.168.2.1', 8 ]
  34766. # network_resource.cidr = '192.168.0.1/24'
  34767. #
  34768. # # This assignment won't use the :converter as the value is already an instance of the value class
  34769. # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
  34770. #
  34771. # # Saving and then reloading will use the :constructor on reload
  34772. # network_resource.save
  34773. # network_resource.reload
  34774. #
  34775. # == Finding records by a value object
  34776. #
  34777. # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database
  34778. # by specifying an instance of the value object in the conditions hash. The following example
  34779. # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
  34780. #
  34781. # Customer.where(balance: Money.new(20, "USD"))
  34782. #
  34783. module ClassMethods
  34784. # Adds reader and writer methods for manipulating a value object:
  34785. # <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
  34786. #
  34787. # Options are:
  34788. # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
  34789. # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
  34790. # to the Address class, but if the real class name is CompanyAddress, you'll have to specify it
  34791. # with this option.
  34792. # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
  34793. # object. Each mapping is represented as an array where the first item is the name of the
  34794. # entity attribute and the second item is the name of the attribute in the value object. The
  34795. # order in which mappings are defined determines the order in which attributes are sent to the
  34796. # value class constructor.
  34797. # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
  34798. # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
  34799. # mapped attributes.
  34800. # This defaults to +false+.
  34801. # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
  34802. # is called to initialize the value object. The constructor is passed all of the mapped attributes,
  34803. # in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
  34804. # to instantiate a <tt>:class_name</tt> object.
  34805. # The default is <tt>:new</tt>.
  34806. # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
  34807. # or a Proc that is called when a new value is assigned to the value object. The converter is
  34808. # passed the single value that is used in the assignment and is only called if the new value is
  34809. # not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
  34810. # can return nil to skip the assignment.
  34811. #
  34812. # Option examples:
  34813. # composed_of :temperature, mapping: %w(reading celsius)
  34814. # composed_of :balance, class_name: "Money", mapping: %w(balance amount),
  34815. # converter: Proc.new { |balance| balance.to_money }
  34816. # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
  34817. # composed_of :gps_location
  34818. # composed_of :gps_location, allow_nil: true
  34819. # composed_of :ip_address,
  34820. # class_name: 'IPAddr',
  34821. # mapping: %w(ip to_i),
  34822. # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
  34823. # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
  34824. #
  34825. def composed_of(part_id, options = {})
  34826. options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
  34827. name = part_id.id2name
  34828. class_name = options[:class_name] || name.camelize
  34829. mapping = options[:mapping] || [ name, name ]
  34830. mapping = [ mapping ] unless mapping.first.is_a?(Array)
  34831. allow_nil = options[:allow_nil] || false
  34832. constructor = options[:constructor] || :new
  34833. converter = options[:converter]
  34834. reader_method(name, class_name, mapping, allow_nil, constructor)
  34835. writer_method(name, class_name, mapping, allow_nil, converter)
  34836. create_reflection(:composed_of, part_id, nil, options, self)
  34837. end
  34838. private
  34839. def reader_method(name, class_name, mapping, allow_nil, constructor)
  34840. define_method(name) do
  34841. if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
  34842. attrs = mapping.collect {|pair| read_attribute(pair.first)}
  34843. object = constructor.respond_to?(:call) ?
  34844. constructor.call(*attrs) :
  34845. class_name.constantize.send(constructor, *attrs)
  34846. @aggregation_cache[name] = object
  34847. end
  34848. @aggregation_cache[name]
  34849. end
  34850. end
  34851. def writer_method(name, class_name, mapping, allow_nil, converter)
  34852. define_method("#{name}=") do |part|
  34853. klass = class_name.constantize
  34854. unless part.is_a?(klass) || converter.nil? || part.nil?
  34855. part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
  34856. end
  34857. if part.nil? && allow_nil
  34858. mapping.each { |pair| self[pair.first] = nil }
  34859. @aggregation_cache[name] = nil
  34860. else
  34861. mapping.each { |pair| self[pair.first] = part.send(pair.last) }
  34862. @aggregation_cache[name] = part.freeze
  34863. end
  34864. end
  34865. end
  34866. end
  34867. end
  34868. end
  34869. require 'active_support/core_ext/string/conversions'
  34870. module ActiveRecord
  34871. module Associations
  34872. # Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
  34873. # ActiveRecord::Associations::ThroughAssociationScope
  34874. class AliasTracker # :nodoc:
  34875. attr_reader :aliases, :table_joins, :connection
  34876. # table_joins is an array of arel joins which might conflict with the aliases we assign here
  34877. def initialize(connection = Base.connection, table_joins = [])
  34878. @aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
  34879. @table_joins = table_joins
  34880. @connection = connection
  34881. end
  34882. def aliased_table_for(table_name, aliased_name = nil)
  34883. table_alias = aliased_name_for(table_name, aliased_name)
  34884. if table_alias == table_name
  34885. Arel::Table.new(table_name)
  34886. else
  34887. Arel::Table.new(table_name).alias(table_alias)
  34888. end
  34889. end
  34890. def aliased_name_for(table_name, aliased_name = nil)
  34891. aliased_name ||= table_name
  34892. if aliases[table_name].zero?
  34893. # If it's zero, we can have our table_name
  34894. aliases[table_name] = 1
  34895. table_name
  34896. else
  34897. # Otherwise, we need to use an alias
  34898. aliased_name = connection.table_alias_for(aliased_name)
  34899. # Update the count
  34900. aliases[aliased_name] += 1
  34901. if aliases[aliased_name] > 1
  34902. "#{truncate(aliased_name)}_#{aliases[aliased_name]}"
  34903. else
  34904. aliased_name
  34905. end
  34906. end
  34907. end
  34908. private
  34909. def initial_count_for(name)
  34910. return 0 if Arel::Table === table_joins
  34911. # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
  34912. quoted_name = connection.quote_table_name(name).downcase
  34913. counts = table_joins.map do |join|
  34914. if join.is_a?(Arel::Nodes::StringJoin)
  34915. # Table names + table aliases
  34916. join.left.downcase.scan(
  34917. /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
  34918. ).size
  34919. else
  34920. join.left.table_name == name ? 1 : 0
  34921. end
  34922. end
  34923. counts.sum
  34924. end
  34925. def truncate(name)
  34926. name.slice(0, connection.table_alias_length - 2)
  34927. end
  34928. end
  34929. end
  34930. end
  34931. require 'active_support/core_ext/array/wrap'
  34932. module ActiveRecord
  34933. module Associations
  34934. # = Active Record Associations
  34935. #
  34936. # This is the root class of all associations ('+ Foo' signifies an included module Foo):
  34937. #
  34938. # Association
  34939. # SingularAssociation
  34940. # HasOneAssociation
  34941. # HasOneThroughAssociation + ThroughAssociation
  34942. # BelongsToAssociation
  34943. # BelongsToPolymorphicAssociation
  34944. # CollectionAssociation
  34945. # HasAndBelongsToManyAssociation
  34946. # HasManyAssociation
  34947. # HasManyThroughAssociation + ThroughAssociation
  34948. class Association #:nodoc:
  34949. attr_reader :owner, :target, :reflection
  34950. delegate :options, :to => :reflection
  34951. def initialize(owner, reflection)
  34952. reflection.check_validity!
  34953. @owner, @reflection = owner, reflection
  34954. reset
  34955. reset_scope
  34956. end
  34957. # Returns the name of the table of the related class:
  34958. #
  34959. # post.comments.aliased_table_name # => "comments"
  34960. #
  34961. def aliased_table_name
  34962. klass.table_name
  34963. end
  34964. # Resets the \loaded flag to +false+ and sets the \target to +nil+.
  34965. def reset
  34966. @loaded = false
  34967. @target = nil
  34968. @stale_state = nil
  34969. end
  34970. # Reloads the \target and returns +self+ on success.
  34971. def reload
  34972. reset
  34973. reset_scope
  34974. load_target
  34975. self unless target.nil?
  34976. end
  34977. # Has the \target been already \loaded?
  34978. def loaded?
  34979. @loaded
  34980. end
  34981. # Asserts the \target has been loaded setting the \loaded flag to +true+.
  34982. def loaded!
  34983. @loaded = true
  34984. @stale_state = stale_state
  34985. end
  34986. # The target is stale if the target no longer points to the record(s) that the
  34987. # relevant foreign_key(s) refers to. If stale, the association accessor method
  34988. # on the owner will reload the target. It's up to subclasses to implement the
  34989. # state_state method if relevant.
  34990. #
  34991. # Note that if the target has not been loaded, it is not considered stale.
  34992. def stale_target?
  34993. loaded? && @stale_state != stale_state
  34994. end
  34995. # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
  34996. def target=(target)
  34997. @target = target
  34998. loaded!
  34999. end
  35000. def scope
  35001. target_scope.merge(association_scope)
  35002. end
  35003. def scoped
  35004. ActiveSupport::Deprecation.warn "#scoped is deprecated. use #scope instead."
  35005. scope
  35006. end
  35007. # The scope for this association.
  35008. #
  35009. # Note that the association_scope is merged into the target_scope only when the
  35010. # scoped method is called. This is because at that point the call may be surrounded
  35011. # by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
  35012. # actually gets built.
  35013. def association_scope
  35014. if klass
  35015. @association_scope ||= AssociationScope.new(self).scope
  35016. end
  35017. end
  35018. def reset_scope
  35019. @association_scope = nil
  35020. end
  35021. # Set the inverse association, if possible
  35022. def set_inverse_instance(record)
  35023. if record && invertible_for?(record)
  35024. inverse = record.association(inverse_reflection_for(record).name)
  35025. inverse.target = owner
  35026. end
  35027. end
  35028. # This class of the target. belongs_to polymorphic overrides this to look at the
  35029. # polymorphic_type field on the owner.
  35030. def klass
  35031. reflection.klass
  35032. end
  35033. # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
  35034. # through association's scope)
  35035. def target_scope
  35036. klass.all
  35037. end
  35038. # Loads the \target if needed and returns it.
  35039. #
  35040. # This method is abstract in the sense that it relies on +find_target+,
  35041. # which is expected to be provided by descendants.
  35042. #
  35043. # If the \target is already \loaded it is just returned. Thus, you can call
  35044. # +load_target+ unconditionally to get the \target.
  35045. #
  35046. # ActiveRecord::RecordNotFound is rescued within the method, and it is
  35047. # not reraised. The proxy is \reset and +nil+ is the return value.
  35048. def load_target
  35049. @target = find_target if (@stale_state && stale_target?) || find_target?
  35050. loaded! unless loaded?
  35051. target
  35052. rescue ActiveRecord::RecordNotFound
  35053. reset
  35054. end
  35055. def interpolate(sql, record = nil)
  35056. if sql.respond_to?(:to_proc)
  35057. owner.send(:instance_exec, record, &sql)
  35058. else
  35059. sql
  35060. end
  35061. end
  35062. # We can't dump @reflection since it contains the scope proc
  35063. def marshal_dump
  35064. ivars = (instance_variables - [:@reflection]).map { |name| [name, instance_variable_get(name)] }
  35065. [@reflection.name, ivars]
  35066. end
  35067. def marshal_load(data)
  35068. reflection_name, ivars = data
  35069. ivars.each { |name, val| instance_variable_set(name, val) }
  35070. @reflection = @owner.class.reflect_on_association(reflection_name)
  35071. end
  35072. private
  35073. def find_target?
  35074. !loaded? && (!owner.new_record? || foreign_key_present?) && klass
  35075. end
  35076. def creation_attributes
  35077. attributes = {}
  35078. if (reflection.macro == :has_one || reflection.macro == :has_many) && !options[:through]
  35079. attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
  35080. if reflection.options[:as]
  35081. attributes[reflection.type] = owner.class.base_class.name
  35082. end
  35083. end
  35084. attributes
  35085. end
  35086. # Sets the owner attributes on the given record
  35087. def set_owner_attributes(record)
  35088. creation_attributes.each { |key, value| record[key] = value }
  35089. end
  35090. # Should be true if there is a foreign key present on the owner which
  35091. # references the target. This is used to determine whether we can load
  35092. # the target if the owner is currently a new record (and therefore
  35093. # without a key).
  35094. #
  35095. # Currently implemented by belongs_to (vanilla and polymorphic) and
  35096. # has_one/has_many :through associations which go through a belongs_to
  35097. def foreign_key_present?
  35098. false
  35099. end
  35100. # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
  35101. # the kind of the class of the associated objects. Meant to be used as
  35102. # a sanity check when you are about to assign an associated record.
  35103. def raise_on_type_mismatch(record)
  35104. unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
  35105. message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
  35106. raise ActiveRecord::AssociationTypeMismatch, message
  35107. end
  35108. end
  35109. # Can be redefined by subclasses, notably polymorphic belongs_to
  35110. # The record parameter is necessary to support polymorphic inverses as we must check for
  35111. # the association in the specific class of the record.
  35112. def inverse_reflection_for(record)
  35113. reflection.inverse_of
  35114. end
  35115. # Is this association invertible? Can be redefined by subclasses.
  35116. def invertible_for?(record)
  35117. inverse_reflection_for(record)
  35118. end
  35119. # This should be implemented to return the values of the relevant key(s) on the owner,
  35120. # so that when stale_state is different from the value stored on the last find_target,
  35121. # the target is stale.
  35122. #
  35123. # This is only relevant to certain associations, which is why it returns nil by default.
  35124. def stale_state
  35125. end
  35126. def build_record(attributes)
  35127. reflection.build_association(attributes) do |record|
  35128. skip_assign = [reflection.foreign_key, reflection.type].compact
  35129. attributes = create_scope.except(*(record.changed - skip_assign))
  35130. record.assign_attributes(attributes)
  35131. end
  35132. end
  35133. end
  35134. end
  35135. end
  35136. module ActiveRecord
  35137. module Associations
  35138. class AssociationScope #:nodoc:
  35139. include JoinHelper
  35140. attr_reader :association, :alias_tracker
  35141. delegate :klass, :owner, :reflection, :interpolate, :to => :association
  35142. delegate :chain, :scope_chain, :options, :source_options, :active_record, :to => :reflection
  35143. def initialize(association)
  35144. @association = association
  35145. @alias_tracker = AliasTracker.new klass.connection
  35146. end
  35147. def scope
  35148. scope = klass.unscoped
  35149. scope.extending! Array(options[:extend])
  35150. add_constraints(scope)
  35151. end
  35152. private
  35153. def column_for(table_name, column_name)
  35154. columns = alias_tracker.connection.schema_cache.columns_hash[table_name]
  35155. columns[column_name]
  35156. end
  35157. def bind_value(scope, column, value)
  35158. substitute = alias_tracker.connection.substitute_at(
  35159. column, scope.bind_values.length)
  35160. scope.bind_values += [[column, value]]
  35161. substitute
  35162. end
  35163. def bind(scope, table_name, column_name, value)
  35164. column = column_for table_name, column_name
  35165. bind_value scope, column, value
  35166. end
  35167. def add_constraints(scope)
  35168. tables = construct_tables
  35169. chain.each_with_index do |reflection, i|
  35170. table, foreign_table = tables.shift, tables.first
  35171. if reflection.source_macro == :has_and_belongs_to_many
  35172. join_table = tables.shift
  35173. scope = scope.joins(join(
  35174. join_table,
  35175. table[reflection.association_primary_key].
  35176. eq(join_table[reflection.association_foreign_key])
  35177. ))
  35178. table, foreign_table = join_table, tables.first
  35179. end
  35180. if reflection.source_macro == :belongs_to
  35181. if reflection.options[:polymorphic]
  35182. key = reflection.association_primary_key(self.klass)
  35183. else
  35184. key = reflection.association_primary_key
  35185. end
  35186. foreign_key = reflection.foreign_key
  35187. else
  35188. key = reflection.foreign_key
  35189. foreign_key = reflection.active_record_primary_key
  35190. end
  35191. if reflection == chain.last
  35192. bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key]
  35193. scope = scope.where(table[key].eq(bind_val))
  35194. if reflection.type
  35195. value = owner.class.base_class.name
  35196. bind_val = bind scope, table.table_name, reflection.type.to_s, value
  35197. scope = scope.where(table[reflection.type].eq(bind_val))
  35198. end
  35199. else
  35200. constraint = table[key].eq(foreign_table[foreign_key])
  35201. if reflection.type
  35202. type = chain[i + 1].klass.base_class.name
  35203. constraint = constraint.and(table[reflection.type].eq(type))
  35204. end
  35205. scope = scope.joins(join(foreign_table, constraint))
  35206. end
  35207. # Exclude the scope of the association itself, because that
  35208. # was already merged in the #scope method.
  35209. scope_chain[i].each do |scope_chain_item|
  35210. klass = i == 0 ? self.klass : reflection.klass
  35211. item = eval_scope(klass, scope_chain_item)
  35212. if scope_chain_item == self.reflection.scope
  35213. scope.merge! item.except(:where, :includes)
  35214. end
  35215. scope.includes! item.includes_values
  35216. scope.where_values += item.where_values
  35217. end
  35218. end
  35219. scope
  35220. end
  35221. def alias_suffix
  35222. reflection.name
  35223. end
  35224. def table_name_for(reflection)
  35225. if reflection == self.reflection
  35226. # If this is a polymorphic belongs_to, we want to get the klass from the
  35227. # association because it depends on the polymorphic_type attribute of
  35228. # the owner
  35229. klass.table_name
  35230. else
  35231. reflection.table_name
  35232. end
  35233. end
  35234. def eval_scope(klass, scope)
  35235. if scope.is_a?(Relation)
  35236. scope
  35237. else
  35238. klass.unscoped.instance_exec(owner, &scope)
  35239. end
  35240. end
  35241. end
  35242. end
  35243. end
  35244. module ActiveRecord
  35245. # = Active Record Belongs To Associations
  35246. module Associations
  35247. class BelongsToAssociation < SingularAssociation #:nodoc:
  35248. def handle_dependency
  35249. target.send(options[:dependent]) if load_target
  35250. end
  35251. def replace(record)
  35252. raise_on_type_mismatch(record) if record
  35253. update_counters(record)
  35254. replace_keys(record)
  35255. set_inverse_instance(record)
  35256. @updated = true if record
  35257. self.target = record
  35258. end
  35259. def reset
  35260. super
  35261. @updated = false
  35262. end
  35263. def updated?
  35264. @updated
  35265. end
  35266. private
  35267. def find_target?
  35268. !loaded? && foreign_key_present? && klass
  35269. end
  35270. def update_counters(record)
  35271. counter_cache_name = reflection.counter_cache_column
  35272. if counter_cache_name && owner.persisted? && different_target?(record)
  35273. if record
  35274. record.class.increment_counter(counter_cache_name, record.id)
  35275. end
  35276. if foreign_key_present?
  35277. klass.decrement_counter(counter_cache_name, target_id)
  35278. end
  35279. end
  35280. end
  35281. # Checks whether record is different to the current target, without loading it
  35282. def different_target?(record)
  35283. if record.nil?
  35284. owner[reflection.foreign_key]
  35285. else
  35286. record.id != owner[reflection.foreign_key]
  35287. end
  35288. end
  35289. def replace_keys(record)
  35290. if record
  35291. owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)]
  35292. else
  35293. owner[reflection.foreign_key] = nil
  35294. end
  35295. end
  35296. def foreign_key_present?
  35297. owner[reflection.foreign_key]
  35298. end
  35299. # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
  35300. # has_one associations.
  35301. def invertible_for?(record)
  35302. inverse = inverse_reflection_for(record)
  35303. inverse && inverse.macro == :has_one
  35304. end
  35305. def target_id
  35306. if options[:primary_key]
  35307. owner.send(reflection.name).try(:id)
  35308. else
  35309. owner[reflection.foreign_key]
  35310. end
  35311. end
  35312. def stale_state
  35313. owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s
  35314. end
  35315. end
  35316. end
  35317. end
  35318. module ActiveRecord
  35319. # = Active Record Belongs To Polymorphic Association
  35320. module Associations
  35321. class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
  35322. def klass
  35323. type = owner[reflection.foreign_type]
  35324. type.presence && type.constantize
  35325. end
  35326. private
  35327. def replace_keys(record)
  35328. super
  35329. owner[reflection.foreign_type] = record && record.class.base_class.name
  35330. end
  35331. def different_target?(record)
  35332. super || record.class != klass
  35333. end
  35334. def inverse_reflection_for(record)
  35335. reflection.polymorphic_inverse_of(record.class)
  35336. end
  35337. def raise_on_type_mismatch(record)
  35338. # A polymorphic association cannot have a type mismatch, by definition
  35339. end
  35340. def stale_state
  35341. foreign_key = super
  35342. foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
  35343. end
  35344. end
  35345. end
  35346. end
  35347. module ActiveRecord::Associations::Builder
  35348. class Association #:nodoc:
  35349. class << self
  35350. attr_accessor :valid_options
  35351. end
  35352. self.valid_options = [:class_name, :foreign_key, :validate]
  35353. attr_reader :model, :name, :scope, :options, :reflection
  35354. def self.build(*args, &block)
  35355. new(*args, &block).build
  35356. end
  35357. def initialize(model, name, scope, options)
  35358. raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
  35359. @model = model
  35360. @name = name
  35361. if scope.is_a?(Hash)
  35362. @scope = nil
  35363. @options = scope
  35364. else
  35365. @scope = scope
  35366. @options = options
  35367. end
  35368. if @scope && @scope.arity == 0
  35369. prev_scope = @scope
  35370. @scope = proc { instance_exec(&prev_scope) }
  35371. end
  35372. end
  35373. def mixin
  35374. @model.generated_feature_methods
  35375. end
  35376. include Module.new { def build; end }
  35377. def build
  35378. validate_options
  35379. define_accessors
  35380. configure_dependency if options[:dependent]
  35381. @reflection = model.create_reflection(macro, name, scope, options, model)
  35382. super # provides an extension point
  35383. @reflection
  35384. end
  35385. def macro
  35386. raise NotImplementedError
  35387. end
  35388. def valid_options
  35389. Association.valid_options
  35390. end
  35391. def validate_options
  35392. options.assert_valid_keys(valid_options)
  35393. end
  35394. def define_accessors
  35395. define_readers
  35396. define_writers
  35397. end
  35398. def define_readers
  35399. mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
  35400. def #{name}(*args)
  35401. association(:#{name}).reader(*args)
  35402. end
  35403. CODE
  35404. end
  35405. def define_writers
  35406. mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
  35407. def #{name}=(value)
  35408. association(:#{name}).writer(value)
  35409. end
  35410. CODE
  35411. end
  35412. def configure_dependency
  35413. unless valid_dependent_options.include? options[:dependent]
  35414. raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{options[:dependent]}"
  35415. end
  35416. if options[:dependent] == :restrict
  35417. ActiveSupport::Deprecation.warn(
  35418. "The :restrict option is deprecated. Please use :restrict_with_exception instead, which " \
  35419. "provides the same functionality."
  35420. )
  35421. end
  35422. mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
  35423. def #{macro}_dependent_for_#{name}
  35424. association(:#{name}).handle_dependency
  35425. end
  35426. CODE
  35427. model.before_destroy "#{macro}_dependent_for_#{name}"
  35428. end
  35429. def valid_dependent_options
  35430. raise NotImplementedError
  35431. end
  35432. end
  35433. end
  35434. module ActiveRecord::Associations::Builder
  35435. class BelongsTo < SingularAssociation #:nodoc:
  35436. def macro
  35437. :belongs_to
  35438. end
  35439. def valid_options
  35440. super + [:foreign_type, :polymorphic, :touch]
  35441. end
  35442. def constructable?
  35443. !options[:polymorphic]
  35444. end
  35445. def build
  35446. reflection = super
  35447. add_counter_cache_callbacks(reflection) if options[:counter_cache]
  35448. add_touch_callbacks(reflection) if options[:touch]
  35449. reflection
  35450. end
  35451. def add_counter_cache_callbacks(reflection)
  35452. cache_column = reflection.counter_cache_column
  35453. mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
  35454. def belongs_to_counter_cache_after_create_for_#{name}
  35455. record = #{name}
  35456. record.class.increment_counter(:#{cache_column}, record.id) unless record.nil?
  35457. end
  35458. def belongs_to_counter_cache_before_destroy_for_#{name}
  35459. unless marked_for_destruction?
  35460. record = #{name}
  35461. record.class.decrement_counter(:#{cache_column}, record.id) unless record.nil?
  35462. end
  35463. end
  35464. CODE
  35465. model.after_create "belongs_to_counter_cache_after_create_for_#{name}"
  35466. model.before_destroy "belongs_to_counter_cache_before_destroy_for_#{name}"
  35467. klass = reflection.class_name.safe_constantize
  35468. klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
  35469. end
  35470. def add_touch_callbacks(reflection)
  35471. mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
  35472. def belongs_to_touch_after_save_or_destroy_for_#{name}
  35473. record = #{name}
  35474. unless record.nil?
  35475. record.touch #{options[:touch].inspect if options[:touch] != true}
  35476. end
  35477. end
  35478. CODE
  35479. model.after_save "belongs_to_touch_after_save_or_destroy_for_#{name}"
  35480. model.after_touch "belongs_to_touch_after_save_or_destroy_for_#{name}"
  35481. model.after_destroy "belongs_to_touch_after_save_or_destroy_for_#{name}"
  35482. end
  35483. def valid_dependent_options
  35484. [:destroy, :delete]
  35485. end
  35486. end
  35487. end
  35488. require 'active_record/associations'
  35489. module ActiveRecord::Associations::Builder
  35490. class CollectionAssociation < Association #:nodoc:
  35491. CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
  35492. def valid_options
  35493. super + [:table_name, :finder_sql, :counter_sql, :before_add,
  35494. :after_add, :before_remove, :after_remove, :extend]
  35495. end
  35496. attr_reader :block_extension, :extension_module
  35497. def initialize(*args, &extension)
  35498. super(*args)
  35499. @block_extension = extension
  35500. end
  35501. def build
  35502. show_deprecation_warnings
  35503. wrap_block_extension
  35504. reflection = super
  35505. CALLBACKS.each { |callback_name| define_callback(callback_name) }
  35506. reflection
  35507. end
  35508. def writable?
  35509. true
  35510. end
  35511. def show_deprecation_warnings
  35512. [:finder_sql, :counter_sql].each do |name|
  35513. if options.include? name
  35514. ActiveSupport::Deprecation.warn("The :#{name} association option is deprecated. Please find an alternative (such as using scopes).")
  35515. end
  35516. end
  35517. end
  35518. def wrap_block_extension
  35519. if block_extension
  35520. @extension_module = mod = Module.new(&block_extension)
  35521. silence_warnings do
  35522. model.parent.const_set(extension_module_name, mod)
  35523. end
  35524. prev_scope = @scope
  35525. if prev_scope
  35526. @scope = proc { |owner| instance_exec(owner, &prev_scope).extending(mod) }
  35527. else
  35528. @scope = proc { extending(mod) }
  35529. end
  35530. end
  35531. end
  35532. def extension_module_name
  35533. @extension_module_name ||= "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
  35534. end
  35535. def define_callback(callback_name)
  35536. full_callback_name = "#{callback_name}_for_#{name}"
  35537. # TODO : why do i need method_defined? I think its because of the inheritance chain
  35538. model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
  35539. model.send("#{full_callback_name}=", Array(options[callback_name.to_sym]))
  35540. end
  35541. def define_readers
  35542. super
  35543. mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
  35544. def #{name.to_s.singularize}_ids
  35545. association(:#{name}).ids_reader
  35546. end
  35547. CODE
  35548. end
  35549. def define_writers
  35550. super
  35551. mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
  35552. def #{name.to_s.singularize}_ids=(ids)
  35553. association(:#{name}).ids_writer(ids)
  35554. end
  35555. CODE
  35556. end
  35557. end
  35558. end
  35559. module ActiveRecord::Associations::Builder
  35560. class HasAndBelongsToMany < CollectionAssociation #:nodoc:
  35561. def macro
  35562. :has_and_belongs_to_many
  35563. end
  35564. def valid_options
  35565. super + [:join_table, :association_foreign_key, :delete_sql, :insert_sql]
  35566. end
  35567. def build
  35568. reflection = super
  35569. define_destroy_hook
  35570. reflection
  35571. end
  35572. def show_deprecation_warnings
  35573. super
  35574. [:delete_sql, :insert_sql].each do |name|
  35575. if options.include? name
  35576. ActiveSupport::Deprecation.warn("The :#{name} association option is deprecated. Please find an alternative (such as using has_many :through).")
  35577. end
  35578. end
  35579. end
  35580. def define_destroy_hook
  35581. name = self.name
  35582. model.send(:include, Module.new {
  35583. class_eval <<-RUBY, __FILE__, __LINE__ + 1
  35584. def destroy_associations
  35585. association(:#{name}).delete_all
  35586. super
  35587. end
  35588. RUBY
  35589. })
  35590. end
  35591. end
  35592. end
  35593. module ActiveRecord::Associations::Builder
  35594. class HasMany < CollectionAssociation #:nodoc:
  35595. def macro
  35596. :has_many
  35597. end
  35598. def valid_options
  35599. super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache]
  35600. end
  35601. def valid_dependent_options
  35602. [:destroy, :delete_all, :nullify, :restrict, :restrict_with_error, :restrict_with_exception]
  35603. end
  35604. end
  35605. end
  35606. module ActiveRecord::Associations::Builder
  35607. class HasOne < SingularAssociation #:nodoc:
  35608. def macro
  35609. :has_one
  35610. end
  35611. def valid_options
  35612. valid = super + [:order, :as]
  35613. valid += [:through, :source, :source_type] if options[:through]
  35614. valid
  35615. end
  35616. def constructable?
  35617. !options[:through]
  35618. end
  35619. def configure_dependency
  35620. super unless options[:through]
  35621. end
  35622. def valid_dependent_options
  35623. [:destroy, :delete, :nullify, :restrict, :restrict_with_error, :restrict_with_exception]
  35624. end
  35625. end
  35626. end
  35627. module ActiveRecord::Associations::Builder
  35628. class SingularAssociation < Association #:nodoc:
  35629. def valid_options
  35630. super + [:remote, :dependent, :counter_cache, :primary_key, :inverse_of]
  35631. end
  35632. def constructable?
  35633. true
  35634. end
  35635. def define_accessors
  35636. super
  35637. define_constructors if constructable?
  35638. end
  35639. def define_constructors
  35640. mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
  35641. def build_#{name}(*args, &block)
  35642. association(:#{name}).build(*args, &block)
  35643. end
  35644. def create_#{name}(*args, &block)
  35645. association(:#{name}).create(*args, &block)
  35646. end
  35647. def create_#{name}!(*args, &block)
  35648. association(:#{name}).create!(*args, &block)
  35649. end
  35650. CODE
  35651. end
  35652. end
  35653. end
  35654. module ActiveRecord
  35655. module Associations
  35656. # = Active Record Association Collection
  35657. #
  35658. # CollectionAssociation is an abstract class that provides common stuff to
  35659. # ease the implementation of association proxies that represent
  35660. # collections. See the class hierarchy in AssociationProxy.
  35661. #
  35662. # CollectionAssociation:
  35663. # HasAndBelongsToManyAssociation => has_and_belongs_to_many
  35664. # HasManyAssociation => has_many
  35665. # HasManyThroughAssociation + ThroughAssociation => has_many :through
  35666. #
  35667. # CollectionAssociation class provides common methods to the collections
  35668. # defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
  35669. # +:through association+ option.
  35670. #
  35671. # You need to be careful with assumptions regarding the target: The proxy
  35672. # does not fetch records from the database until it needs them, but new
  35673. # ones created with +build+ are added to the target. So, the target may be
  35674. # non-empty and still lack children waiting to be read from the database.
  35675. # If you look directly to the database you cannot assume that's the entire
  35676. # collection because new records may have been added to the target, etc.
  35677. #
  35678. # If you need to work on all current children, new and existing records,
  35679. # +load_target+ and the +loaded+ flag are your friends.
  35680. class CollectionAssociation < Association #:nodoc:
  35681. # Implements the reader method, e.g. foo.items for Foo.has_many :items
  35682. def reader(force_reload = false)
  35683. if force_reload
  35684. klass.uncached { reload }
  35685. elsif stale_target?
  35686. reload
  35687. end
  35688. CollectionProxy.new(klass, self)
  35689. end
  35690. # Implements the writer method, e.g. foo.items= for Foo.has_many :items
  35691. def writer(records)
  35692. replace(records)
  35693. end
  35694. # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
  35695. def ids_reader
  35696. if loaded? || options[:finder_sql]
  35697. load_target.map do |record|
  35698. record.send(reflection.association_primary_key)
  35699. end
  35700. else
  35701. column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
  35702. scope.pluck(column)
  35703. end
  35704. end
  35705. # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
  35706. def ids_writer(ids)
  35707. pk_column = reflection.primary_key_column
  35708. ids = Array(ids).reject { |id| id.blank? }
  35709. ids.map! { |i| pk_column.type_cast(i) }
  35710. replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
  35711. end
  35712. def reset
  35713. super
  35714. @target = []
  35715. end
  35716. def select(select = nil)
  35717. if block_given?
  35718. load_target.select.each { |e| yield e }
  35719. else
  35720. scope.select(select)
  35721. end
  35722. end
  35723. def find(*args)
  35724. if block_given?
  35725. load_target.find(*args) { |*block_args| yield(*block_args) }
  35726. else
  35727. if options[:finder_sql]
  35728. find_by_scan(*args)
  35729. else
  35730. scope.find(*args)
  35731. end
  35732. end
  35733. end
  35734. def first(*args)
  35735. first_or_last(:first, *args)
  35736. end
  35737. def last(*args)
  35738. first_or_last(:last, *args)
  35739. end
  35740. def build(attributes = {}, &block)
  35741. if attributes.is_a?(Array)
  35742. attributes.collect { |attr| build(attr, &block) }
  35743. else
  35744. add_to_target(build_record(attributes)) do |record|
  35745. yield(record) if block_given?
  35746. end
  35747. end
  35748. end
  35749. def create(attributes = {}, &block)
  35750. create_record(attributes, &block)
  35751. end
  35752. def create!(attributes = {}, &block)
  35753. create_record(attributes, true, &block)
  35754. end
  35755. # Add +records+ to this association. Returns +self+ so method calls may
  35756. # be chained. Since << flattens its argument list and inserts each record,
  35757. # +push+ and +concat+ behave identically.
  35758. def concat(*records)
  35759. load_target if owner.new_record?
  35760. if owner.new_record?
  35761. concat_records(records)
  35762. else
  35763. transaction { concat_records(records) }
  35764. end
  35765. end
  35766. # Starts a transaction in the association class's database connection.
  35767. #
  35768. # class Author < ActiveRecord::Base
  35769. # has_many :books
  35770. # end
  35771. #
  35772. # Author.first.books.transaction do
  35773. # # same effect as calling Book.transaction
  35774. # end
  35775. def transaction(*args)
  35776. reflection.klass.transaction(*args) do
  35777. yield
  35778. end
  35779. end
  35780. # Remove all records from this association.
  35781. #
  35782. # See delete for more info.
  35783. def delete_all
  35784. delete(:all).tap do
  35785. reset
  35786. loaded!
  35787. end
  35788. end
  35789. # Destroy all the records from this association.
  35790. #
  35791. # See destroy for more info.
  35792. def destroy_all
  35793. destroy(load_target).tap do
  35794. reset
  35795. loaded!
  35796. end
  35797. end
  35798. # Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
  35799. # association, it will be used for the query. Otherwise, construct options and pass them with
  35800. # scope to the target class's +count+.
  35801. def count(column_name = nil, count_options = {})
  35802. column_name, count_options = nil, column_name if column_name.is_a?(Hash)
  35803. if options[:counter_sql] || options[:finder_sql]
  35804. unless count_options.blank?
  35805. raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
  35806. end
  35807. reflection.klass.count_by_sql(custom_counter_sql)
  35808. else
  35809. if association_scope.uniq_value
  35810. # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
  35811. column_name ||= reflection.klass.primary_key
  35812. count_options[:distinct] = true
  35813. end
  35814. value = scope.count(column_name, count_options)
  35815. limit = options[:limit]
  35816. offset = options[:offset]
  35817. if limit || offset
  35818. [ [value - offset.to_i, 0].max, limit.to_i ].min
  35819. else
  35820. value
  35821. end
  35822. end
  35823. end
  35824. # Removes +records+ from this association calling +before_remove+ and
  35825. # +after_remove+ callbacks.
  35826. #
  35827. # This method is abstract in the sense that +delete_records+ has to be
  35828. # provided by descendants. Note this method does not imply the records
  35829. # are actually removed from the database, that depends precisely on
  35830. # +delete_records+. They are in any case removed from the collection.
  35831. def delete(*records)
  35832. dependent = options[:dependent]
  35833. if records.first == :all
  35834. if loaded? || dependent == :destroy
  35835. delete_or_destroy(load_target, dependent)
  35836. else
  35837. delete_records(:all, dependent)
  35838. end
  35839. else
  35840. records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
  35841. delete_or_destroy(records, dependent)
  35842. end
  35843. end
  35844. # Destroy +records+ and remove them from this association calling
  35845. # +before_remove+ and +after_remove+ callbacks.
  35846. #
  35847. # Note that this method will _always_ remove records from the database
  35848. # ignoring the +:dependent+ option.
  35849. def destroy(*records)
  35850. records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
  35851. delete_or_destroy(records, :destroy)
  35852. end
  35853. # Returns the size of the collection by executing a SELECT COUNT(*)
  35854. # query if the collection hasn't been loaded, and calling
  35855. # <tt>collection.size</tt> if it has.
  35856. #
  35857. # If the collection has been already loaded +size+ and +length+ are
  35858. # equivalent. If not and you are going to need the records anyway
  35859. # +length+ will take one less query. Otherwise +size+ is more efficient.
  35860. #
  35861. # This method is abstract in the sense that it relies on
  35862. # +count_records+, which is a method descendants have to provide.
  35863. def size
  35864. if !find_target? || loaded?
  35865. if association_scope.uniq_value
  35866. target.uniq.size
  35867. else
  35868. target.size
  35869. end
  35870. elsif !loaded? && !association_scope.group_values.empty?
  35871. load_target.size
  35872. elsif !loaded? && !association_scope.uniq_value && target.is_a?(Array)
  35873. unsaved_records = target.select { |r| r.new_record? }
  35874. unsaved_records.size + count_records
  35875. else
  35876. count_records
  35877. end
  35878. end
  35879. # Returns the size of the collection calling +size+ on the target.
  35880. #
  35881. # If the collection has been already loaded +length+ and +size+ are
  35882. # equivalent. If not and you are going to need the records anyway this
  35883. # method will take one less query. Otherwise +size+ is more efficient.
  35884. def length
  35885. load_target.size
  35886. end
  35887. # Returns true if the collection is empty.
  35888. #
  35889. # If the collection has been loaded or the <tt>:counter_sql</tt> option
  35890. # is provided, it is equivalent to <tt>collection.size.zero?</tt>. If the
  35891. # collection has not been loaded, it is equivalent to
  35892. # <tt>collection.exists?</tt>. If the collection has not already been
  35893. # loaded and you are going to fetch the records anyway it is better to
  35894. # check <tt>collection.length.zero?</tt>.
  35895. def empty?
  35896. if loaded? || options[:counter_sql]
  35897. size.zero?
  35898. else
  35899. @target.blank? && !scope.exists?
  35900. end
  35901. end
  35902. # Returns true if the collections is not empty.
  35903. # Equivalent to +!collection.empty?+.
  35904. def any?
  35905. if block_given?
  35906. load_target.any? { |*block_args| yield(*block_args) }
  35907. else
  35908. !empty?
  35909. end
  35910. end
  35911. # Returns true if the collection has more than 1 record.
  35912. # Equivalent to +collection.size > 1+.
  35913. def many?
  35914. if block_given?
  35915. load_target.many? { |*block_args| yield(*block_args) }
  35916. else
  35917. size > 1
  35918. end
  35919. end
  35920. def uniq
  35921. seen = {}
  35922. load_target.find_all do |record|
  35923. seen[record.id] = true unless seen.key?(record.id)
  35924. end
  35925. end
  35926. # Replace this collection with +other_array+. This will perform a diff
  35927. # and delete/add only records that have changed.
  35928. def replace(other_array)
  35929. other_array.each { |val| raise_on_type_mismatch(val) }
  35930. original_target = load_target.dup
  35931. if owner.new_record?
  35932. replace_records(other_array, original_target)
  35933. else
  35934. transaction { replace_records(other_array, original_target) }
  35935. end
  35936. end
  35937. def include?(record)
  35938. if record.is_a?(reflection.klass)
  35939. if record.new_record?
  35940. include_in_memory?(record)
  35941. else
  35942. load_target if options[:finder_sql]
  35943. loaded? ? target.include?(record) : scope.exists?(record)
  35944. end
  35945. else
  35946. false
  35947. end
  35948. end
  35949. def load_target
  35950. if find_target?
  35951. @target = merge_target_lists(find_target, target)
  35952. end
  35953. loaded!
  35954. target
  35955. end
  35956. def add_to_target(record)
  35957. callback(:before_add, record)
  35958. yield(record) if block_given?
  35959. if association_scope.uniq_value && index = @target.index(record)
  35960. @target[index] = record
  35961. else
  35962. @target << record
  35963. end
  35964. callback(:after_add, record)
  35965. set_inverse_instance(record)
  35966. record
  35967. end
  35968. def scope(opts = {})
  35969. scope = super()
  35970. scope.none! if opts.fetch(:nullify, true) && null_scope?
  35971. scope
  35972. end
  35973. def null_scope?
  35974. owner.new_record? && !foreign_key_present?
  35975. end
  35976. private
  35977. def custom_counter_sql
  35978. if options[:counter_sql]
  35979. interpolate(options[:counter_sql])
  35980. else
  35981. # replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
  35982. interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
  35983. count_with = $2.to_s
  35984. count_with = '*' if count_with.blank? || count_with =~ /,/ || count_with =~ /\.\*/
  35985. "SELECT #{$1}COUNT(#{count_with}) FROM"
  35986. end
  35987. end
  35988. end
  35989. def custom_finder_sql
  35990. interpolate(options[:finder_sql])
  35991. end
  35992. def find_target
  35993. records =
  35994. if options[:finder_sql]
  35995. reflection.klass.find_by_sql(custom_finder_sql)
  35996. else
  35997. scope.to_a
  35998. end
  35999. records.each { |record| set_inverse_instance(record) }
  36000. records
  36001. end
  36002. # We have some records loaded from the database (persisted) and some that are
  36003. # in-memory (memory). The same record may be represented in the persisted array
  36004. # and in the memory array.
  36005. #
  36006. # So the task of this method is to merge them according to the following rules:
  36007. #
  36008. # * The final array must not have duplicates
  36009. # * The order of the persisted array is to be preserved
  36010. # * Any changes made to attributes on objects in the memory array are to be preserved
  36011. # * Otherwise, attributes should have the value found in the database
  36012. def merge_target_lists(persisted, memory)
  36013. return persisted if memory.empty?
  36014. return memory if persisted.empty?
  36015. persisted.map! do |record|
  36016. if mem_record = memory.delete(record)
  36017. ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
  36018. mem_record[name] = record[name]
  36019. end
  36020. mem_record
  36021. else
  36022. record
  36023. end
  36024. end
  36025. persisted + memory
  36026. end
  36027. def create_record(attributes, raise = false, &block)
  36028. unless owner.persisted?
  36029. raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
  36030. end
  36031. if attributes.is_a?(Array)
  36032. attributes.collect { |attr| create_record(attr, raise, &block) }
  36033. else
  36034. transaction do
  36035. add_to_target(build_record(attributes)) do |record|
  36036. yield(record) if block_given?
  36037. insert_record(record, true, raise)
  36038. end
  36039. end
  36040. end
  36041. end
  36042. # Do the relevant stuff to insert the given record into the association collection.
  36043. def insert_record(record, validate = true, raise = false)
  36044. raise NotImplementedError
  36045. end
  36046. def create_scope
  36047. scope.scope_for_create.stringify_keys
  36048. end
  36049. def delete_or_destroy(records, method)
  36050. records = records.flatten
  36051. records.each { |record| raise_on_type_mismatch(record) }
  36052. existing_records = records.reject { |r| r.new_record? }
  36053. if existing_records.empty?
  36054. remove_records(existing_records, records, method)
  36055. else
  36056. transaction { remove_records(existing_records, records, method) }
  36057. end
  36058. end
  36059. def remove_records(existing_records, records, method)
  36060. records.each { |record| callback(:before_remove, record) }
  36061. delete_records(existing_records, method) if existing_records.any?
  36062. records.each { |record| target.delete(record) }
  36063. records.each { |record| callback(:after_remove, record) }
  36064. end
  36065. # Delete the given records from the association, using one of the methods :destroy,
  36066. # :delete_all or :nullify (or nil, in which case a default is used).
  36067. def delete_records(records, method)
  36068. raise NotImplementedError
  36069. end
  36070. def replace_records(new_target, original_target)
  36071. delete(target - new_target)
  36072. unless concat(new_target - target)
  36073. @target = original_target
  36074. raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
  36075. "new records could not be saved."
  36076. end
  36077. target
  36078. end
  36079. def concat_records(records)
  36080. result = true
  36081. records.flatten.each do |record|
  36082. raise_on_type_mismatch(record)
  36083. add_to_target(record) do |r|
  36084. result &&= insert_record(record) unless owner.new_record?
  36085. end
  36086. end
  36087. result && records
  36088. end
  36089. def callback(method, record)
  36090. callbacks_for(method).each do |callback|
  36091. case callback
  36092. when Symbol
  36093. owner.send(callback, record)
  36094. when Proc
  36095. callback.call(owner, record)
  36096. else
  36097. callback.send(method, owner, record)
  36098. end
  36099. end
  36100. end
  36101. def callbacks_for(callback_name)
  36102. full_callback_name = "#{callback_name}_for_#{reflection.name}"
  36103. owner.class.send(full_callback_name.to_sym) || []
  36104. end
  36105. # Should we deal with assoc.first or assoc.last by issuing an independent query to
  36106. # the database, or by getting the target, and then taking the first/last item from that?
  36107. #
  36108. # If the args is just a non-empty options hash, go to the database.
  36109. #
  36110. # Otherwise, go to the database only if none of the following are true:
  36111. # * target already loaded
  36112. # * owner is new record
  36113. # * custom :finder_sql exists
  36114. # * target contains new or changed record(s)
  36115. # * the first arg is an integer (which indicates the number of records to be returned)
  36116. def fetch_first_or_last_using_find?(args)
  36117. if args.first.is_a?(Hash)
  36118. true
  36119. else
  36120. !(loaded? ||
  36121. owner.new_record? ||
  36122. options[:finder_sql] ||
  36123. target.any? { |record| record.new_record? || record.changed? } ||
  36124. args.first.kind_of?(Integer))
  36125. end
  36126. end
  36127. def include_in_memory?(record)
  36128. if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
  36129. owner.send(reflection.through_reflection.name).any? { |source|
  36130. target = source.send(reflection.source_reflection.name)
  36131. target.respond_to?(:include?) ? target.include?(record) : target == record
  36132. } || target.include?(record)
  36133. else
  36134. target.include?(record)
  36135. end
  36136. end
  36137. # If using a custom finder_sql, #find scans the entire collection.
  36138. def find_by_scan(*args)
  36139. expects_array = args.first.kind_of?(Array)
  36140. ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq
  36141. if ids.size == 1
  36142. id = ids.first
  36143. record = load_target.detect { |r| id == r.id }
  36144. expects_array ? [ record ] : record
  36145. else
  36146. load_target.select { |r| ids.include?(r.id) }
  36147. end
  36148. end
  36149. # Fetches the first/last using SQL if possible, otherwise from the target array.
  36150. def first_or_last(type, *args)
  36151. args.shift if args.first.is_a?(Hash) && args.first.empty?
  36152. collection = fetch_first_or_last_using_find?(args) ? scope : load_target
  36153. collection.send(type, *args).tap do |record|
  36154. set_inverse_instance record if record.is_a? ActiveRecord::Base
  36155. end
  36156. end
  36157. end
  36158. end
  36159. end
  36160. module ActiveRecord
  36161. module Associations
  36162. # Association proxies in Active Record are middlemen between the object that
  36163. # holds the association, known as the <tt>@owner</tt>, and the actual associated
  36164. # object, known as the <tt>@target</tt>. The kind of association any proxy is
  36165. # about is available in <tt>@reflection</tt>. That's an instance of the class
  36166. # ActiveRecord::Reflection::AssociationReflection.
  36167. #
  36168. # For example, given
  36169. #
  36170. # class Blog < ActiveRecord::Base
  36171. # has_many :posts
  36172. # end
  36173. #
  36174. # blog = Blog.first
  36175. #
  36176. # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
  36177. # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
  36178. # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
  36179. #
  36180. # This class delegates unknown methods to <tt>@target</tt> via
  36181. # <tt>method_missing</tt>.
  36182. #
  36183. # The <tt>@target</tt> object is not \loaded until needed. For example,
  36184. #
  36185. # blog.posts.count
  36186. #
  36187. # is computed directly through SQL and does not trigger by itself the
  36188. # instantiation of the actual post records.
  36189. class CollectionProxy < Relation
  36190. delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
  36191. def initialize(klass, association) #:nodoc:
  36192. @association = association
  36193. super klass, klass.arel_table
  36194. self.default_scoped = true
  36195. merge! association.scope(nullify: false)
  36196. end
  36197. def target
  36198. @association.target
  36199. end
  36200. def load_target
  36201. @association.load_target
  36202. end
  36203. # Returns +true+ if the association has been loaded, otherwise +false+.
  36204. #
  36205. # person.pets.loaded? # => false
  36206. # person.pets
  36207. # person.pets.loaded? # => true
  36208. def loaded?
  36209. @association.loaded?
  36210. end
  36211. # Works in two ways.
  36212. #
  36213. # *First:* Specify a subset of fields to be selected from the result set.
  36214. #
  36215. # class Person < ActiveRecord::Base
  36216. # has_many :pets
  36217. # end
  36218. #
  36219. # person.pets
  36220. # # => [
  36221. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36222. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36223. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36224. # # ]
  36225. #
  36226. # person.pets.select(:name)
  36227. # # => [
  36228. # # #<Pet id: nil, name: "Fancy-Fancy">,
  36229. # # #<Pet id: nil, name: "Spook">,
  36230. # # #<Pet id: nil, name: "Choo-Choo">
  36231. # # ]
  36232. #
  36233. # person.pets.select([:id, :name])
  36234. # # => [
  36235. # # #<Pet id: 1, name: "Fancy-Fancy">,
  36236. # # #<Pet id: 2, name: "Spook">,
  36237. # # #<Pet id: 3, name: "Choo-Choo">
  36238. # # ]
  36239. #
  36240. # Be careful because this also means youre initializing a model
  36241. # object with only the fields that youve selected. If you attempt
  36242. # to access a field that is not in the initialized record youll
  36243. # receive:
  36244. #
  36245. # person.pets.select(:name).first.person_id
  36246. # # => ActiveModel::MissingAttributeError: missing attribute: person_id
  36247. #
  36248. # *Second:* You can pass a block so it can be used just like Array#select.
  36249. # This build an array of objects from the database for the scope,
  36250. # converting them into an array and iterating through them using
  36251. # Array#select.
  36252. #
  36253. # person.pets.select { |pet| pet.name =~ /oo/ }
  36254. # # => [
  36255. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36256. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36257. # # ]
  36258. #
  36259. # person.pets.select(:name) { |pet| pet.name =~ /oo/ }
  36260. # # => [
  36261. # # #<Pet id: 2, name: "Spook">,
  36262. # # #<Pet id: 3, name: "Choo-Choo">
  36263. # # ]
  36264. def select(select = nil, &block)
  36265. @association.select(select, &block)
  36266. end
  36267. # Finds an object in the collection responding to the +id+. Uses the same
  36268. # rules as <tt>ActiveRecord::Base.find</tt>. Returns <tt>ActiveRecord::RecordNotFound</tt>
  36269. # error if the object can not be found.
  36270. #
  36271. # class Person < ActiveRecord::Base
  36272. # has_many :pets
  36273. # end
  36274. #
  36275. # person.pets
  36276. # # => [
  36277. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36278. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36279. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36280. # # ]
  36281. #
  36282. # person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
  36283. # person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4
  36284. #
  36285. # person.pets.find(2) { |pet| pet.name.downcase! }
  36286. # # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
  36287. #
  36288. # person.pets.find(2, 3)
  36289. # # => [
  36290. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36291. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36292. # # ]
  36293. def find(*args, &block)
  36294. @association.find(*args, &block)
  36295. end
  36296. # Returns the first record, or the first +n+ records, from the collection.
  36297. # If the collection is empty, the first form returns +nil+, and the second
  36298. # form returns an empty array.
  36299. #
  36300. # class Person < ActiveRecord::Base
  36301. # has_many :pets
  36302. # end
  36303. #
  36304. # person.pets
  36305. # # => [
  36306. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36307. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36308. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36309. # # ]
  36310. #
  36311. # person.pets.first # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
  36312. #
  36313. # person.pets.first(2)
  36314. # # => [
  36315. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36316. # # #<Pet id: 2, name: "Spook", person_id: 1>
  36317. # # ]
  36318. #
  36319. # another_person_without.pets # => []
  36320. # another_person_without.pets.first # => nil
  36321. # another_person_without.pets.first(3) # => []
  36322. def first(*args)
  36323. @association.first(*args)
  36324. end
  36325. # Returns the last record, or the last +n+ records, from the collection.
  36326. # If the collection is empty, the first form returns +nil+, and the second
  36327. # form returns an empty array.
  36328. #
  36329. # class Person < ActiveRecord::Base
  36330. # has_many :pets
  36331. # end
  36332. #
  36333. # person.pets
  36334. # # => [
  36335. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36336. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36337. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36338. # # ]
  36339. #
  36340. # person.pets.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36341. #
  36342. # person.pets.last(2)
  36343. # # => [
  36344. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36345. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36346. # # ]
  36347. #
  36348. # another_person_without.pets # => []
  36349. # another_person_without.pets.last # => nil
  36350. # another_person_without.pets.last(3) # => []
  36351. def last(*args)
  36352. @association.last(*args)
  36353. end
  36354. # Returns a new object of the collection type that has been instantiated
  36355. # with +attributes+ and linked to this object, but have not yet been saved.
  36356. # You can pass an array of attributes hashes, this will return an array
  36357. # with the new objects.
  36358. #
  36359. # class Person
  36360. # has_many :pets
  36361. # end
  36362. #
  36363. # person.pets.build
  36364. # # => #<Pet id: nil, name: nil, person_id: 1>
  36365. #
  36366. # person.pets.build(name: 'Fancy-Fancy')
  36367. # # => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
  36368. #
  36369. # person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
  36370. # # => [
  36371. # # #<Pet id: nil, name: "Spook", person_id: 1>,
  36372. # # #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
  36373. # # #<Pet id: nil, name: "Brain", person_id: 1>
  36374. # # ]
  36375. #
  36376. # person.pets.size # => 5 # size of the collection
  36377. # person.pets.count # => 0 # count from database
  36378. def build(attributes = {}, &block)
  36379. @association.build(attributes, &block)
  36380. end
  36381. # Returns a new object of the collection type that has been instantiated with
  36382. # attributes, linked to this object and that has already been saved (if it
  36383. # passes the validations).
  36384. #
  36385. # class Person
  36386. # has_many :pets
  36387. # end
  36388. #
  36389. # person.pets.create(name: 'Fancy-Fancy')
  36390. # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
  36391. #
  36392. # person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}])
  36393. # # => [
  36394. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36395. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36396. # # ]
  36397. #
  36398. # person.pets.size # => 3
  36399. # person.pets.count # => 3
  36400. #
  36401. # person.pets.find(1, 2, 3)
  36402. # # => [
  36403. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36404. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36405. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36406. # # ]
  36407. def create(attributes = {}, &block)
  36408. @association.create(attributes, &block)
  36409. end
  36410. # Like +create+, except that if the record is invalid, raises an exception.
  36411. #
  36412. # class Person
  36413. # has_many :pets
  36414. # end
  36415. #
  36416. # class Pet
  36417. # validates :name, presence: true
  36418. # end
  36419. #
  36420. # person.pets.create!(name: nil)
  36421. # # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
  36422. def create!(attributes = {}, &block)
  36423. @association.create!(attributes, &block)
  36424. end
  36425. # Add one or more records to the collection by setting their foreign keys
  36426. # to the association's primary key. Since << flattens its argument list and
  36427. # inserts each record, +push+ and +concat+ behave identically. Returns +self+
  36428. # so method calls may be chained.
  36429. #
  36430. # class Person < ActiveRecord::Base
  36431. # pets :has_many
  36432. # end
  36433. #
  36434. # person.pets.size # => 0
  36435. # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
  36436. # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
  36437. # person.pets.size # => 3
  36438. #
  36439. # person.id # => 1
  36440. # person.pets
  36441. # # => [
  36442. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36443. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36444. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36445. # # ]
  36446. #
  36447. # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
  36448. # person.pets.size # => 5
  36449. def concat(*records)
  36450. @association.concat(*records)
  36451. end
  36452. # Replace this collection with +other_array+. This will perform a diff
  36453. # and delete/add only records that have changed.
  36454. #
  36455. # class Person < ActiveRecord::Base
  36456. # has_many :pets
  36457. # end
  36458. #
  36459. # person.pets
  36460. # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
  36461. #
  36462. # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
  36463. #
  36464. # person.pets.replace(other_pets)
  36465. #
  36466. # person.pets
  36467. # # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
  36468. #
  36469. # If the supplied array has an incorrect association type, it raises
  36470. # an <tt>ActiveRecord::AssociationTypeMismatch</tt> error:
  36471. #
  36472. # person.pets.replace(["doo", "ggie", "gaga"])
  36473. # # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
  36474. def replace(other_array)
  36475. @association.replace(other_array)
  36476. end
  36477. # Deletes all the records from the collection. For +has_many+ associations,
  36478. # the deletion is done according to the strategy specified by the <tt>:dependent</tt>
  36479. # option. Returns an array with the deleted records.
  36480. #
  36481. # If no <tt>:dependent</tt> option is given, then it will follow the
  36482. # default strategy. The default strategy is <tt>:nullify</tt>. This
  36483. # sets the foreign keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>,
  36484. # the default strategy is +delete_all+.
  36485. #
  36486. # class Person < ActiveRecord::Base
  36487. # has_many :pets # dependent: :nullify option by default
  36488. # end
  36489. #
  36490. # person.pets.size # => 3
  36491. # person.pets
  36492. # # => [
  36493. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36494. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36495. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36496. # # ]
  36497. #
  36498. # person.pets.delete_all
  36499. # # => [
  36500. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36501. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36502. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36503. # # ]
  36504. #
  36505. # person.pets.size # => 0
  36506. # person.pets # => []
  36507. #
  36508. # Pet.find(1, 2, 3)
  36509. # # => [
  36510. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>,
  36511. # # #<Pet id: 2, name: "Spook", person_id: nil>,
  36512. # # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
  36513. # # ]
  36514. #
  36515. # If it is set to <tt>:destroy</tt> all the objects from the collection
  36516. # are removed by calling their +destroy+ method. See +destroy+ for more
  36517. # information.
  36518. #
  36519. # class Person < ActiveRecord::Base
  36520. # has_many :pets, dependent: :destroy
  36521. # end
  36522. #
  36523. # person.pets.size # => 3
  36524. # person.pets
  36525. # # => [
  36526. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36527. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36528. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36529. # # ]
  36530. #
  36531. # person.pets.delete_all
  36532. # # => [
  36533. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36534. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36535. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36536. # # ]
  36537. #
  36538. # Pet.find(1, 2, 3)
  36539. # # => ActiveRecord::RecordNotFound
  36540. #
  36541. # If it is set to <tt>:delete_all</tt>, all the objects are deleted
  36542. # *without* calling their +destroy+ method.
  36543. #
  36544. # class Person < ActiveRecord::Base
  36545. # has_many :pets, dependent: :delete_all
  36546. # end
  36547. #
  36548. # person.pets.size # => 3
  36549. # person.pets
  36550. # # => [
  36551. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36552. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36553. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36554. # # ]
  36555. #
  36556. # person.pets.delete_all
  36557. # # => [
  36558. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36559. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36560. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36561. # # ]
  36562. #
  36563. # Pet.find(1, 2, 3)
  36564. # # => ActiveRecord::RecordNotFound
  36565. def delete_all
  36566. @association.delete_all
  36567. end
  36568. # Deletes the records of the collection directly from the database.
  36569. # This will _always_ remove the records ignoring the +:dependent+
  36570. # option.
  36571. #
  36572. # class Person < ActiveRecord::Base
  36573. # has_many :pets
  36574. # end
  36575. #
  36576. # person.pets.size # => 3
  36577. # person.pets
  36578. # # => [
  36579. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36580. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36581. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36582. # # ]
  36583. #
  36584. # person.pets.destroy_all
  36585. #
  36586. # person.pets.size # => 0
  36587. # person.pets # => []
  36588. #
  36589. # Pet.find(1) # => Couldn't find Pet with id=1
  36590. def destroy_all
  36591. @association.destroy_all
  36592. end
  36593. # Deletes the +records+ supplied and removes them from the collection. For
  36594. # +has_many+ associations, the deletion is done according to the strategy
  36595. # specified by the <tt>:dependent</tt> option. Returns an array with the
  36596. # deleted records.
  36597. #
  36598. # If no <tt>:dependent</tt> option is given, then it will follow the default
  36599. # strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign
  36600. # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
  36601. # strategy is +delete_all+.
  36602. #
  36603. # class Person < ActiveRecord::Base
  36604. # has_many :pets # dependent: :nullify option by default
  36605. # end
  36606. #
  36607. # person.pets.size # => 3
  36608. # person.pets
  36609. # # => [
  36610. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36611. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36612. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36613. # # ]
  36614. #
  36615. # person.pets.delete(Pet.find(1))
  36616. # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
  36617. #
  36618. # person.pets.size # => 2
  36619. # person.pets
  36620. # # => [
  36621. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36622. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36623. # # ]
  36624. #
  36625. # Pet.find(1)
  36626. # # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
  36627. #
  36628. # If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
  36629. # their +destroy+ method. See +destroy+ for more information.
  36630. #
  36631. # class Person < ActiveRecord::Base
  36632. # has_many :pets, dependent: :destroy
  36633. # end
  36634. #
  36635. # person.pets.size # => 3
  36636. # person.pets
  36637. # # => [
  36638. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36639. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36640. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36641. # # ]
  36642. #
  36643. # person.pets.delete(Pet.find(1), Pet.find(3))
  36644. # # => [
  36645. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36646. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36647. # # ]
  36648. #
  36649. # person.pets.size # => 1
  36650. # person.pets
  36651. # # => [#<Pet id: 2, name: "Spook", person_id: 1>]
  36652. #
  36653. # Pet.find(1, 3)
  36654. # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
  36655. #
  36656. # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
  36657. # *without* calling their +destroy+ method.
  36658. #
  36659. # class Person < ActiveRecord::Base
  36660. # has_many :pets, dependent: :delete_all
  36661. # end
  36662. #
  36663. # person.pets.size # => 3
  36664. # person.pets
  36665. # # => [
  36666. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36667. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36668. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36669. # # ]
  36670. #
  36671. # person.pets.delete(Pet.find(1))
  36672. # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
  36673. #
  36674. # person.pets.size # => 2
  36675. # person.pets
  36676. # # => [
  36677. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36678. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36679. # # ]
  36680. #
  36681. # Pet.find(1)
  36682. # # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
  36683. #
  36684. # You can pass +Fixnum+ or +String+ values, it finds the records
  36685. # responding to the +id+ and executes delete on them.
  36686. #
  36687. # class Person < ActiveRecord::Base
  36688. # has_many :pets
  36689. # end
  36690. #
  36691. # person.pets.size # => 3
  36692. # person.pets
  36693. # # => [
  36694. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36695. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36696. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36697. # # ]
  36698. #
  36699. # person.pets.delete("1")
  36700. # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
  36701. #
  36702. # person.pets.delete(2, 3)
  36703. # # => [
  36704. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36705. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36706. # # ]
  36707. def delete(*records)
  36708. @association.delete(*records)
  36709. end
  36710. # Destroys the +records+ supplied and removes them from the collection.
  36711. # This method will _always_ remove record from the database ignoring
  36712. # the +:dependent+ option. Returns an array with the removed records.
  36713. #
  36714. # class Person < ActiveRecord::Base
  36715. # has_many :pets
  36716. # end
  36717. #
  36718. # person.pets.size # => 3
  36719. # person.pets
  36720. # # => [
  36721. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36722. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36723. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36724. # # ]
  36725. #
  36726. # person.pets.destroy(Pet.find(1))
  36727. # # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
  36728. #
  36729. # person.pets.size # => 2
  36730. # person.pets
  36731. # # => [
  36732. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36733. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36734. # # ]
  36735. #
  36736. # person.pets.destroy(Pet.find(2), Pet.find(3))
  36737. # # => [
  36738. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36739. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36740. # # ]
  36741. #
  36742. # person.pets.size # => 0
  36743. # person.pets # => []
  36744. #
  36745. # Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
  36746. #
  36747. # You can pass +Fixnum+ or +String+ values, it finds the records
  36748. # responding to the +id+ and then deletes them from the database.
  36749. #
  36750. # person.pets.size # => 3
  36751. # person.pets
  36752. # # => [
  36753. # # #<Pet id: 4, name: "Benny", person_id: 1>,
  36754. # # #<Pet id: 5, name: "Brain", person_id: 1>,
  36755. # # #<Pet id: 6, name: "Boss", person_id: 1>
  36756. # # ]
  36757. #
  36758. # person.pets.destroy("4")
  36759. # # => #<Pet id: 4, name: "Benny", person_id: 1>
  36760. #
  36761. # person.pets.size # => 2
  36762. # person.pets
  36763. # # => [
  36764. # # #<Pet id: 5, name: "Brain", person_id: 1>,
  36765. # # #<Pet id: 6, name: "Boss", person_id: 1>
  36766. # # ]
  36767. #
  36768. # person.pets.destroy(5, 6)
  36769. # # => [
  36770. # # #<Pet id: 5, name: "Brain", person_id: 1>,
  36771. # # #<Pet id: 6, name: "Boss", person_id: 1>
  36772. # # ]
  36773. #
  36774. # person.pets.size # => 0
  36775. # person.pets # => []
  36776. #
  36777. # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6)
  36778. def destroy(*records)
  36779. @association.destroy(*records)
  36780. end
  36781. # Specifies whether the records should be unique or not.
  36782. #
  36783. # class Person < ActiveRecord::Base
  36784. # has_many :pets
  36785. # end
  36786. #
  36787. # person.pets.select(:name)
  36788. # # => [
  36789. # # #<Pet name: "Fancy-Fancy">,
  36790. # # #<Pet name: "Fancy-Fancy">
  36791. # # ]
  36792. #
  36793. # person.pets.select(:name).uniq
  36794. # # => [#<Pet name: "Fancy-Fancy">]
  36795. def uniq
  36796. @association.uniq
  36797. end
  36798. # Count all records using SQL.
  36799. #
  36800. # class Person < ActiveRecord::Base
  36801. # has_many :pets
  36802. # end
  36803. #
  36804. # person.pets.count # => 3
  36805. # person.pets
  36806. # # => [
  36807. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36808. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36809. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36810. # # ]
  36811. def count(column_name = nil, options = {})
  36812. @association.count(column_name, options)
  36813. end
  36814. # Returns the size of the collection. If the collection hasn't been loaded,
  36815. # it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
  36816. #
  36817. # If the collection has been already loaded +size+ and +length+ are
  36818. # equivalent. If not and you are going to need the records anyway
  36819. # +length+ will take one less query. Otherwise +size+ is more efficient.
  36820. #
  36821. # class Person < ActiveRecord::Base
  36822. # has_many :pets
  36823. # end
  36824. #
  36825. # person.pets.size # => 3
  36826. # # executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1
  36827. #
  36828. # person.pets # This will execute a SELECT * FROM query
  36829. # # => [
  36830. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36831. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36832. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36833. # # ]
  36834. #
  36835. # person.pets.size # => 3
  36836. # # Because the collection is already loaded, this will behave like
  36837. # # collection.size and no SQL count query is executed.
  36838. def size
  36839. @association.size
  36840. end
  36841. # Returns the size of the collection calling +size+ on the target.
  36842. # If the collection has been already loaded, +length+ and +size+ are
  36843. # equivalent. If not and you are going to need the records anyway this
  36844. # method will take one less query. Otherwise +size+ is more efficient.
  36845. #
  36846. # class Person < ActiveRecord::Base
  36847. # has_many :pets
  36848. # end
  36849. #
  36850. # person.pets.length # => 3
  36851. # # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1
  36852. #
  36853. # # Because the collection is loaded, you can
  36854. # # call the collection with no additional queries:
  36855. # person.pets
  36856. # # => [
  36857. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36858. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  36859. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  36860. # # ]
  36861. def length
  36862. @association.length
  36863. end
  36864. # Returns +true+ if the collection is empty. If the collection has been
  36865. # loaded or the <tt>:counter_sql</tt> option is provided, it is equivalent
  36866. # to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
  36867. # it is equivalent to <tt>collection.exists?</tt>. If the collection has
  36868. # not already been loaded and you are going to fetch the records anyway it
  36869. # is better to check <tt>collection.length.zero?</tt>.
  36870. #
  36871. # class Person < ActiveRecord::Base
  36872. # has_many :pets
  36873. # end
  36874. #
  36875. # person.pets.count # => 1
  36876. # person.pets.empty? # => false
  36877. #
  36878. # person.pets.delete_all
  36879. #
  36880. # person.pets.count # => 0
  36881. # person.pets.empty? # => true
  36882. def empty?
  36883. @association.empty?
  36884. end
  36885. # Returns +true+ if the collection is not empty.
  36886. #
  36887. # class Person < ActiveRecord::Base
  36888. # has_many :pets
  36889. # end
  36890. #
  36891. # person.pets.count # => 0
  36892. # person.pets.any? # => false
  36893. #
  36894. # person.pets << Pet.new(name: 'Snoop')
  36895. # person.pets.count # => 0
  36896. # person.pets.any? # => true
  36897. #
  36898. # You can also pass a block to define criteria. The behavior
  36899. # is the same, it returns true if the collection based on the
  36900. # criteria is not empty.
  36901. #
  36902. # person.pets
  36903. # # => [#<Pet name: "Snoop", group: "dogs">]
  36904. #
  36905. # person.pets.any? do |pet|
  36906. # pet.group == 'cats'
  36907. # end
  36908. # # => false
  36909. #
  36910. # person.pets.any? do |pet|
  36911. # pet.group == 'dogs'
  36912. # end
  36913. # # => true
  36914. def any?(&block)
  36915. @association.any?(&block)
  36916. end
  36917. # Returns true if the collection has more than one record.
  36918. # Equivalent to <tt>collection.size > 1</tt>.
  36919. #
  36920. # class Person < ActiveRecord::Base
  36921. # has_many :pets
  36922. # end
  36923. #
  36924. # person.pets.count #=> 1
  36925. # person.pets.many? #=> false
  36926. #
  36927. # person.pets << Pet.new(name: 'Snoopy')
  36928. # person.pets.count #=> 2
  36929. # person.pets.many? #=> true
  36930. #
  36931. # You can also pass a block to define criteria. The
  36932. # behavior is the same, it returns true if the collection
  36933. # based on the criteria has more than one record.
  36934. #
  36935. # person.pets
  36936. # # => [
  36937. # # #<Pet name: "Gorby", group: "cats">,
  36938. # # #<Pet name: "Puff", group: "cats">,
  36939. # # #<Pet name: "Snoop", group: "dogs">
  36940. # # ]
  36941. #
  36942. # person.pets.many? do |pet|
  36943. # pet.group == 'dogs'
  36944. # end
  36945. # # => false
  36946. #
  36947. # person.pets.many? do |pet|
  36948. # pet.group == 'cats'
  36949. # end
  36950. # # => true
  36951. def many?(&block)
  36952. @association.many?(&block)
  36953. end
  36954. # Returns +true+ if the given object is present in the collection.
  36955. #
  36956. # class Person < ActiveRecord::Base
  36957. # has_many :pets
  36958. # end
  36959. #
  36960. # person.pets # => [#<Pet id: 20, name: "Snoop">]
  36961. #
  36962. # person.pets.include?(Pet.find(20)) # => true
  36963. # person.pets.include?(Pet.find(21)) # => false
  36964. def include?(record)
  36965. @association.include?(record)
  36966. end
  36967. alias_method :new, :build
  36968. def proxy_association
  36969. @association
  36970. end
  36971. # We don't want this object to be put on the scoping stack, because
  36972. # that could create an infinite loop where we call an @association
  36973. # method, which gets the current scope, which is this object, which
  36974. # delegates to @association, and so on.
  36975. def scoping
  36976. @association.scope.scoping { yield }
  36977. end
  36978. # Returns a <tt>Relation</tt> object for the records in this association
  36979. def scope
  36980. association = @association
  36981. @association.scope.extending! do
  36982. define_method(:proxy_association) { association }
  36983. end
  36984. end
  36985. # :nodoc:
  36986. alias spawn scope
  36987. # Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
  36988. # contain the same number of elements and if each element is equal
  36989. # to the corresponding element in the other array, otherwise returns
  36990. # +false+.
  36991. #
  36992. # class Person < ActiveRecord::Base
  36993. # has_many :pets
  36994. # end
  36995. #
  36996. # person.pets
  36997. # # => [
  36998. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  36999. # # #<Pet id: 2, name: "Spook", person_id: 1>
  37000. # # ]
  37001. #
  37002. # other = person.pets.to_ary
  37003. #
  37004. # person.pets == other
  37005. # # => true
  37006. #
  37007. # other = [Pet.new(id: 1), Pet.new(id: 2)]
  37008. #
  37009. # person.pets == other
  37010. # # => false
  37011. def ==(other)
  37012. load_target == other
  37013. end
  37014. # Returns a new array of objects from the collection. If the collection
  37015. # hasn't been loaded, it fetches the records from the database.
  37016. #
  37017. # class Person < ActiveRecord::Base
  37018. # has_many :pets
  37019. # end
  37020. #
  37021. # person.pets
  37022. # # => [
  37023. # # #<Pet id: 4, name: "Benny", person_id: 1>,
  37024. # # #<Pet id: 5, name: "Brain", person_id: 1>,
  37025. # # #<Pet id: 6, name: "Boss", person_id: 1>
  37026. # # ]
  37027. #
  37028. # other_pets = person.pets.to_ary
  37029. # # => [
  37030. # # #<Pet id: 4, name: "Benny", person_id: 1>,
  37031. # # #<Pet id: 5, name: "Brain", person_id: 1>,
  37032. # # #<Pet id: 6, name: "Boss", person_id: 1>
  37033. # # ]
  37034. #
  37035. # other_pets.replace([Pet.new(name: 'BooGoo')])
  37036. #
  37037. # other_pets
  37038. # # => [#<Pet id: nil, name: "BooGoo", person_id: 1>]
  37039. #
  37040. # person.pets
  37041. # # This is not affected by replace
  37042. # # => [
  37043. # # #<Pet id: 4, name: "Benny", person_id: 1>,
  37044. # # #<Pet id: 5, name: "Brain", person_id: 1>,
  37045. # # #<Pet id: 6, name: "Boss", person_id: 1>
  37046. # # ]
  37047. def to_ary
  37048. load_target.dup
  37049. end
  37050. alias_method :to_a, :to_ary
  37051. # Adds one or more +records+ to the collection by setting their foreign keys
  37052. # to the associations primary key. Returns +self+, so several appends may be
  37053. # chained together.
  37054. #
  37055. # class Person < ActiveRecord::Base
  37056. # has_many :pets
  37057. # end
  37058. #
  37059. # person.pets.size # => 0
  37060. # person.pets << Pet.new(name: 'Fancy-Fancy')
  37061. # person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')]
  37062. # person.pets.size # => 3
  37063. #
  37064. # person.id # => 1
  37065. # person.pets
  37066. # # => [
  37067. # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
  37068. # # #<Pet id: 2, name: "Spook", person_id: 1>,
  37069. # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
  37070. # # ]
  37071. def <<(*records)
  37072. proxy_association.concat(records) && self
  37073. end
  37074. alias_method :push, :<<
  37075. # Equivalent to +delete_all+. The difference is that returns +self+, instead
  37076. # of an array with the deleted objects, so methods can be chained. See
  37077. # +delete_all+ for more information.
  37078. def clear
  37079. delete_all
  37080. self
  37081. end
  37082. # Reloads the collection from the database. Returns +self+.
  37083. # Equivalent to <tt>collection(true)</tt>.
  37084. #
  37085. # class Person < ActiveRecord::Base
  37086. # has_many :pets
  37087. # end
  37088. #
  37089. # person.pets # fetches pets from the database
  37090. # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
  37091. #
  37092. # person.pets # uses the pets cache
  37093. # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
  37094. #
  37095. # person.pets.reload # fetches pets from the database
  37096. # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
  37097. #
  37098. # person.pets(true) # fetches pets from the database
  37099. # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
  37100. def reload
  37101. proxy_association.reload
  37102. self
  37103. end
  37104. end
  37105. end
  37106. end
  37107. module ActiveRecord
  37108. # = Active Record Has And Belongs To Many Association
  37109. module Associations
  37110. class HasAndBelongsToManyAssociation < CollectionAssociation #:nodoc:
  37111. attr_reader :join_table
  37112. def initialize(owner, reflection)
  37113. @join_table = Arel::Table.new(reflection.join_table)
  37114. super
  37115. end
  37116. def insert_record(record, validate = true, raise = false)
  37117. if record.new_record?
  37118. if raise
  37119. record.save!(:validate => validate)
  37120. else
  37121. return unless record.save(:validate => validate)
  37122. end
  37123. end
  37124. if options[:insert_sql]
  37125. owner.connection.insert(interpolate(options[:insert_sql], record))
  37126. else
  37127. stmt = join_table.compile_insert(
  37128. join_table[reflection.foreign_key] => owner.id,
  37129. join_table[reflection.association_foreign_key] => record.id
  37130. )
  37131. owner.connection.insert stmt
  37132. end
  37133. record
  37134. end
  37135. private
  37136. def count_records
  37137. load_target.size
  37138. end
  37139. def delete_records(records, method)
  37140. if sql = options[:delete_sql]
  37141. records = load_target if records == :all
  37142. records.each { |record| owner.connection.delete(interpolate(sql, record)) }
  37143. else
  37144. relation = join_table
  37145. condition = relation[reflection.foreign_key].eq(owner.id)
  37146. unless records == :all
  37147. condition = condition.and(
  37148. relation[reflection.association_foreign_key]
  37149. .in(records.map { |x| x.id }.compact)
  37150. )
  37151. end
  37152. owner.connection.delete(relation.where(condition).compile_delete)
  37153. end
  37154. end
  37155. def invertible_for?(record)
  37156. false
  37157. end
  37158. end
  37159. end
  37160. end
  37161. module ActiveRecord
  37162. # = Active Record Has Many Association
  37163. module Associations
  37164. # This is the proxy that handles a has many association.
  37165. #
  37166. # If the association has a <tt>:through</tt> option further specialization
  37167. # is provided by its child HasManyThroughAssociation.
  37168. class HasManyAssociation < CollectionAssociation #:nodoc:
  37169. def handle_dependency
  37170. case options[:dependent]
  37171. when :restrict, :restrict_with_exception
  37172. raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
  37173. when :restrict_with_error
  37174. unless empty?
  37175. record = klass.human_attribute_name(reflection.name).downcase
  37176. owner.errors.add(:base, :"restrict_dependent_destroy.many", record: record)
  37177. false
  37178. end
  37179. else
  37180. if options[:dependent] == :destroy
  37181. # No point in executing the counter update since we're going to destroy the parent anyway
  37182. load_target.each(&:mark_for_destruction)
  37183. end
  37184. delete_all
  37185. end
  37186. end
  37187. def insert_record(record, validate = true, raise = false)
  37188. set_owner_attributes(record)
  37189. if raise
  37190. record.save!(:validate => validate)
  37191. else
  37192. record.save(:validate => validate)
  37193. end
  37194. end
  37195. private
  37196. # Returns the number of records in this collection.
  37197. #
  37198. # If the association has a counter cache it gets that value. Otherwise
  37199. # it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
  37200. # there's one. Some configuration options like :group make it impossible
  37201. # to do an SQL count, in those cases the array count will be used.
  37202. #
  37203. # That does not depend on whether the collection has already been loaded
  37204. # or not. The +size+ method is the one that takes the loaded flag into
  37205. # account and delegates to +count_records+ if needed.
  37206. #
  37207. # If the collection is empty the target is set to an empty array and
  37208. # the loaded flag is set to true as well.
  37209. def count_records
  37210. count = if has_cached_counter?
  37211. owner.send(:read_attribute, cached_counter_attribute_name)
  37212. elsif options[:counter_sql] || options[:finder_sql]
  37213. reflection.klass.count_by_sql(custom_counter_sql)
  37214. else
  37215. scope.count
  37216. end
  37217. # If there's nothing in the database and @target has no new records
  37218. # we are certain the current target is an empty array. This is a
  37219. # documented side-effect of the method that may avoid an extra SELECT.
  37220. @target ||= [] and loaded! if count == 0
  37221. [association_scope.limit_value, count].compact.min
  37222. end
  37223. def has_cached_counter?(reflection = reflection)
  37224. owner.attribute_present?(cached_counter_attribute_name(reflection))
  37225. end
  37226. def cached_counter_attribute_name(reflection = reflection)
  37227. options[:counter_cache] || "#{reflection.name}_count"
  37228. end
  37229. def update_counter(difference, reflection = reflection)
  37230. if has_cached_counter?(reflection)
  37231. counter = cached_counter_attribute_name(reflection)
  37232. owner.class.update_counters(owner.id, counter => difference)
  37233. owner[counter] += difference
  37234. owner.changed_attributes.delete(counter) # eww
  37235. end
  37236. end
  37237. # This shit is nasty. We need to avoid the following situation:
  37238. #
  37239. # * An associated record is deleted via record.destroy
  37240. # * Hence the callbacks run, and they find a belongs_to on the record with a
  37241. # :counter_cache options which points back at our owner. So they update the
  37242. # counter cache.
  37243. # * In which case, we must make sure to *not* update the counter cache, or else
  37244. # it will be decremented twice.
  37245. #
  37246. # Hence this method.
  37247. def inverse_updates_counter_cache?(reflection = reflection)
  37248. counter_name = cached_counter_attribute_name(reflection)
  37249. reflection.klass.reflect_on_all_associations(:belongs_to).any? { |inverse_reflection|
  37250. inverse_reflection.counter_cache_column == counter_name
  37251. }
  37252. end
  37253. # Deletes the records according to the <tt>:dependent</tt> option.
  37254. def delete_records(records, method)
  37255. if method == :destroy
  37256. records.each { |r| r.destroy }
  37257. update_counter(-records.length) unless inverse_updates_counter_cache?
  37258. else
  37259. if records == :all
  37260. scope = self.scope
  37261. else
  37262. keys = records.map { |r| r[reflection.association_primary_key] }
  37263. scope = self.scope.where(reflection.association_primary_key => keys)
  37264. end
  37265. if method == :delete_all
  37266. update_counter(-scope.delete_all)
  37267. else
  37268. update_counter(-scope.update_all(reflection.foreign_key => nil))
  37269. end
  37270. end
  37271. end
  37272. def foreign_key_present?
  37273. owner.attribute_present?(reflection.association_primary_key)
  37274. end
  37275. end
  37276. end
  37277. end
  37278. module ActiveRecord
  37279. # = Active Record Has Many Through Association
  37280. module Associations
  37281. class HasManyThroughAssociation < HasManyAssociation #:nodoc:
  37282. include ThroughAssociation
  37283. def initialize(owner, reflection)
  37284. super
  37285. @through_records = {}
  37286. @through_association = nil
  37287. end
  37288. # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been
  37289. # loaded and calling collection.size if it has. If it's more likely than not that the collection does
  37290. # have a size larger than zero, and you need to fetch that collection afterwards, it'll take one fewer
  37291. # SELECT query if you use #length.
  37292. def size
  37293. if has_cached_counter?
  37294. owner.send(:read_attribute, cached_counter_attribute_name)
  37295. elsif loaded?
  37296. target.size
  37297. else
  37298. count
  37299. end
  37300. end
  37301. def concat(*records)
  37302. unless owner.new_record?
  37303. records.flatten.each do |record|
  37304. raise_on_type_mismatch(record)
  37305. record.save! if record.new_record?
  37306. end
  37307. end
  37308. super
  37309. end
  37310. def concat_records(records)
  37311. ensure_not_nested
  37312. records = super
  37313. if owner.new_record? && records
  37314. records.flatten.each do |record|
  37315. build_through_record(record)
  37316. end
  37317. end
  37318. records
  37319. end
  37320. def insert_record(record, validate = true, raise = false)
  37321. ensure_not_nested
  37322. if record.new_record?
  37323. if raise
  37324. record.save!(:validate => validate)
  37325. else
  37326. return unless record.save(:validate => validate)
  37327. end
  37328. end
  37329. save_through_record(record)
  37330. update_counter(1)
  37331. record
  37332. end
  37333. private
  37334. def through_association
  37335. @through_association ||= owner.association(through_reflection.name)
  37336. end
  37337. # We temporarily cache through record that has been build, because if we build a
  37338. # through record in build_record and then subsequently call insert_record, then we
  37339. # want to use the exact same object.
  37340. #
  37341. # However, after insert_record has been called, we clear the cache entry because
  37342. # we want it to be possible to have multiple instances of the same record in an
  37343. # association
  37344. def build_through_record(record)
  37345. @through_records[record.object_id] ||= begin
  37346. ensure_mutable
  37347. through_record = through_association.build
  37348. through_record.send("#{source_reflection.name}=", record)
  37349. through_record
  37350. end
  37351. end
  37352. def save_through_record(record)
  37353. build_through_record(record).save!
  37354. ensure
  37355. @through_records.delete(record.object_id)
  37356. end
  37357. def build_record(attributes)
  37358. ensure_not_nested
  37359. record = super(attributes)
  37360. inverse = source_reflection.inverse_of
  37361. if inverse
  37362. if inverse.macro == :has_many
  37363. record.send(inverse.name) << build_through_record(record)
  37364. elsif inverse.macro == :has_one
  37365. record.send("#{inverse.name}=", build_through_record(record))
  37366. end
  37367. end
  37368. record
  37369. end
  37370. def target_reflection_has_associated_record?
  37371. !(through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?)
  37372. end
  37373. def update_through_counter?(method)
  37374. case method
  37375. when :destroy
  37376. !inverse_updates_counter_cache?(through_reflection)
  37377. when :nullify
  37378. false
  37379. else
  37380. true
  37381. end
  37382. end
  37383. def delete_records(records, method)
  37384. ensure_not_nested
  37385. # This is unoptimised; it will load all the target records
  37386. # even when we just want to delete everything.
  37387. records = load_target if records == :all
  37388. scope = through_association.scope
  37389. scope.where! construct_join_attributes(*records)
  37390. case method
  37391. when :destroy
  37392. count = scope.destroy_all.length
  37393. when :nullify
  37394. count = scope.update_all(source_reflection.foreign_key => nil)
  37395. else
  37396. count = scope.delete_all
  37397. end
  37398. delete_through_records(records)
  37399. if source_reflection.options[:counter_cache]
  37400. counter = source_reflection.counter_cache_column
  37401. klass.decrement_counter counter, records.map(&:id)
  37402. end
  37403. if through_reflection.macro == :has_many && update_through_counter?(method)
  37404. update_counter(-count, through_reflection)
  37405. end
  37406. update_counter(-count)
  37407. end
  37408. def through_records_for(record)
  37409. attributes = construct_join_attributes(record)
  37410. candidates = Array.wrap(through_association.target)
  37411. candidates.find_all { |c| c.attributes.slice(*attributes.keys) == attributes }
  37412. end
  37413. def delete_through_records(records)
  37414. records.each do |record|
  37415. through_records = through_records_for(record)
  37416. if through_reflection.macro == :has_many
  37417. through_records.each { |r| through_association.target.delete(r) }
  37418. else
  37419. if through_records.include?(through_association.target)
  37420. through_association.target = nil
  37421. end
  37422. end
  37423. @through_records.delete(record.object_id)
  37424. end
  37425. end
  37426. def find_target
  37427. return [] unless target_reflection_has_associated_record?
  37428. scope.to_a
  37429. end
  37430. # NOTE - not sure that we can actually cope with inverses here
  37431. def invertible_for?(record)
  37432. false
  37433. end
  37434. end
  37435. end
  37436. end
  37437. module ActiveRecord
  37438. # = Active Record Belongs To Has One Association
  37439. module Associations
  37440. class HasOneAssociation < SingularAssociation #:nodoc:
  37441. def handle_dependency
  37442. case options[:dependent]
  37443. when :restrict, :restrict_with_exception
  37444. raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target
  37445. when :restrict_with_error
  37446. if load_target
  37447. record = klass.human_attribute_name(reflection.name).downcase
  37448. owner.errors.add(:base, :"restrict_dependent_destroy.one", record: record)
  37449. false
  37450. end
  37451. else
  37452. delete
  37453. end
  37454. end
  37455. def replace(record, save = true)
  37456. raise_on_type_mismatch(record) if record
  37457. load_target
  37458. # If target and record are nil, or target is equal to record,
  37459. # we don't need to have transaction.
  37460. if (target || record) && target != record
  37461. transaction_if(save) do
  37462. remove_target!(options[:dependent]) if target && !target.destroyed?
  37463. if record
  37464. set_owner_attributes(record)
  37465. set_inverse_instance(record)
  37466. if owner.persisted? && save && !record.save
  37467. nullify_owner_attributes(record)
  37468. set_owner_attributes(target) if target
  37469. raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
  37470. end
  37471. end
  37472. end
  37473. end
  37474. self.target = record
  37475. end
  37476. def delete(method = options[:dependent])
  37477. if load_target
  37478. case method
  37479. when :delete
  37480. target.delete
  37481. when :destroy
  37482. target.destroy
  37483. when :nullify
  37484. target.update_columns(reflection.foreign_key => nil)
  37485. end
  37486. end
  37487. end
  37488. private
  37489. # The reason that the save param for replace is false, if for create (not just build),
  37490. # is because the setting of the foreign keys is actually handled by the scoping when
  37491. # the record is instantiated, and so they are set straight away and do not need to be
  37492. # updated within replace.
  37493. def set_new_record(record)
  37494. replace(record, false)
  37495. end
  37496. def remove_target!(method)
  37497. case method
  37498. when :delete
  37499. target.delete
  37500. when :destroy
  37501. target.destroy
  37502. else
  37503. nullify_owner_attributes(target)
  37504. if target.persisted? && owner.persisted? && !target.save
  37505. set_owner_attributes(target)
  37506. raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
  37507. "The record failed to save after its foreign key was set to nil."
  37508. end
  37509. end
  37510. end
  37511. def nullify_owner_attributes(record)
  37512. record[reflection.foreign_key] = nil
  37513. end
  37514. def transaction_if(value)
  37515. if value
  37516. reflection.klass.transaction { yield }
  37517. else
  37518. yield
  37519. end
  37520. end
  37521. end
  37522. end
  37523. end
  37524. module ActiveRecord
  37525. # = Active Record Has One Through Association
  37526. module Associations
  37527. class HasOneThroughAssociation < HasOneAssociation #:nodoc:
  37528. include ThroughAssociation
  37529. def replace(record)
  37530. create_through_record(record)
  37531. self.target = record
  37532. end
  37533. private
  37534. def create_through_record(record)
  37535. ensure_not_nested
  37536. through_proxy = owner.association(through_reflection.name)
  37537. through_record = through_proxy.send(:load_target)
  37538. if through_record && !record
  37539. through_record.destroy
  37540. elsif record
  37541. attributes = construct_join_attributes(record)
  37542. if through_record
  37543. through_record.update(attributes)
  37544. elsif owner.new_record?
  37545. through_proxy.build(attributes)
  37546. else
  37547. through_proxy.create(attributes)
  37548. end
  37549. end
  37550. end
  37551. end
  37552. end
  37553. end
  37554. module ActiveRecord
  37555. module Associations
  37556. class JoinDependency # :nodoc:
  37557. class JoinAssociation < JoinPart # :nodoc:
  37558. include JoinHelper
  37559. # The reflection of the association represented
  37560. attr_reader :reflection
  37561. # The JoinDependency object which this JoinAssociation exists within. This is mainly
  37562. # relevant for generating aliases which do not conflict with other joins which are
  37563. # part of the query.
  37564. attr_reader :join_dependency
  37565. # A JoinBase instance representing the active record we are joining onto.
  37566. # (So in Author.has_many :posts, the Author would be that base record.)
  37567. attr_reader :parent
  37568. # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin
  37569. attr_accessor :join_type
  37570. # These implement abstract methods from the superclass
  37571. attr_reader :aliased_prefix
  37572. attr_reader :tables
  37573. delegate :options, :through_reflection, :source_reflection, :chain, :to => :reflection
  37574. delegate :table, :table_name, :to => :parent, :prefix => :parent
  37575. delegate :alias_tracker, :to => :join_dependency
  37576. alias :alias_suffix :parent_table_name
  37577. def initialize(reflection, join_dependency, parent = nil)
  37578. reflection.check_validity!
  37579. if reflection.options[:polymorphic]
  37580. raise EagerLoadPolymorphicError.new(reflection)
  37581. end
  37582. super(reflection.klass)
  37583. @reflection = reflection
  37584. @join_dependency = join_dependency
  37585. @parent = parent
  37586. @join_type = Arel::InnerJoin
  37587. @aliased_prefix = "t#{ join_dependency.join_parts.size }"
  37588. @tables = construct_tables.reverse
  37589. end
  37590. def ==(other)
  37591. other.class == self.class &&
  37592. other.reflection == reflection &&
  37593. other.parent == parent
  37594. end
  37595. def find_parent_in(other_join_dependency)
  37596. other_join_dependency.join_parts.detect do |join_part|
  37597. parent == join_part
  37598. end
  37599. end
  37600. def join_to(relation)
  37601. tables = @tables.dup
  37602. foreign_table = parent_table
  37603. foreign_klass = parent.active_record
  37604. # The chain starts with the target table, but we want to end with it here (makes
  37605. # more sense in this context), so we reverse
  37606. chain.reverse.each_with_index do |reflection, i|
  37607. table = tables.shift
  37608. case reflection.source_macro
  37609. when :belongs_to
  37610. key = reflection.association_primary_key
  37611. foreign_key = reflection.foreign_key
  37612. when :has_and_belongs_to_many
  37613. # Join the join table first...
  37614. relation.from(join(
  37615. table,
  37616. table[reflection.foreign_key].
  37617. eq(foreign_table[reflection.active_record_primary_key])
  37618. ))
  37619. foreign_table, table = table, tables.shift
  37620. key = reflection.association_primary_key
  37621. foreign_key = reflection.association_foreign_key
  37622. else
  37623. key = reflection.foreign_key
  37624. foreign_key = reflection.active_record_primary_key
  37625. end
  37626. constraint = build_constraint(reflection, table, key, foreign_table, foreign_key)
  37627. scope_chain_items = scope_chain[i]
  37628. if reflection.type
  37629. scope_chain_items += [
  37630. ActiveRecord::Relation.new(reflection.klass, table)
  37631. .where(reflection.type => foreign_klass.base_class.name)
  37632. ]
  37633. end
  37634. scope_chain_items.each do |item|
  37635. unless item.is_a?(Relation)
  37636. item = ActiveRecord::Relation.new(reflection.klass, table).instance_exec(self, &item)
  37637. end
  37638. constraint = constraint.and(item.arel.constraints) unless item.arel.constraints.empty?
  37639. end
  37640. relation.from(join(table, constraint))
  37641. # The current table in this iteration becomes the foreign table in the next
  37642. foreign_table, foreign_klass = table, reflection.klass
  37643. end
  37644. relation
  37645. end
  37646. def build_constraint(reflection, table, key, foreign_table, foreign_key)
  37647. constraint = table[key].eq(foreign_table[foreign_key])
  37648. if reflection.klass.finder_needs_type_condition?
  37649. constraint = table.create_and([
  37650. constraint,
  37651. reflection.klass.send(:type_condition, table)
  37652. ])
  37653. end
  37654. constraint
  37655. end
  37656. def join_relation(joining_relation)
  37657. self.join_type = Arel::OuterJoin
  37658. joining_relation.joins(self)
  37659. end
  37660. def table
  37661. tables.last
  37662. end
  37663. def aliased_table_name
  37664. table.table_alias || table.name
  37665. end
  37666. def scope_chain
  37667. @scope_chain ||= reflection.scope_chain.reverse
  37668. end
  37669. end
  37670. end
  37671. end
  37672. end
  37673. module ActiveRecord
  37674. module Associations
  37675. class JoinDependency # :nodoc:
  37676. class JoinBase < JoinPart # :nodoc:
  37677. def ==(other)
  37678. other.class == self.class &&
  37679. other.active_record == active_record
  37680. end
  37681. def aliased_prefix
  37682. "t0"
  37683. end
  37684. def table
  37685. Arel::Table.new(table_name, arel_engine)
  37686. end
  37687. def aliased_table_name
  37688. active_record.table_name
  37689. end
  37690. end
  37691. end
  37692. end
  37693. end
  37694. module ActiveRecord
  37695. module Associations
  37696. class JoinDependency # :nodoc:
  37697. # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited
  37698. # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which
  37699. # everything else is being joined onto. A JoinAssociation represents an association which
  37700. # is joining to the base. A JoinAssociation may result in more than one actual join
  37701. # operations (for example a has_and_belongs_to_many JoinAssociation would result in
  37702. # two; one for the join table and one for the target table).
  37703. class JoinPart # :nodoc:
  37704. # The Active Record class which this join part is associated 'about'; for a JoinBase
  37705. # this is the actual base model, for a JoinAssociation this is the target model of the
  37706. # association.
  37707. attr_reader :active_record
  37708. delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :active_record
  37709. def initialize(active_record)
  37710. @active_record = active_record
  37711. @cached_record = {}
  37712. @column_names_with_alias = nil
  37713. end
  37714. def aliased_table
  37715. Arel::Nodes::TableAlias.new table, aliased_table_name
  37716. end
  37717. def ==(other)
  37718. raise NotImplementedError
  37719. end
  37720. # An Arel::Table for the active_record
  37721. def table
  37722. raise NotImplementedError
  37723. end
  37724. # The prefix to be used when aliasing columns in the active_record's table
  37725. def aliased_prefix
  37726. raise NotImplementedError
  37727. end
  37728. # The alias for the active_record's table
  37729. def aliased_table_name
  37730. raise NotImplementedError
  37731. end
  37732. # The alias for the primary key of the active_record's table
  37733. def aliased_primary_key
  37734. "#{aliased_prefix}_r0"
  37735. end
  37736. # An array of [column_name, alias] pairs for the table
  37737. def column_names_with_alias
  37738. unless @column_names_with_alias
  37739. @column_names_with_alias = []
  37740. ([primary_key] + (column_names - [primary_key])).compact.each_with_index do |column_name, i|
  37741. @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
  37742. end
  37743. end
  37744. @column_names_with_alias
  37745. end
  37746. def extract_record(row)
  37747. Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}]
  37748. end
  37749. def record_id(row)
  37750. row[aliased_primary_key]
  37751. end
  37752. def instantiate(row)
  37753. @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
  37754. end
  37755. end
  37756. end
  37757. end
  37758. end
  37759. module ActiveRecord
  37760. module Associations
  37761. class JoinDependency # :nodoc:
  37762. autoload :JoinPart, 'active_record/associations/join_dependency/join_part'
  37763. autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
  37764. autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
  37765. attr_reader :join_parts, :reflections, :alias_tracker, :active_record
  37766. def initialize(base, associations, joins)
  37767. @active_record = base
  37768. @table_joins = joins
  37769. @join_parts = [JoinBase.new(base)]
  37770. @associations = {}
  37771. @reflections = []
  37772. @alias_tracker = AliasTracker.new(base.connection, joins)
  37773. @alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
  37774. build(associations)
  37775. end
  37776. def graft(*associations)
  37777. associations.each do |association|
  37778. join_associations.detect {|a| association == a} ||
  37779. build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
  37780. end
  37781. self
  37782. end
  37783. def join_associations
  37784. join_parts.last(join_parts.length - 1)
  37785. end
  37786. def join_base
  37787. join_parts.first
  37788. end
  37789. def columns
  37790. join_parts.collect { |join_part|
  37791. table = join_part.aliased_table
  37792. join_part.column_names_with_alias.collect{ |column_name, aliased_name|
  37793. table[column_name].as Arel.sql(aliased_name)
  37794. }
  37795. }.flatten
  37796. end
  37797. def instantiate(rows)
  37798. primary_key = join_base.aliased_primary_key
  37799. parents = {}
  37800. records = rows.map { |model|
  37801. primary_id = model[primary_key]
  37802. parent = parents[primary_id] ||= join_base.instantiate(model)
  37803. construct(parent, @associations, join_associations, model)
  37804. parent
  37805. }.uniq
  37806. remove_duplicate_results!(active_record, records, @associations)
  37807. records
  37808. end
  37809. def remove_duplicate_results!(base, records, associations)
  37810. case associations
  37811. when Symbol, String
  37812. reflection = base.reflections[associations]
  37813. remove_uniq_by_reflection(reflection, records)
  37814. when Array
  37815. associations.each do |association|
  37816. remove_duplicate_results!(base, records, association)
  37817. end
  37818. when Hash
  37819. associations.each_key do |name|
  37820. reflection = base.reflections[name]
  37821. remove_uniq_by_reflection(reflection, records)
  37822. parent_records = []
  37823. records.each do |record|
  37824. if descendant = record.send(reflection.name)
  37825. if reflection.collection?
  37826. parent_records.concat descendant.target.uniq
  37827. else
  37828. parent_records << descendant
  37829. end
  37830. end
  37831. end
  37832. remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
  37833. end
  37834. end
  37835. end
  37836. protected
  37837. def cache_joined_association(association)
  37838. associations = []
  37839. parent = association.parent
  37840. while parent != join_base
  37841. associations.unshift(parent.reflection.name)
  37842. parent = parent.parent
  37843. end
  37844. ref = @associations
  37845. associations.each do |key|
  37846. ref = ref[key]
  37847. end
  37848. ref[association.reflection.name] ||= {}
  37849. end
  37850. def build(associations, parent = nil, join_type = Arel::InnerJoin)
  37851. parent ||= join_parts.last
  37852. case associations
  37853. when Symbol, String
  37854. reflection = parent.reflections[associations.to_s.intern] or
  37855. raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
  37856. unless join_association = find_join_association(reflection, parent)
  37857. @reflections << reflection
  37858. join_association = build_join_association(reflection, parent)
  37859. join_association.join_type = join_type
  37860. @join_parts << join_association
  37861. cache_joined_association(join_association)
  37862. end
  37863. join_association
  37864. when Array
  37865. associations.each do |association|
  37866. build(association, parent, join_type)
  37867. end
  37868. when Hash
  37869. associations.keys.sort_by { |a| a.to_s }.each do |name|
  37870. join_association = build(name, parent, join_type)
  37871. build(associations[name], join_association, join_type)
  37872. end
  37873. else
  37874. raise ConfigurationError, associations.inspect
  37875. end
  37876. end
  37877. def find_join_association(name_or_reflection, parent)
  37878. if String === name_or_reflection
  37879. name_or_reflection = name_or_reflection.to_sym
  37880. end
  37881. join_associations.detect { |j|
  37882. j.reflection == name_or_reflection && j.parent == parent
  37883. }
  37884. end
  37885. def remove_uniq_by_reflection(reflection, records)
  37886. if reflection && reflection.collection?
  37887. records.each { |record| record.send(reflection.name).target.uniq! }
  37888. end
  37889. end
  37890. def build_join_association(reflection, parent)
  37891. JoinAssociation.new(reflection, self, parent)
  37892. end
  37893. def construct(parent, associations, join_parts, row)
  37894. case associations
  37895. when Symbol, String
  37896. name = associations.to_s
  37897. join_part = join_parts.detect { |j|
  37898. j.reflection.name.to_s == name &&
  37899. j.parent_table_name == parent.class.table_name }
  37900. raise(ConfigurationError, "No such association") unless join_part
  37901. join_parts.delete(join_part)
  37902. construct_association(parent, join_part, row)
  37903. when Array
  37904. associations.each do |association|
  37905. construct(parent, association, join_parts, row)
  37906. end
  37907. when Hash
  37908. associations.sort_by { |k,_| k.to_s }.each do |association_name, assoc|
  37909. association = construct(parent, association_name, join_parts, row)
  37910. construct(association, assoc, join_parts, row) if association
  37911. end
  37912. else
  37913. raise ConfigurationError, associations.inspect
  37914. end
  37915. end
  37916. def construct_association(record, join_part, row)
  37917. return if record.id.to_s != join_part.parent.record_id(row).to_s
  37918. macro = join_part.reflection.macro
  37919. if macro == :has_one
  37920. return record.association(join_part.reflection.name).target if record.association_cache.key?(join_part.reflection.name)
  37921. association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil?
  37922. set_target_and_inverse(join_part, association, record)
  37923. else
  37924. association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil?
  37925. case macro
  37926. when :has_many, :has_and_belongs_to_many
  37927. other = record.association(join_part.reflection.name)
  37928. other.loaded!
  37929. other.target.push(association) if association
  37930. other.set_inverse_instance(association)
  37931. when :belongs_to
  37932. set_target_and_inverse(join_part, association, record)
  37933. else
  37934. raise ConfigurationError, "unknown macro: #{join_part.reflection.macro}"
  37935. end
  37936. end
  37937. association
  37938. end
  37939. def set_target_and_inverse(join_part, association, record)
  37940. other = record.association(join_part.reflection.name)
  37941. other.target = association
  37942. other.set_inverse_instance(association)
  37943. end
  37944. end
  37945. end
  37946. end
  37947. module ActiveRecord
  37948. module Associations
  37949. # Helper class module which gets mixed into JoinDependency::JoinAssociation and AssociationScope
  37950. module JoinHelper #:nodoc:
  37951. def join_type
  37952. Arel::InnerJoin
  37953. end
  37954. private
  37955. def construct_tables
  37956. tables = []
  37957. chain.each do |reflection|
  37958. tables << alias_tracker.aliased_table_for(
  37959. table_name_for(reflection),
  37960. table_alias_for(reflection, reflection != self.reflection)
  37961. )
  37962. if reflection.source_macro == :has_and_belongs_to_many
  37963. tables << alias_tracker.aliased_table_for(
  37964. (reflection.source_reflection || reflection).join_table,
  37965. table_alias_for(reflection, true)
  37966. )
  37967. end
  37968. end
  37969. tables
  37970. end
  37971. def table_name_for(reflection)
  37972. reflection.table_name
  37973. end
  37974. def table_alias_for(reflection, join = false)
  37975. name = "#{reflection.plural_name}_#{alias_suffix}"
  37976. name << "_join" if join
  37977. name
  37978. end
  37979. def join(table, constraint)
  37980. table.create_join(table, table.create_on(constraint), join_type)
  37981. end
  37982. end
  37983. end
  37984. end
  37985. module ActiveRecord
  37986. module Associations
  37987. class Preloader
  37988. class Association #:nodoc:
  37989. attr_reader :owners, :reflection, :preload_scope, :model, :klass
  37990. def initialize(klass, owners, reflection, preload_scope)
  37991. @klass = klass
  37992. @owners = owners
  37993. @reflection = reflection
  37994. @preload_scope = preload_scope
  37995. @model = owners.first && owners.first.class
  37996. @scope = nil
  37997. @owners_by_key = nil
  37998. end
  37999. def run
  38000. unless owners.first.association(reflection.name).loaded?
  38001. preload
  38002. end
  38003. end
  38004. def preload
  38005. raise NotImplementedError
  38006. end
  38007. def scope
  38008. @scope ||= build_scope
  38009. end
  38010. def records_for(ids)
  38011. scope.where(association_key.in(ids))
  38012. end
  38013. def table
  38014. klass.arel_table
  38015. end
  38016. # The name of the key on the associated records
  38017. def association_key_name
  38018. raise NotImplementedError
  38019. end
  38020. # This is overridden by HABTM as the condition should be on the foreign_key column in
  38021. # the join table
  38022. def association_key
  38023. table[association_key_name]
  38024. end
  38025. # The name of the key on the model which declares the association
  38026. def owner_key_name
  38027. raise NotImplementedError
  38028. end
  38029. # We're converting to a string here because postgres will return the aliased association
  38030. # key in a habtm as a string (for whatever reason)
  38031. def owners_by_key
  38032. @owners_by_key ||= owners.group_by do |owner|
  38033. key = owner[owner_key_name]
  38034. key && key.to_s
  38035. end
  38036. end
  38037. def options
  38038. reflection.options
  38039. end
  38040. private
  38041. def associated_records_by_owner
  38042. owners_map = owners_by_key
  38043. owner_keys = owners_map.keys.compact
  38044. if klass.nil? || owner_keys.empty?
  38045. records = []
  38046. else
  38047. # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
  38048. # Make several smaller queries if necessary or make one query if the adapter supports it
  38049. sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
  38050. records = sliced.map { |slice| records_for(slice).to_a }.flatten
  38051. end
  38052. # Each record may have multiple owners, and vice-versa
  38053. records_by_owner = Hash[owners.map { |owner| [owner, []] }]
  38054. records.each do |record|
  38055. owner_key = record[association_key_name].to_s
  38056. owners_map[owner_key].each do |owner|
  38057. records_by_owner[owner] << record
  38058. end
  38059. end
  38060. records_by_owner
  38061. end
  38062. def reflection_scope
  38063. @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
  38064. end
  38065. def build_scope
  38066. scope = klass.unscoped
  38067. scope.default_scoped = true
  38068. values = reflection_scope.values
  38069. preload_values = preload_scope.values
  38070. scope.where_values = Array(values[:where]) + Array(preload_values[:where])
  38071. scope.references_values = Array(values[:references]) + Array(preload_values[:references])
  38072. scope.select! preload_values[:select] || values[:select] || table[Arel.star]
  38073. scope.includes! preload_values[:includes] || values[:includes]
  38074. if options[:as]
  38075. scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
  38076. end
  38077. scope
  38078. end
  38079. end
  38080. end
  38081. end
  38082. end
  38083. module ActiveRecord
  38084. module Associations
  38085. class Preloader
  38086. class BelongsTo < SingularAssociation #:nodoc:
  38087. def association_key_name
  38088. reflection.options[:primary_key] || klass && klass.primary_key
  38089. end
  38090. def owner_key_name
  38091. reflection.foreign_key
  38092. end
  38093. end
  38094. end
  38095. end
  38096. end
  38097. module ActiveRecord
  38098. module Associations
  38099. class Preloader
  38100. class CollectionAssociation < Association #:nodoc:
  38101. private
  38102. def build_scope
  38103. super.order(preload_scope.values[:order] || reflection_scope.values[:order])
  38104. end
  38105. def preload
  38106. associated_records_by_owner.each do |owner, records|
  38107. association = owner.association(reflection.name)
  38108. association.loaded!
  38109. association.target.concat(records)
  38110. records.each { |record| association.set_inverse_instance(record) }
  38111. end
  38112. end
  38113. end
  38114. end
  38115. end
  38116. end
  38117. module ActiveRecord
  38118. module Associations
  38119. class Preloader
  38120. class HasAndBelongsToMany < CollectionAssociation #:nodoc:
  38121. attr_reader :join_table
  38122. def initialize(klass, records, reflection, preload_options)
  38123. super
  38124. @join_table = Arel::Table.new(reflection.join_table).alias('t0')
  38125. end
  38126. # Unlike the other associations, we want to get a raw array of rows so that we can
  38127. # access the aliased column on the join table
  38128. def records_for(ids)
  38129. scope = super
  38130. klass.connection.select_all(scope.arel, 'SQL', scope.bind_values)
  38131. end
  38132. def owner_key_name
  38133. reflection.active_record_primary_key
  38134. end
  38135. def association_key_name
  38136. 'ar_association_key_name'
  38137. end
  38138. def association_key
  38139. join_table[reflection.foreign_key]
  38140. end
  38141. private
  38142. # Once we have used the join table column (in super), we manually instantiate the
  38143. # actual records, ensuring that we don't create more than one instances of the same
  38144. # record
  38145. def associated_records_by_owner
  38146. records = {}
  38147. super.each do |owner_key, rows|
  38148. rows.map! { |row| records[row[klass.primary_key]] ||= klass.instantiate(row) }
  38149. end
  38150. end
  38151. def build_scope
  38152. super.joins(join).select(join_select)
  38153. end
  38154. def join_select
  38155. association_key.as(Arel.sql(association_key_name))
  38156. end
  38157. def join
  38158. condition = table[reflection.association_primary_key].eq(
  38159. join_table[reflection.association_foreign_key])
  38160. table.create_join(join_table, table.create_on(condition))
  38161. end
  38162. end
  38163. end
  38164. end
  38165. end
  38166. module ActiveRecord
  38167. module Associations
  38168. class Preloader
  38169. class HasMany < CollectionAssociation #:nodoc:
  38170. def association_key_name
  38171. reflection.foreign_key
  38172. end
  38173. def owner_key_name
  38174. reflection.active_record_primary_key
  38175. end
  38176. end
  38177. end
  38178. end
  38179. end
  38180. module ActiveRecord
  38181. module Associations
  38182. class Preloader
  38183. class HasManyThrough < CollectionAssociation #:nodoc:
  38184. include ThroughAssociation
  38185. def associated_records_by_owner
  38186. super.each do |owner, records|
  38187. records.uniq! if reflection_scope.uniq_value
  38188. end
  38189. end
  38190. end
  38191. end
  38192. end
  38193. end
  38194. module ActiveRecord
  38195. module Associations
  38196. class Preloader
  38197. class HasOne < SingularAssociation #:nodoc:
  38198. def association_key_name
  38199. reflection.foreign_key
  38200. end
  38201. def owner_key_name
  38202. reflection.active_record_primary_key
  38203. end
  38204. private
  38205. def build_scope
  38206. super.order(preload_scope.values[:order] || reflection_scope.values[:order])
  38207. end
  38208. end
  38209. end
  38210. end
  38211. end
  38212. module ActiveRecord
  38213. module Associations
  38214. class Preloader
  38215. class HasOneThrough < SingularAssociation #:nodoc:
  38216. include ThroughAssociation
  38217. end
  38218. end
  38219. end
  38220. end
  38221. module ActiveRecord
  38222. module Associations
  38223. class Preloader
  38224. class SingularAssociation < Association #:nodoc:
  38225. private
  38226. def preload
  38227. associated_records_by_owner.each do |owner, associated_records|
  38228. record = associated_records.first
  38229. association = owner.association(reflection.name)
  38230. association.target = record
  38231. association.set_inverse_instance(record)
  38232. end
  38233. end
  38234. end
  38235. end
  38236. end
  38237. end
  38238. module ActiveRecord
  38239. module Associations
  38240. class Preloader
  38241. module ThroughAssociation #:nodoc:
  38242. def through_reflection
  38243. reflection.through_reflection
  38244. end
  38245. def source_reflection
  38246. reflection.source_reflection
  38247. end
  38248. def associated_records_by_owner
  38249. through_records = through_records_by_owner
  38250. Preloader.new(through_records.values.flatten, source_reflection.name, reflection_scope).run
  38251. through_records.each do |owner, records|
  38252. records.map! { |r| r.send(source_reflection.name) }.flatten!
  38253. records.compact!
  38254. end
  38255. end
  38256. private
  38257. def through_records_by_owner
  38258. Preloader.new(owners, through_reflection.name, through_scope).run
  38259. Hash[owners.map do |owner|
  38260. through_records = Array.wrap(owner.send(through_reflection.name))
  38261. # Dont cache the association - we would only be caching a subset
  38262. if (through_scope != through_reflection.klass.unscoped) ||
  38263. (reflection.options[:source_type] && through_reflection.collection?)
  38264. owner.association(through_reflection.name).reset
  38265. end
  38266. [owner, through_records]
  38267. end]
  38268. end
  38269. def through_scope
  38270. through_scope = through_reflection.klass.unscoped
  38271. if options[:source_type]
  38272. through_scope.where! reflection.foreign_type => options[:source_type]
  38273. else
  38274. unless reflection_scope.where_values.empty?
  38275. through_scope.includes_values = reflection_scope.values[:includes] || options[:source]
  38276. through_scope.where_values = reflection_scope.values[:where]
  38277. end
  38278. through_scope.order! reflection_scope.values[:order]
  38279. through_scope.references! reflection_scope.values[:references]
  38280. end
  38281. through_scope
  38282. end
  38283. end
  38284. end
  38285. end
  38286. end
  38287. module ActiveRecord
  38288. module Associations
  38289. # Implements the details of eager loading of Active Record associations.
  38290. #
  38291. # Note that 'eager loading' and 'preloading' are actually the same thing.
  38292. # However, there are two different eager loading strategies.
  38293. #
  38294. # The first one is by using table joins. This was only strategy available
  38295. # prior to Rails 2.1. Suppose that you have an Author model with columns
  38296. # 'name' and 'age', and a Book model with columns 'name' and 'sales'. Using
  38297. # this strategy, Active Record would try to retrieve all data for an author
  38298. # and all of its books via a single query:
  38299. #
  38300. # SELECT * FROM authors
  38301. # LEFT OUTER JOIN books ON authors.id = books.author_id
  38302. # WHERE authors.name = 'Ken Akamatsu'
  38303. #
  38304. # However, this could result in many rows that contain redundant data. After
  38305. # having received the first row, we already have enough data to instantiate
  38306. # the Author object. In all subsequent rows, only the data for the joined
  38307. # 'books' table is useful; the joined 'authors' data is just redundant, and
  38308. # processing this redundant data takes memory and CPU time. The problem
  38309. # quickly becomes worse and worse as the level of eager loading increases
  38310. # (i.e. if Active Record is to eager load the associations' associations as
  38311. # well).
  38312. #
  38313. # The second strategy is to use multiple database queries, one for each
  38314. # level of association. Since Rails 2.1, this is the default strategy. In
  38315. # situations where a table join is necessary (e.g. when the +:conditions+
  38316. # option references an association's column), it will fallback to the table
  38317. # join strategy.
  38318. class Preloader #:nodoc:
  38319. extend ActiveSupport::Autoload
  38320. eager_autoload do
  38321. autoload :Association, 'active_record/associations/preloader/association'
  38322. autoload :SingularAssociation, 'active_record/associations/preloader/singular_association'
  38323. autoload :CollectionAssociation, 'active_record/associations/preloader/collection_association'
  38324. autoload :ThroughAssociation, 'active_record/associations/preloader/through_association'
  38325. autoload :HasMany, 'active_record/associations/preloader/has_many'
  38326. autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through'
  38327. autoload :HasOne, 'active_record/associations/preloader/has_one'
  38328. autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through'
  38329. autoload :HasAndBelongsToMany, 'active_record/associations/preloader/has_and_belongs_to_many'
  38330. autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
  38331. end
  38332. attr_reader :records, :associations, :preload_scope, :model
  38333. # Eager loads the named associations for the given Active Record record(s).
  38334. #
  38335. # In this description, 'association name' shall refer to the name passed
  38336. # to an association creation method. For example, a model that specifies
  38337. # <tt>belongs_to :author</tt>, <tt>has_many :buyers</tt> has association
  38338. # names +:author+ and +:buyers+.
  38339. #
  38340. # == Parameters
  38341. # +records+ is an array of ActiveRecord::Base. This array needs not be flat,
  38342. # i.e. +records+ itself may also contain arrays of records. In any case,
  38343. # +preload_associations+ will preload the all associations records by
  38344. # flattening +records+.
  38345. #
  38346. # +associations+ specifies one or more associations that you want to
  38347. # preload. It may be:
  38348. # - a Symbol or a String which specifies a single association name. For
  38349. # example, specifying +:books+ allows this method to preload all books
  38350. # for an Author.
  38351. # - an Array which specifies multiple association names. This array
  38352. # is processed recursively. For example, specifying <tt>[:avatar, :books]</tt>
  38353. # allows this method to preload an author's avatar as well as all of his
  38354. # books.
  38355. # - a Hash which specifies multiple association names, as well as
  38356. # association names for the to-be-preloaded association objects. For
  38357. # example, specifying <tt>{ author: :avatar }</tt> will preload a
  38358. # book's author, as well as that author's avatar.
  38359. #
  38360. # +:associations+ has the same format as the +:include+ option for
  38361. # <tt>ActiveRecord::Base.find</tt>. So +associations+ could look like this:
  38362. #
  38363. # :books
  38364. # [ :books, :author ]
  38365. # { author: :avatar }
  38366. # [ :books, { author: :avatar } ]
  38367. def initialize(records, associations, preload_scope = nil)
  38368. @records = Array.wrap(records).compact.uniq
  38369. @associations = Array.wrap(associations)
  38370. @preload_scope = preload_scope || Relation.new(nil, nil)
  38371. end
  38372. def run
  38373. unless records.empty?
  38374. associations.each { |association| preload(association) }
  38375. end
  38376. end
  38377. private
  38378. def preload(association)
  38379. case association
  38380. when Hash
  38381. preload_hash(association)
  38382. when Symbol
  38383. preload_one(association)
  38384. when String
  38385. preload_one(association.to_sym)
  38386. else
  38387. raise ArgumentError, "#{association.inspect} was not recognised for preload"
  38388. end
  38389. end
  38390. def preload_hash(association)
  38391. association.each do |parent, child|
  38392. Preloader.new(records, parent, preload_scope).run
  38393. Preloader.new(records.map { |record| record.send(parent) }.flatten, child).run
  38394. end
  38395. end
  38396. # Not all records have the same class, so group then preload group on the reflection
  38397. # itself so that if various subclass share the same association then we do not split
  38398. # them unnecessarily
  38399. #
  38400. # Additionally, polymorphic belongs_to associations can have multiple associated
  38401. # classes, depending on the polymorphic_type field. So we group by the classes as
  38402. # well.
  38403. def preload_one(association)
  38404. grouped_records(association).each do |reflection, klasses|
  38405. klasses.each do |klass, records|
  38406. preloader_for(reflection).new(klass, records, reflection, preload_scope).run
  38407. end
  38408. end
  38409. end
  38410. def grouped_records(association)
  38411. Hash[
  38412. records_by_reflection(association).map do |reflection, records|
  38413. [reflection, records.group_by { |record| association_klass(reflection, record) }]
  38414. end
  38415. ]
  38416. end
  38417. def records_by_reflection(association)
  38418. records.group_by do |record|
  38419. reflection = record.class.reflections[association]
  38420. unless reflection
  38421. raise ActiveRecord::ConfigurationError, "Association named '#{association}' was not found; " \
  38422. "perhaps you misspelled it?"
  38423. end
  38424. reflection
  38425. end
  38426. end
  38427. def association_klass(reflection, record)
  38428. if reflection.macro == :belongs_to && reflection.options[:polymorphic]
  38429. klass = record.send(reflection.foreign_type)
  38430. klass && klass.constantize
  38431. else
  38432. reflection.klass
  38433. end
  38434. end
  38435. def preloader_for(reflection)
  38436. case reflection.macro
  38437. when :has_many
  38438. reflection.options[:through] ? HasManyThrough : HasMany
  38439. when :has_one
  38440. reflection.options[:through] ? HasOneThrough : HasOne
  38441. when :has_and_belongs_to_many
  38442. HasAndBelongsToMany
  38443. when :belongs_to
  38444. BelongsTo
  38445. end
  38446. end
  38447. end
  38448. end
  38449. end
  38450. module ActiveRecord
  38451. module Associations
  38452. class SingularAssociation < Association #:nodoc:
  38453. # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
  38454. def reader(force_reload = false)
  38455. if force_reload
  38456. klass.uncached { reload }
  38457. elsif !loaded? || stale_target?
  38458. reload
  38459. end
  38460. target
  38461. end
  38462. # Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
  38463. def writer(record)
  38464. replace(record)
  38465. end
  38466. def create(attributes = {}, &block)
  38467. create_record(attributes, &block)
  38468. end
  38469. def create!(attributes = {}, &block)
  38470. create_record(attributes, true, &block)
  38471. end
  38472. def build(attributes = {})
  38473. record = build_record(attributes)
  38474. yield(record) if block_given?
  38475. set_new_record(record)
  38476. record
  38477. end
  38478. private
  38479. def create_scope
  38480. scope.scope_for_create.stringify_keys.except(klass.primary_key)
  38481. end
  38482. def find_target
  38483. scope.first.tap { |record| set_inverse_instance(record) }
  38484. end
  38485. # Implemented by subclasses
  38486. def replace(record)
  38487. raise NotImplementedError, "Subclasses must implement a replace(record) method"
  38488. end
  38489. def set_new_record(record)
  38490. replace(record)
  38491. end
  38492. def create_record(attributes, raise_error = false)
  38493. record = build_record(attributes)
  38494. yield(record) if block_given?
  38495. saved = record.save
  38496. set_new_record(record)
  38497. raise RecordInvalid.new(record) if !saved && raise_error
  38498. record
  38499. end
  38500. end
  38501. end
  38502. end
  38503. module ActiveRecord
  38504. # = Active Record Through Association
  38505. module Associations
  38506. module ThroughAssociation #:nodoc:
  38507. delegate :source_reflection, :through_reflection, :chain, :to => :reflection
  38508. protected
  38509. # We merge in these scopes for two reasons:
  38510. #
  38511. # 1. To get the default_scope conditions for any of the other reflections in the chain
  38512. # 2. To get the type conditions for any STI models in the chain
  38513. def target_scope
  38514. scope = super
  38515. chain[1..-1].each do |reflection|
  38516. scope = scope.merge(
  38517. reflection.klass.all.with_default_scope.
  38518. except(:select, :create_with, :includes, :preload, :joins, :eager_load)
  38519. )
  38520. end
  38521. scope
  38522. end
  38523. private
  38524. # Construct attributes for :through pointing to owner and associate. This is used by the
  38525. # methods which create and delete records on the association.
  38526. #
  38527. # We only support indirectly modifying through associations which has a belongs_to source.
  38528. # This is the "has_many :tags, through: :taggings" situation, where the join model
  38529. # typically has a belongs_to on both side. In other words, associations which could also
  38530. # be represented as has_and_belongs_to_many associations.
  38531. #
  38532. # We do not support creating/deleting records on the association where the source has
  38533. # some other type, because this opens up a whole can of worms, and in basically any
  38534. # situation it is more natural for the user to just create or modify their join records
  38535. # directly as required.
  38536. def construct_join_attributes(*records)
  38537. ensure_mutable
  38538. join_attributes = {
  38539. source_reflection.foreign_key =>
  38540. records.map { |record|
  38541. record.send(source_reflection.association_primary_key(reflection.klass))
  38542. }
  38543. }
  38544. if options[:source_type]
  38545. join_attributes[source_reflection.foreign_type] =
  38546. records.map { |record| record.class.base_class.name }
  38547. end
  38548. if records.count == 1
  38549. Hash[join_attributes.map { |k, v| [k, v.first] }]
  38550. else
  38551. join_attributes
  38552. end
  38553. end
  38554. # Note: this does not capture all cases, for example it would be crazy to try to
  38555. # properly support stale-checking for nested associations.
  38556. def stale_state
  38557. if through_reflection.macro == :belongs_to
  38558. owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
  38559. end
  38560. end
  38561. def foreign_key_present?
  38562. through_reflection.macro == :belongs_to &&
  38563. !owner[through_reflection.foreign_key].nil?
  38564. end
  38565. def ensure_mutable
  38566. if source_reflection.macro != :belongs_to
  38567. raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
  38568. end
  38569. end
  38570. def ensure_not_nested
  38571. if reflection.nested?
  38572. raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
  38573. end
  38574. end
  38575. end
  38576. end
  38577. end
  38578. require 'active_support/core_ext/enumerable'
  38579. require 'active_support/core_ext/string/conversions'
  38580. require 'active_support/core_ext/module/remove_method'
  38581. require 'active_record/errors'
  38582. module ActiveRecord
  38583. class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
  38584. def initialize(reflection, associated_class = nil)
  38585. super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
  38586. end
  38587. end
  38588. class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
  38589. def initialize(owner_class_name, reflection)
  38590. super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
  38591. end
  38592. end
  38593. class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
  38594. def initialize(owner_class_name, reflection, source_reflection)
  38595. super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
  38596. end
  38597. end
  38598. class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
  38599. def initialize(owner_class_name, reflection)
  38600. super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
  38601. end
  38602. end
  38603. class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
  38604. def initialize(owner_class_name, reflection, source_reflection)
  38605. super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
  38606. end
  38607. end
  38608. class HasOneThroughCantAssociateThroughCollection < ActiveRecordError #:nodoc:
  38609. def initialize(owner_class_name, reflection, through_reflection)
  38610. super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
  38611. end
  38612. end
  38613. class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
  38614. def initialize(reflection)
  38615. through_reflection = reflection.through_reflection
  38616. source_reflection_names = reflection.source_reflection_names
  38617. source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
  38618. super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
  38619. end
  38620. end
  38621. class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc:
  38622. def initialize(owner, reflection)
  38623. super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
  38624. end
  38625. end
  38626. class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
  38627. def initialize(owner, reflection)
  38628. super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
  38629. end
  38630. end
  38631. class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
  38632. def initialize(owner, reflection)
  38633. super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
  38634. end
  38635. end
  38636. class HasManyThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc:
  38637. def initialize(owner, reflection)
  38638. super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
  38639. end
  38640. end
  38641. class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
  38642. def initialize(reflection)
  38643. super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
  38644. end
  38645. end
  38646. class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
  38647. def initialize(reflection)
  38648. super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
  38649. end
  38650. end
  38651. class ReadOnlyAssociation < ActiveRecordError #:nodoc:
  38652. def initialize(reflection)
  38653. super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
  38654. end
  38655. end
  38656. # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
  38657. # (has_many, has_one) when there is at least 1 child associated instance.
  38658. # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
  38659. class DeleteRestrictionError < ActiveRecordError #:nodoc:
  38660. def initialize(name)
  38661. super("Cannot delete record because of dependent #{name}")
  38662. end
  38663. end
  38664. # See ActiveRecord::Associations::ClassMethods for documentation.
  38665. module Associations # :nodoc:
  38666. extend ActiveSupport::Autoload
  38667. extend ActiveSupport::Concern
  38668. # These classes will be loaded when associations are created.
  38669. # So there is no need to eager load them.
  38670. autoload :Association, 'active_record/associations/association'
  38671. autoload :SingularAssociation, 'active_record/associations/singular_association'
  38672. autoload :CollectionAssociation, 'active_record/associations/collection_association'
  38673. autoload :CollectionProxy, 'active_record/associations/collection_proxy'
  38674. autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
  38675. autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association'
  38676. autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association'
  38677. autoload :HasManyAssociation, 'active_record/associations/has_many_association'
  38678. autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association'
  38679. autoload :HasOneAssociation, 'active_record/associations/has_one_association'
  38680. autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
  38681. autoload :ThroughAssociation, 'active_record/associations/through_association'
  38682. module Builder #:nodoc:
  38683. autoload :Association, 'active_record/associations/builder/association'
  38684. autoload :SingularAssociation, 'active_record/associations/builder/singular_association'
  38685. autoload :CollectionAssociation, 'active_record/associations/builder/collection_association'
  38686. autoload :BelongsTo, 'active_record/associations/builder/belongs_to'
  38687. autoload :HasOne, 'active_record/associations/builder/has_one'
  38688. autoload :HasMany, 'active_record/associations/builder/has_many'
  38689. autoload :HasAndBelongsToMany, 'active_record/associations/builder/has_and_belongs_to_many'
  38690. end
  38691. eager_autoload do
  38692. autoload :Preloader, 'active_record/associations/preloader'
  38693. autoload :JoinDependency, 'active_record/associations/join_dependency'
  38694. autoload :AssociationScope, 'active_record/associations/association_scope'
  38695. autoload :AliasTracker, 'active_record/associations/alias_tracker'
  38696. autoload :JoinHelper, 'active_record/associations/join_helper'
  38697. end
  38698. # Clears out the association cache.
  38699. def clear_association_cache #:nodoc:
  38700. @association_cache.clear if persisted?
  38701. end
  38702. # :nodoc:
  38703. attr_reader :association_cache
  38704. # Returns the association instance for the given name, instantiating it if it doesn't already exist
  38705. def association(name) #:nodoc:
  38706. association = association_instance_get(name)
  38707. if association.nil?
  38708. reflection = self.class.reflect_on_association(name)
  38709. association = reflection.association_class.new(self, reflection)
  38710. association_instance_set(name, association)
  38711. end
  38712. association
  38713. end
  38714. private
  38715. # Returns the specified association instance if it responds to :loaded?, nil otherwise.
  38716. def association_instance_get(name)
  38717. @association_cache[name.to_sym]
  38718. end
  38719. # Set the specified association instance.
  38720. def association_instance_set(name, association)
  38721. @association_cache[name] = association
  38722. end
  38723. # Associations are a set of macro-like class methods for tying objects together through
  38724. # foreign keys. They express relationships like "Project has one Project Manager"
  38725. # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
  38726. # class which are specialized according to the collection or association symbol and the
  38727. # options hash. It works much the same way as Ruby's own <tt>attr*</tt>
  38728. # methods.
  38729. #
  38730. # class Project < ActiveRecord::Base
  38731. # belongs_to :portfolio
  38732. # has_one :project_manager
  38733. # has_many :milestones
  38734. # has_and_belongs_to_many :categories
  38735. # end
  38736. #
  38737. # The project class now has the following methods (and more) to ease the traversal and
  38738. # manipulation of its relationships:
  38739. # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
  38740. # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
  38741. # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
  38742. # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt>
  38743. # <tt>Project#milestones.build, Project#milestones.create</tt>
  38744. # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
  38745. # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
  38746. #
  38747. # === A word of warning
  38748. #
  38749. # Don't create associations that have the same name as instance methods of
  38750. # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
  38751. # its model, it will override the inherited method and break things.
  38752. # For instance, +attributes+ and +connection+ would be bad choices for association names.
  38753. #
  38754. # == Auto-generated methods
  38755. #
  38756. # === Singular associations (one-to-one)
  38757. # | | belongs_to |
  38758. # generated methods | belongs_to | :polymorphic | has_one
  38759. # ----------------------------------+------------+--------------+---------
  38760. # other | X | X | X
  38761. # other=(other) | X | X | X
  38762. # build_other(attributes={}) | X | | X
  38763. # create_other(attributes={}) | X | | X
  38764. # create_other!(attributes={}) | X | | X
  38765. #
  38766. # ===Collection associations (one-to-many / many-to-many)
  38767. # | | | has_many
  38768. # generated methods | habtm | has_many | :through
  38769. # ----------------------------------+-------+----------+----------
  38770. # others | X | X | X
  38771. # others=(other,other,...) | X | X | X
  38772. # other_ids | X | X | X
  38773. # other_ids=(id,id,...) | X | X | X
  38774. # others<< | X | X | X
  38775. # others.push | X | X | X
  38776. # others.concat | X | X | X
  38777. # others.build(attributes={}) | X | X | X
  38778. # others.create(attributes={}) | X | X | X
  38779. # others.create!(attributes={}) | X | X | X
  38780. # others.size | X | X | X
  38781. # others.length | X | X | X
  38782. # others.count | X | X | X
  38783. # others.sum(*args) | X | X | X
  38784. # others.empty? | X | X | X
  38785. # others.clear | X | X | X
  38786. # others.delete(other,other,...) | X | X | X
  38787. # others.delete_all | X | X | X
  38788. # others.destroy(other,other,...) | X | X | X
  38789. # others.destroy_all | X | X | X
  38790. # others.find(*args) | X | X | X
  38791. # others.exists? | X | X | X
  38792. # others.uniq | X | X | X
  38793. # others.reset | X | X | X
  38794. #
  38795. # === Overriding generated methods
  38796. #
  38797. # Association methods are generated in a module that is included into the model class,
  38798. # which allows you to easily override with your own methods and call the original
  38799. # generated method with +super+. For example:
  38800. #
  38801. # class Car < ActiveRecord::Base
  38802. # belongs_to :owner
  38803. # belongs_to :old_owner
  38804. # def owner=(new_owner)
  38805. # self.old_owner = self.owner
  38806. # super
  38807. # end
  38808. # end
  38809. #
  38810. # If your model class is <tt>Project</tt>, the module is
  38811. # named <tt>Project::GeneratedFeatureMethods</tt>. The GeneratedFeatureMethods module is
  38812. # included in the model class immediately after the (anonymous) generated attributes methods
  38813. # module, meaning an association will override the methods for an attribute with the same name.
  38814. #
  38815. # == Cardinality and associations
  38816. #
  38817. # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
  38818. # relationships between models. Each model uses an association to describe its role in
  38819. # the relation. The +belongs_to+ association is always used in the model that has
  38820. # the foreign key.
  38821. #
  38822. # === One-to-one
  38823. #
  38824. # Use +has_one+ in the base, and +belongs_to+ in the associated model.
  38825. #
  38826. # class Employee < ActiveRecord::Base
  38827. # has_one :office
  38828. # end
  38829. # class Office < ActiveRecord::Base
  38830. # belongs_to :employee # foreign key - employee_id
  38831. # end
  38832. #
  38833. # === One-to-many
  38834. #
  38835. # Use +has_many+ in the base, and +belongs_to+ in the associated model.
  38836. #
  38837. # class Manager < ActiveRecord::Base
  38838. # has_many :employees
  38839. # end
  38840. # class Employee < ActiveRecord::Base
  38841. # belongs_to :manager # foreign key - manager_id
  38842. # end
  38843. #
  38844. # === Many-to-many
  38845. #
  38846. # There are two ways to build a many-to-many relationship.
  38847. #
  38848. # The first way uses a +has_many+ association with the <tt>:through</tt> option and a join model, so
  38849. # there are two stages of associations.
  38850. #
  38851. # class Assignment < ActiveRecord::Base
  38852. # belongs_to :programmer # foreign key - programmer_id
  38853. # belongs_to :project # foreign key - project_id
  38854. # end
  38855. # class Programmer < ActiveRecord::Base
  38856. # has_many :assignments
  38857. # has_many :projects, through: :assignments
  38858. # end
  38859. # class Project < ActiveRecord::Base
  38860. # has_many :assignments
  38861. # has_many :programmers, through: :assignments
  38862. # end
  38863. #
  38864. # For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table
  38865. # that has no corresponding model or primary key.
  38866. #
  38867. # class Programmer < ActiveRecord::Base
  38868. # has_and_belongs_to_many :projects # foreign keys in the join table
  38869. # end
  38870. # class Project < ActiveRecord::Base
  38871. # has_and_belongs_to_many :programmers # foreign keys in the join table
  38872. # end
  38873. #
  38874. # Choosing which way to build a many-to-many relationship is not always simple.
  38875. # If you need to work with the relationship model as its own entity,
  38876. # use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when
  38877. # you never work directly with the relationship itself.
  38878. #
  38879. # == Is it a +belongs_to+ or +has_one+ association?
  38880. #
  38881. # Both express a 1-1 relationship. The difference is mostly where to place the foreign
  38882. # key, which goes on the table for the class declaring the +belongs_to+ relationship.
  38883. #
  38884. # class User < ActiveRecord::Base
  38885. # # I reference an account.
  38886. # belongs_to :account
  38887. # end
  38888. #
  38889. # class Account < ActiveRecord::Base
  38890. # # One user references me.
  38891. # has_one :user
  38892. # end
  38893. #
  38894. # The tables for these classes could look something like:
  38895. #
  38896. # CREATE TABLE users (
  38897. # id int(11) NOT NULL auto_increment,
  38898. # account_id int(11) default NULL,
  38899. # name varchar default NULL,
  38900. # PRIMARY KEY (id)
  38901. # )
  38902. #
  38903. # CREATE TABLE accounts (
  38904. # id int(11) NOT NULL auto_increment,
  38905. # name varchar default NULL,
  38906. # PRIMARY KEY (id)
  38907. # )
  38908. #
  38909. # == Unsaved objects and associations
  38910. #
  38911. # You can manipulate objects and associations before they are saved to the database, but
  38912. # there is some special behavior you should be aware of, mostly involving the saving of
  38913. # associated objects.
  38914. #
  38915. # You can set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
  38916. # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
  38917. # to +true+ will _always_ save the members, whereas setting it to +false+ will
  38918. # _never_ save the members. More details about :autosave option is available at
  38919. # autosave_association.rb .
  38920. #
  38921. # === One-to-one associations
  38922. #
  38923. # * Assigning an object to a +has_one+ association automatically saves that object and
  38924. # the object being replaced (if there is one), in order to update their foreign
  38925. # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
  38926. # * If either of these saves fail (due to one of the objects being invalid), an
  38927. # <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
  38928. # cancelled.
  38929. # * If you wish to assign an object to a +has_one+ association without saving it,
  38930. # use the <tt>build_association</tt> method (documented below). The object being
  38931. # replaced will still be saved to update its foreign key.
  38932. # * Assigning an object to a +belongs_to+ association does not save the object, since
  38933. # the foreign key field belongs on the parent. It does not save the parent either.
  38934. #
  38935. # === Collections
  38936. #
  38937. # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically
  38938. # saves that object, except if the parent object (the owner of the collection) is not yet
  38939. # stored in the database.
  38940. # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
  38941. # fails, then <tt>push</tt> returns +false+.
  38942. # * If saving fails while replacing the collection (via <tt>association=</tt>), an
  38943. # <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
  38944. # cancelled.
  38945. # * You can add an object to a collection without automatically saving it by using the
  38946. # <tt>collection.build</tt> method (documented below).
  38947. # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
  38948. # saved when the parent is saved.
  38949. #
  38950. # == Customizing the query
  38951. #
  38952. # Associations are built from <tt>Relation</tt>s, and you can use the <tt>Relation</tt> syntax
  38953. # to customize them. For example, to add a condition:
  38954. #
  38955. # class Blog < ActiveRecord::Base
  38956. # has_many :published_posts, -> { where published: true }, class_name: 'Post'
  38957. # end
  38958. #
  38959. # Inside the <tt>-> { ... }</tt> block you can use all of the usual <tt>Relation</tt> methods.
  38960. #
  38961. # === Accessing the owner object
  38962. #
  38963. # Sometimes it is useful to have access to the owner object when building the query. The owner
  38964. # is passed as a parameter to the block. For example, the following association would find all
  38965. # events that occur on the user's birthday:
  38966. #
  38967. # class User < ActiveRecord::Base
  38968. # has_many :birthday_events, ->(user) { where starts_on: user.birthday }, class_name: 'Event'
  38969. # end
  38970. #
  38971. # == Association callbacks
  38972. #
  38973. # Similar to the normal callbacks that hook into the life cycle of an Active Record object,
  38974. # you can also define callbacks that get triggered when you add an object to or remove an
  38975. # object from an association collection.
  38976. #
  38977. # class Project
  38978. # has_and_belongs_to_many :developers, after_add: :evaluate_velocity
  38979. #
  38980. # def evaluate_velocity(developer)
  38981. # ...
  38982. # end
  38983. # end
  38984. #
  38985. # It's possible to stack callbacks by passing them as an array. Example:
  38986. #
  38987. # class Project
  38988. # has_and_belongs_to_many :developers,
  38989. # after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
  38990. # end
  38991. #
  38992. # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
  38993. #
  38994. # Should any of the +before_add+ callbacks throw an exception, the object does not get
  38995. # added to the collection. Same with the +before_remove+ callbacks; if an exception is
  38996. # thrown the object doesn't get removed.
  38997. #
  38998. # == Association extensions
  38999. #
  39000. # The proxy objects that control the access to associations can be extended through anonymous
  39001. # modules. This is especially beneficial for adding new finders, creators, and other
  39002. # factory-type methods that are only used as part of this association.
  39003. #
  39004. # class Account < ActiveRecord::Base
  39005. # has_many :people do
  39006. # def find_or_create_by_name(name)
  39007. # first_name, last_name = name.split(" ", 2)
  39008. # find_or_create_by(first_name: first_name, last_name: last_name)
  39009. # end
  39010. # end
  39011. # end
  39012. #
  39013. # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
  39014. # person.first_name # => "David"
  39015. # person.last_name # => "Heinemeier Hansson"
  39016. #
  39017. # If you need to share the same extensions between many associations, you can use a named
  39018. # extension module.
  39019. #
  39020. # module FindOrCreateByNameExtension
  39021. # def find_or_create_by_name(name)
  39022. # first_name, last_name = name.split(" ", 2)
  39023. # find_or_create_by(first_name: first_name, last_name: last_name)
  39024. # end
  39025. # end
  39026. #
  39027. # class Account < ActiveRecord::Base
  39028. # has_many :people, -> { extending FindOrCreateByNameExtension }
  39029. # end
  39030. #
  39031. # class Company < ActiveRecord::Base
  39032. # has_many :people, -> { extending FindOrCreateByNameExtension }
  39033. # end
  39034. #
  39035. # Some extensions can only be made to work with knowledge of the association's internals.
  39036. # Extensions can access relevant state using the following methods (where +items+ is the
  39037. # name of the association):
  39038. #
  39039. # * <tt>record.association(:items).owner</tt> - Returns the object the association is part of.
  39040. # * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association.
  39041. # * <tt>record.association(:items).target</tt> - Returns the associated object for +belongs_to+ and +has_one+, or
  39042. # the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
  39043. #
  39044. # However, inside the actual extension code, you will not have access to the <tt>record</tt> as
  39045. # above. In this case, you can access <tt>proxy_association</tt>. For example,
  39046. # <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return
  39047. # the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside
  39048. # association extensions.
  39049. #
  39050. # == Association Join Models
  39051. #
  39052. # Has Many associations can be configured with the <tt>:through</tt> option to use an
  39053. # explicit join model to retrieve the data. This operates similarly to a
  39054. # +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
  39055. # callbacks, and extra attributes on the join model. Consider the following schema:
  39056. #
  39057. # class Author < ActiveRecord::Base
  39058. # has_many :authorships
  39059. # has_many :books, through: :authorships
  39060. # end
  39061. #
  39062. # class Authorship < ActiveRecord::Base
  39063. # belongs_to :author
  39064. # belongs_to :book
  39065. # end
  39066. #
  39067. # @author = Author.first
  39068. # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
  39069. # @author.books # selects all books by using the Authorship join model
  39070. #
  39071. # You can also go through a +has_many+ association on the join model:
  39072. #
  39073. # class Firm < ActiveRecord::Base
  39074. # has_many :clients
  39075. # has_many :invoices, through: :clients
  39076. # end
  39077. #
  39078. # class Client < ActiveRecord::Base
  39079. # belongs_to :firm
  39080. # has_many :invoices
  39081. # end
  39082. #
  39083. # class Invoice < ActiveRecord::Base
  39084. # belongs_to :client
  39085. # end
  39086. #
  39087. # @firm = Firm.first
  39088. # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
  39089. # @firm.invoices # selects all invoices by going through the Client join model
  39090. #
  39091. # Similarly you can go through a +has_one+ association on the join model:
  39092. #
  39093. # class Group < ActiveRecord::Base
  39094. # has_many :users
  39095. # has_many :avatars, through: :users
  39096. # end
  39097. #
  39098. # class User < ActiveRecord::Base
  39099. # belongs_to :group
  39100. # has_one :avatar
  39101. # end
  39102. #
  39103. # class Avatar < ActiveRecord::Base
  39104. # belongs_to :user
  39105. # end
  39106. #
  39107. # @group = Group.first
  39108. # @group.users.collect { |u| u.avatar }.compact # select all avatars for all users in the group
  39109. # @group.avatars # selects all avatars by going through the User join model.
  39110. #
  39111. # An important caveat with going through +has_one+ or +has_many+ associations on the
  39112. # join model is that these associations are *read-only*. For example, the following
  39113. # would not work following the previous example:
  39114. #
  39115. # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
  39116. # @group.avatars.delete(@group.avatars.last) # so would this
  39117. #
  39118. # If you are using a +belongs_to+ on the join model, it is a good idea to set the
  39119. # <tt>:inverse_of</tt> option on the +belongs_to+, which will mean that the following example
  39120. # works correctly (where <tt>tags</tt> is a +has_many+ <tt>:through</tt> association):
  39121. #
  39122. # @post = Post.first
  39123. # @tag = @post.tags.build name: "ruby"
  39124. # @tag.save
  39125. #
  39126. # The last line ought to save the through record (a <tt>Taggable</tt>). This will only work if the
  39127. # <tt>:inverse_of</tt> is set:
  39128. #
  39129. # class Taggable < ActiveRecord::Base
  39130. # belongs_to :post
  39131. # belongs_to :tag, inverse_of: :taggings
  39132. # end
  39133. #
  39134. # == Nested Associations
  39135. #
  39136. # You can actually specify *any* association with the <tt>:through</tt> option, including an
  39137. # association which has a <tt>:through</tt> option itself. For example:
  39138. #
  39139. # class Author < ActiveRecord::Base
  39140. # has_many :posts
  39141. # has_many :comments, through: :posts
  39142. # has_many :commenters, through: :comments
  39143. # end
  39144. #
  39145. # class Post < ActiveRecord::Base
  39146. # has_many :comments
  39147. # end
  39148. #
  39149. # class Comment < ActiveRecord::Base
  39150. # belongs_to :commenter
  39151. # end
  39152. #
  39153. # @author = Author.first
  39154. # @author.commenters # => People who commented on posts written by the author
  39155. #
  39156. # An equivalent way of setting up this association this would be:
  39157. #
  39158. # class Author < ActiveRecord::Base
  39159. # has_many :posts
  39160. # has_many :commenters, through: :posts
  39161. # end
  39162. #
  39163. # class Post < ActiveRecord::Base
  39164. # has_many :comments
  39165. # has_many :commenters, through: :comments
  39166. # end
  39167. #
  39168. # class Comment < ActiveRecord::Base
  39169. # belongs_to :commenter
  39170. # end
  39171. #
  39172. # When using nested association, you will not be able to modify the association because there
  39173. # is not enough information to know what modification to make. For example, if you tried to
  39174. # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
  39175. # intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
  39176. #
  39177. # == Polymorphic Associations
  39178. #
  39179. # Polymorphic associations on models are not restricted on what types of models they
  39180. # can be associated with. Rather, they specify an interface that a +has_many+ association
  39181. # must adhere to.
  39182. #
  39183. # class Asset < ActiveRecord::Base
  39184. # belongs_to :attachable, polymorphic: true
  39185. # end
  39186. #
  39187. # class Post < ActiveRecord::Base
  39188. # has_many :assets, as: :attachable # The :as option specifies the polymorphic interface to use.
  39189. # end
  39190. #
  39191. # @asset.attachable = @post
  39192. #
  39193. # This works by using a type column in addition to a foreign key to specify the associated
  39194. # record. In the Asset example, you'd need an +attachable_id+ integer column and an
  39195. # +attachable_type+ string column.
  39196. #
  39197. # Using polymorphic associations in combination with single table inheritance (STI) is
  39198. # a little tricky. In order for the associations to work as expected, ensure that you
  39199. # store the base model for the STI models in the type column of the polymorphic
  39200. # association. To continue with the asset example above, suppose there are guest posts
  39201. # and member posts that use the posts table for STI. In this case, there must be a +type+
  39202. # column in the posts table.
  39203. #
  39204. # class Asset < ActiveRecord::Base
  39205. # belongs_to :attachable, polymorphic: true
  39206. #
  39207. # def attachable_type=(sType)
  39208. # super(sType.to_s.classify.constantize.base_class.to_s)
  39209. # end
  39210. # end
  39211. #
  39212. # class Post < ActiveRecord::Base
  39213. # # because we store "Post" in attachable_type now dependent: :destroy will work
  39214. # has_many :assets, as: :attachable, dependent: :destroy
  39215. # end
  39216. #
  39217. # class GuestPost < Post
  39218. # end
  39219. #
  39220. # class MemberPost < Post
  39221. # end
  39222. #
  39223. # == Caching
  39224. #
  39225. # All of the methods are built on a simple caching principle that will keep the result
  39226. # of the last query around unless specifically instructed not to. The cache is even
  39227. # shared across methods to make it even cheaper to use the macro-added methods without
  39228. # worrying too much about performance at the first go.
  39229. #
  39230. # project.milestones # fetches milestones from the database
  39231. # project.milestones.size # uses the milestone cache
  39232. # project.milestones.empty? # uses the milestone cache
  39233. # project.milestones(true).size # fetches milestones from the database
  39234. # project.milestones # uses the milestone cache
  39235. #
  39236. # == Eager loading of associations
  39237. #
  39238. # Eager loading is a way to find objects of a certain class and a number of named associations.
  39239. # This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100
  39240. # posts that each need to display their author triggers 101 database queries. Through the
  39241. # use of eager loading, the 101 queries can be reduced to 2.
  39242. #
  39243. # class Post < ActiveRecord::Base
  39244. # belongs_to :author
  39245. # has_many :comments
  39246. # end
  39247. #
  39248. # Consider the following loop using the class above:
  39249. #
  39250. # Post.all.each do |post|
  39251. # puts "Post: " + post.title
  39252. # puts "Written by: " + post.author.name
  39253. # puts "Last comment on: " + post.comments.first.created_on
  39254. # end
  39255. #
  39256. # To iterate over these one hundred posts, we'll generate 201 database queries. Let's
  39257. # first just optimize it for retrieving the author:
  39258. #
  39259. # Post.includes(:author).each do |post|
  39260. #
  39261. # This references the name of the +belongs_to+ association that also used the <tt>:author</tt>
  39262. # symbol. After loading the posts, find will collect the +author_id+ from each one and load
  39263. # all the referenced authors with one query. Doing so will cut down the number of queries
  39264. # from 201 to 102.
  39265. #
  39266. # We can improve upon the situation further by referencing both associations in the finder with:
  39267. #
  39268. # Post.includes(:author, :comments).each do |post|
  39269. #
  39270. # This will load all comments with a single query. This reduces the total number of queries
  39271. # to 3. More generally the number of queries will be 1 plus the number of associations
  39272. # named (except if some of the associations are polymorphic +belongs_to+ - see below).
  39273. #
  39274. # To include a deep hierarchy of associations, use a hash:
  39275. #
  39276. # Post.includes(:author, {comments: {author: :gravatar}}).each do |post|
  39277. #
  39278. # That'll grab not only all the comments but all their authors and gravatar pictures.
  39279. # You can mix and match symbols, arrays and hashes in any combination to describe the
  39280. # associations you want to load.
  39281. #
  39282. # All of this power shouldn't fool you into thinking that you can pull out huge amounts
  39283. # of data with no performance penalty just because you've reduced the number of queries.
  39284. # The database still needs to send all the data to Active Record and it still needs to
  39285. # be processed. So it's no catch-all for performance problems, but it's a great way to
  39286. # cut down on the number of queries in a situation as the one described above.
  39287. #
  39288. # Since only one table is loaded at a time, conditions or orders cannot reference tables
  39289. # other than the main one. If this is the case Active Record falls back to the previously
  39290. # used LEFT OUTER JOIN based strategy. For example
  39291. #
  39292. # Post.includes([:author, :comments]).where(['comments.approved = ?', true])
  39293. #
  39294. # This will result in a single SQL query with joins along the lines of:
  39295. # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
  39296. # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
  39297. # like this can have unintended consequences.
  39298. # In the above example posts with no approved comments are not returned at all, because
  39299. # the conditions apply to the SQL statement as a whole and not just to the association.
  39300. # You must disambiguate column references for this fallback to happen, for example
  39301. # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
  39302. #
  39303. # If you do want eager load only some members of an association it is usually more natural
  39304. # to include an association which has conditions defined on it:
  39305. #
  39306. # class Post < ActiveRecord::Base
  39307. # has_many :approved_comments, -> { where approved: true }, class_name: 'Comment'
  39308. # end
  39309. #
  39310. # Post.includes(:approved_comments)
  39311. #
  39312. # This will load posts and eager load the +approved_comments+ association, which contains
  39313. # only those comments that have been approved.
  39314. #
  39315. # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
  39316. # returning all the associated objects:
  39317. #
  39318. # class Picture < ActiveRecord::Base
  39319. # has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment'
  39320. # end
  39321. #
  39322. # Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments.
  39323. #
  39324. # Eager loading is supported with polymorphic associations.
  39325. #
  39326. # class Address < ActiveRecord::Base
  39327. # belongs_to :addressable, polymorphic: true
  39328. # end
  39329. #
  39330. # A call that tries to eager load the addressable model
  39331. #
  39332. # Address.includes(:addressable)
  39333. #
  39334. # This will execute one query to load the addresses and load the addressables with one
  39335. # query per addressable type.
  39336. # For example if all the addressables are either of class Person or Company then a total
  39337. # of 3 queries will be executed. The list of addressable types to load is determined on
  39338. # the back of the addresses loaded. This is not supported if Active Record has to fallback
  39339. # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
  39340. # The reason is that the parent model's type is a column value so its corresponding table
  39341. # name cannot be put in the +FROM+/+JOIN+ clauses of that query.
  39342. #
  39343. # == Table Aliasing
  39344. #
  39345. # Active Record uses table aliasing in the case that a table is referenced multiple times
  39346. # in a join. If a table is referenced only once, the standard table name is used. The
  39347. # second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
  39348. # Indexes are appended for any more successive uses of the table name.
  39349. #
  39350. # Post.joins(:comments)
  39351. # # => SELECT ... FROM posts INNER JOIN comments ON ...
  39352. # Post.joins(:special_comments) # STI
  39353. # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
  39354. # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
  39355. # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
  39356. #
  39357. # Acts as tree example:
  39358. #
  39359. # TreeMixin.joins(:children)
  39360. # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
  39361. # TreeMixin.joins(children: :parent)
  39362. # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
  39363. # INNER JOIN parents_mixins ...
  39364. # TreeMixin.joins(children: {parent: :children})
  39365. # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
  39366. # INNER JOIN parents_mixins ...
  39367. # INNER JOIN mixins childrens_mixins_2
  39368. #
  39369. # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
  39370. #
  39371. # Post.joins(:categories)
  39372. # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
  39373. # Post.joins(categories: :posts)
  39374. # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
  39375. # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
  39376. # Post.joins(categories: {posts: :categories})
  39377. # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
  39378. # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
  39379. # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
  39380. #
  39381. # If you wish to specify your own custom joins using <tt>joins</tt> method, those table
  39382. # names will take precedence over the eager associations:
  39383. #
  39384. # Post.joins(:comments).joins("inner join comments ...")
  39385. # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
  39386. # Post.joins(:comments, :special_comments).joins("inner join comments ...")
  39387. # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
  39388. # INNER JOIN comments special_comments_posts ...
  39389. # INNER JOIN comments ...
  39390. #
  39391. # Table aliases are automatically truncated according to the maximum length of table identifiers
  39392. # according to the specific database.
  39393. #
  39394. # == Modules
  39395. #
  39396. # By default, associations will look for objects within the current module scope. Consider:
  39397. #
  39398. # module MyApplication
  39399. # module Business
  39400. # class Firm < ActiveRecord::Base
  39401. # has_many :clients
  39402. # end
  39403. #
  39404. # class Client < ActiveRecord::Base; end
  39405. # end
  39406. # end
  39407. #
  39408. # When <tt>Firm#clients</tt> is called, it will in turn call
  39409. # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
  39410. # If you want to associate with a class in another module scope, this can be done by
  39411. # specifying the complete class name.
  39412. #
  39413. # module MyApplication
  39414. # module Business
  39415. # class Firm < ActiveRecord::Base; end
  39416. # end
  39417. #
  39418. # module Billing
  39419. # class Account < ActiveRecord::Base
  39420. # belongs_to :firm, class_name: "MyApplication::Business::Firm"
  39421. # end
  39422. # end
  39423. # end
  39424. #
  39425. # == Bi-directional associations
  39426. #
  39427. # When you specify an association there is usually an association on the associated model
  39428. # that specifies the same relationship in reverse. For example, with the following models:
  39429. #
  39430. # class Dungeon < ActiveRecord::Base
  39431. # has_many :traps
  39432. # has_one :evil_wizard
  39433. # end
  39434. #
  39435. # class Trap < ActiveRecord::Base
  39436. # belongs_to :dungeon
  39437. # end
  39438. #
  39439. # class EvilWizard < ActiveRecord::Base
  39440. # belongs_to :dungeon
  39441. # end
  39442. #
  39443. # The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are
  39444. # the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+
  39445. # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
  39446. # Active Record doesn't know anything about these inverse relationships and so no object
  39447. # loading optimization is possible. For example:
  39448. #
  39449. # d = Dungeon.first
  39450. # t = d.traps.first
  39451. # d.level == t.dungeon.level # => true
  39452. # d.level = 10
  39453. # d.level == t.dungeon.level # => false
  39454. #
  39455. # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
  39456. # the same object data from the database, but are actually different in-memory copies
  39457. # of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
  39458. # Active Record about inverse relationships and it will optimise object loading. For
  39459. # example, if we changed our model definitions to:
  39460. #
  39461. # class Dungeon < ActiveRecord::Base
  39462. # has_many :traps, inverse_of: :dungeon
  39463. # has_one :evil_wizard, inverse_of: :dungeon
  39464. # end
  39465. #
  39466. # class Trap < ActiveRecord::Base
  39467. # belongs_to :dungeon, inverse_of: :traps
  39468. # end
  39469. #
  39470. # class EvilWizard < ActiveRecord::Base
  39471. # belongs_to :dungeon, inverse_of: :evil_wizard
  39472. # end
  39473. #
  39474. # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same
  39475. # in-memory instance and our final <tt>d.level == t.dungeon.level</tt> will return +true+.
  39476. #
  39477. # There are limitations to <tt>:inverse_of</tt> support:
  39478. #
  39479. # * does not work with <tt>:through</tt> associations.
  39480. # * does not work with <tt>:polymorphic</tt> associations.
  39481. # * for +belongs_to+ associations +has_many+ inverse associations are ignored.
  39482. #
  39483. # == Deleting from associations
  39484. #
  39485. # === Dependent associations
  39486. #
  39487. # +has_many+, +has_one+ and +belongs_to+ associations support the <tt>:dependent</tt> option.
  39488. # This allows you to specify that associated records should be deleted when the owner is
  39489. # deleted.
  39490. #
  39491. # For example:
  39492. #
  39493. # class Author
  39494. # has_many :posts, dependent: :destroy
  39495. # end
  39496. # Author.find(1).destroy # => Will destroy all of the author's posts, too
  39497. #
  39498. # The <tt>:dependent</tt> option can have different values which specify how the deletion
  39499. # is done. For more information, see the documentation for this option on the different
  39500. # specific association types. When no option is given, the behavior is to do nothing
  39501. # with the associated records when destroying a record.
  39502. #
  39503. # Note that <tt>:dependent</tt> is implemented using Rails' callback
  39504. # system, which works by processing callbacks in order. Therefore, other
  39505. # callbacks declared either before or after the <tt>:dependent</tt> option
  39506. # can affect what it does.
  39507. #
  39508. # === Delete or destroy?
  39509. #
  39510. # +has_many+ and +has_and_belongs_to_many+ associations have the methods <tt>destroy</tt>,
  39511. # <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>.
  39512. #
  39513. # For +has_and_belongs_to_many+, <tt>delete</tt> and <tt>destroy</tt> are the same: they
  39514. # cause the records in the join table to be removed.
  39515. #
  39516. # For +has_many+, <tt>destroy</tt> will always call the <tt>destroy</tt> method of the
  39517. # record(s) being removed so that callbacks are run. However <tt>delete</tt> will either
  39518. # do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
  39519. # if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
  39520. # The default strategy is <tt>:nullify</tt> (set the foreign keys to <tt>nil</tt>), except for
  39521. # +has_many+ <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete
  39522. # the join records, without running their callbacks).
  39523. #
  39524. # There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, except that
  39525. # it returns the association rather than the records which have been deleted.
  39526. #
  39527. # === What gets deleted?
  39528. #
  39529. # There is a potential pitfall here: +has_and_belongs_to_many+ and +has_many+ <tt>:through</tt>
  39530. # associations have records in join tables, as well as the associated records. So when we
  39531. # call one of these deletion methods, what exactly should be deleted?
  39532. #
  39533. # The answer is that it is assumed that deletion on an association is about removing the
  39534. # <i>link</i> between the owner and the associated object(s), rather than necessarily the
  39535. # associated objects themselves. So with +has_and_belongs_to_many+ and +has_many+
  39536. # <tt>:through</tt>, the join records will be deleted, but the associated records won't.
  39537. #
  39538. # This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.find_by_name('food'))</tt>
  39539. # you would want the 'food' tag to be unlinked from the post, rather than for the tag itself
  39540. # to be removed from the database.
  39541. #
  39542. # However, there are examples where this strategy doesn't make sense. For example, suppose
  39543. # a person has many projects, and each project has many tasks. If we deleted one of a person's
  39544. # tasks, we would probably not want the project to be deleted. In this scenario, the delete method
  39545. # won't actually work: it can only be used if the association on the join model is a
  39546. # +belongs_to+. In other situations you are expected to perform operations directly on
  39547. # either the associated records or the <tt>:through</tt> association.
  39548. #
  39549. # With a regular +has_many+ there is no distinction between the "associated records"
  39550. # and the "link", so there is only one choice for what gets deleted.
  39551. #
  39552. # With +has_and_belongs_to_many+ and +has_many+ <tt>:through</tt>, if you want to delete the
  39553. # associated records themselves, you can always do something along the lines of
  39554. # <tt>person.tasks.each(&:destroy)</tt>.
  39555. #
  39556. # == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
  39557. #
  39558. # If you attempt to assign an object to an association that doesn't match the inferred
  39559. # or specified <tt>:class_name</tt>, you'll get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
  39560. #
  39561. # == Options
  39562. #
  39563. # All of the association macros can be specialized through options. This makes cases
  39564. # more complex than the simple and guessable ones possible.
  39565. module ClassMethods
  39566. # Specifies a one-to-many association. The following methods for retrieval and query of
  39567. # collections of associated objects will be added:
  39568. #
  39569. # [collection(force_reload = false)]
  39570. # Returns an array of all the associated objects.
  39571. # An empty array is returned if none are found.
  39572. # [collection<<(object, ...)]
  39573. # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
  39574. # Note that this operation instantly fires update sql without waiting for the save or update call on the
  39575. # parent object.
  39576. # [collection.delete(object, ...)]
  39577. # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
  39578. # Objects will be in addition destroyed if they're associated with <tt>dependent: :destroy</tt>,
  39579. # and deleted if they're associated with <tt>dependent: :delete_all</tt>.
  39580. #
  39581. # If the <tt>:through</tt> option is used, then the join records are deleted (rather than
  39582. # nullified) by default, but you can specify <tt>dependent: :destroy</tt> or
  39583. # <tt>dependent: :nullify</tt> to override this.
  39584. # [collection.destroy(object, ...)]
  39585. # Removes one or more objects from the collection by running <tt>destroy</tt> on
  39586. # each record, regardless of any dependent option, ensuring callbacks are run.
  39587. #
  39588. # If the <tt>:through</tt> option is used, then the join records are destroyed
  39589. # instead, not the objects themselves.
  39590. # [collection=objects]
  39591. # Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
  39592. # option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
  39593. # direct.
  39594. # [collection_singular_ids]
  39595. # Returns an array of the associated objects' ids
  39596. # [collection_singular_ids=ids]
  39597. # Replace the collection with the objects identified by the primary keys in +ids+. This
  39598. # method loads the models and calls <tt>collection=</tt>. See above.
  39599. # [collection.clear]
  39600. # Removes every object from the collection. This destroys the associated objects if they
  39601. # are associated with <tt>dependent: :destroy</tt>, deletes them directly from the
  39602. # database if <tt>dependent: :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
  39603. # If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
  39604. # Join models are directly deleted.
  39605. # [collection.empty?]
  39606. # Returns +true+ if there are no associated objects.
  39607. # [collection.size]
  39608. # Returns the number of associated objects.
  39609. # [collection.find(...)]
  39610. # Finds an associated object according to the same rules as ActiveRecord::Base.find.
  39611. # [collection.exists?(...)]
  39612. # Checks whether an associated object with the given conditions exists.
  39613. # Uses the same rules as ActiveRecord::Base.exists?.
  39614. # [collection.build(attributes = {}, ...)]
  39615. # Returns one or more new objects of the collection type that have been instantiated
  39616. # with +attributes+ and linked to this object through a foreign key, but have not yet
  39617. # been saved.
  39618. # [collection.create(attributes = {})]
  39619. # Returns a new object of the collection type that has been instantiated
  39620. # with +attributes+, linked to this object through a foreign key, and that has already
  39621. # been saved (if it passed the validation). *Note*: This only works if the base model
  39622. # already exists in the DB, not if it is a new (unsaved) record!
  39623. #
  39624. # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
  39625. # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
  39626. #
  39627. # === Example
  39628. #
  39629. # Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
  39630. # * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>)
  39631. # * <tt>Firm#clients<<</tt>
  39632. # * <tt>Firm#clients.delete</tt>
  39633. # * <tt>Firm#clients.destroy</tt>
  39634. # * <tt>Firm#clients=</tt>
  39635. # * <tt>Firm#client_ids</tt>
  39636. # * <tt>Firm#client_ids=</tt>
  39637. # * <tt>Firm#clients.clear</tt>
  39638. # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
  39639. # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
  39640. # * <tt>Firm#clients.find</tt> (similar to <tt>Client.where(firm_id: id).find(id)</tt>)
  39641. # * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)
  39642. # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
  39643. # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
  39644. # The declaration can also include an options hash to specialize the behavior of the association.
  39645. #
  39646. # === Options
  39647. # [:class_name]
  39648. # Specify the class name of the association. Use it only if that name can't be inferred
  39649. # from the association name. So <tt>has_many :products</tt> will by default be linked
  39650. # to the Product class, but if the real class name is SpecialProduct, you'll have to
  39651. # specify it with this option.
  39652. # [:foreign_key]
  39653. # Specify the foreign key used for the association. By default this is guessed to be the name
  39654. # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+
  39655. # association will use "person_id" as the default <tt>:foreign_key</tt>.
  39656. # [:primary_key]
  39657. # Specify the method that returns the primary key used for the association. By default this is +id+.
  39658. # [:dependent]
  39659. # Controls what happens to the associated objects when
  39660. # their owner is destroyed. Note that these are implemented as
  39661. # callbacks, and Rails executes callbacks in order. Therefore, other
  39662. # similar callbacks may affect the :dependent behavior, and the
  39663. # :dependent behavior may affect other callbacks.
  39664. #
  39665. # * <tt>:destroy</tt> causes all the associated objects to also be destroyed
  39666. # * <tt>:delete_all</tt> causes all the asssociated objects to be deleted directly from the database (so callbacks will not execute)
  39667. # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
  39668. # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records
  39669. # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects
  39670. #
  39671. # If using with the <tt>:through</tt> option, the association on the join model must be
  39672. # a +belongs_to+, and the records which get deleted are the join records, rather than
  39673. # the associated records.
  39674. # [:counter_cache]
  39675. # This option can be used to configure a custom named <tt>:counter_cache.</tt> You only need this option,
  39676. # when you customized the name of your <tt>:counter_cache</tt> on the <tt>belongs_to</tt> association.
  39677. # [:as]
  39678. # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
  39679. # [:through]
  39680. # Specifies an association through which to perform the query. This can be any other type
  39681. # of association, including other <tt>:through</tt> associations. Options for <tt>:class_name</tt>,
  39682. # <tt>:primary_key</tt> and <tt>:foreign_key</tt> are ignored, as the association uses the
  39683. # source reflection.
  39684. #
  39685. # If the association on the join model is a +belongs_to+, the collection can be modified
  39686. # and the records on the <tt>:through</tt> model will be automatically created and removed
  39687. # as appropriate. Otherwise, the collection is read-only, so you should manipulate the
  39688. # <tt>:through</tt> association directly.
  39689. #
  39690. # If you are going to modify the association (rather than just read from it), then it is
  39691. # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
  39692. # join model. This allows associated records to be built which will automatically create
  39693. # the appropriate join model records when they are saved. (See the 'Association Join Models'
  39694. # section above.)
  39695. # [:source]
  39696. # Specifies the source association name used by <tt>has_many :through</tt> queries.
  39697. # Only use it if the name cannot be inferred from the association.
  39698. # <tt>has_many :subscribers, through: :subscriptions</tt> will look for either <tt>:subscribers</tt> or
  39699. # <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
  39700. # [:source_type]
  39701. # Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
  39702. # association is a polymorphic +belongs_to+.
  39703. # [:validate]
  39704. # If +false+, don't validate the associated objects when saving the parent object. true by default.
  39705. # [:autosave]
  39706. # If true, always save the associated objects or destroy them if marked for destruction,
  39707. # when saving the parent object. If false, never save or destroy the associated objects.
  39708. # By default, only save associated objects that are new records. This option is implemented as a
  39709. # before_save callback. Because callbacks are run in the order they are defined, associated objects
  39710. # may need to be explicitly saved in any user-defined before_save callbacks.
  39711. #
  39712. # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
  39713. # [:inverse_of]
  39714. # Specifies the name of the <tt>belongs_to</tt> association on the associated object
  39715. # that is the inverse of this <tt>has_many</tt> association. Does not work in combination
  39716. # with <tt>:through</tt> or <tt>:as</tt> options.
  39717. # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
  39718. #
  39719. # Option examples:
  39720. # has_many :comments, -> { order "posted_on" }
  39721. # has_many :comments, -> { includes :author }
  39722. # has_many :people, -> { where("deleted = 0").order("name") }, class_name: "Person"
  39723. # has_many :tracks, -> { order "position" }, dependent: :destroy
  39724. # has_many :comments, dependent: :nullify
  39725. # has_many :tags, as: :taggable
  39726. # has_many :reports, -> { readonly }
  39727. # has_many :subscribers, through: :subscriptions, source: :user
  39728. def has_many(name, scope = nil, options = {}, &extension)
  39729. Builder::HasMany.build(self, name, scope, options, &extension)
  39730. end
  39731. # Specifies a one-to-one association with another class. This method should only be used
  39732. # if the other class contains the foreign key. If the current class contains the foreign key,
  39733. # then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
  39734. # on when to use has_one and when to use belongs_to.
  39735. #
  39736. # The following methods for retrieval and query of a single associated object will be added:
  39737. #
  39738. # [association(force_reload = false)]
  39739. # Returns the associated object. +nil+ is returned if none is found.
  39740. # [association=(associate)]
  39741. # Assigns the associate object, extracts the primary key, sets it as the foreign key,
  39742. # and saves the associate object.
  39743. # [build_association(attributes = {})]
  39744. # Returns a new object of the associated type that has been instantiated
  39745. # with +attributes+ and linked to this object through a foreign key, but has not
  39746. # yet been saved.
  39747. # [create_association(attributes = {})]
  39748. # Returns a new object of the associated type that has been instantiated
  39749. # with +attributes+, linked to this object through a foreign key, and that
  39750. # has already been saved (if it passed the validation).
  39751. # [create_association!(attributes = {})]
  39752. # Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
  39753. # if the record is invalid.
  39754. #
  39755. # (+association+ is replaced with the symbol passed as the first argument, so
  39756. # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
  39757. #
  39758. # === Example
  39759. #
  39760. # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
  39761. # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.where(account_id: id).first</tt>)
  39762. # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
  39763. # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
  39764. # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
  39765. # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
  39766. #
  39767. # === Options
  39768. #
  39769. # The declaration can also include an options hash to specialize the behavior of the association.
  39770. #
  39771. # Options are:
  39772. # [:class_name]
  39773. # Specify the class name of the association. Use it only if that name can't be inferred
  39774. # from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
  39775. # if the real class name is Person, you'll have to specify it with this option.
  39776. # [:dependent]
  39777. # Controls what happens to the associated object when
  39778. # its owner is destroyed:
  39779. #
  39780. # * <tt>:destroy</tt> causes the associated object to also be destroyed
  39781. # * <tt>:delete</tt> causes the asssociated object to be deleted directly from the database (so callbacks will not execute)
  39782. # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
  39783. # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
  39784. # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
  39785. # [:foreign_key]
  39786. # Specify the foreign key used for the association. By default this is guessed to be the name
  39787. # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
  39788. # will use "person_id" as the default <tt>:foreign_key</tt>.
  39789. # [:primary_key]
  39790. # Specify the method that returns the primary key used for the association. By default this is +id+.
  39791. # [:as]
  39792. # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
  39793. # [:through]
  39794. # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
  39795. # <tt>:primary_key</tt>, and <tt>:foreign_key</tt> are ignored, as the association uses the
  39796. # source reflection. You can only use a <tt>:through</tt> query through a <tt>has_one</tt>
  39797. # or <tt>belongs_to</tt> association on the join model.
  39798. # [:source]
  39799. # Specifies the source association name used by <tt>has_one :through</tt> queries.
  39800. # Only use it if the name cannot be inferred from the association.
  39801. # <tt>has_one :favorite, through: :favorites</tt> will look for a
  39802. # <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
  39803. # [:source_type]
  39804. # Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
  39805. # association is a polymorphic +belongs_to+.
  39806. # [:validate]
  39807. # If +false+, don't validate the associated object when saving the parent object. +false+ by default.
  39808. # [:autosave]
  39809. # If true, always save the associated object or destroy it if marked for destruction,
  39810. # when saving the parent object. If false, never save or destroy the associated object.
  39811. # By default, only save the associated object if it's a new record.
  39812. #
  39813. # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
  39814. # [:inverse_of]
  39815. # Specifies the name of the <tt>belongs_to</tt> association on the associated object
  39816. # that is the inverse of this <tt>has_one</tt> association. Does not work in combination
  39817. # with <tt>:through</tt> or <tt>:as</tt> options.
  39818. # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
  39819. #
  39820. # Option examples:
  39821. # has_one :credit_card, dependent: :destroy # destroys the associated credit card
  39822. # has_one :credit_card, dependent: :nullify # updates the associated records foreign
  39823. # # key value to NULL rather than destroying it
  39824. # has_one :last_comment, -> { order 'posted_on' }, class_name: "Comment"
  39825. # has_one :project_manager, -> { where role: 'project_manager' }, class_name: "Person"
  39826. # has_one :attachment, as: :attachable
  39827. # has_one :boss, readonly: :true
  39828. # has_one :club, through: :membership
  39829. # has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
  39830. def has_one(name, scope = nil, options = {})
  39831. Builder::HasOne.build(self, name, scope, options)
  39832. end
  39833. # Specifies a one-to-one association with another class. This method should only be used
  39834. # if this class contains the foreign key. If the other class contains the foreign key,
  39835. # then you should use +has_one+ instead. See also ActiveRecord::Associations::ClassMethods's overview
  39836. # on when to use +has_one+ and when to use +belongs_to+.
  39837. #
  39838. # Methods will be added for retrieval and query for a single associated object, for which
  39839. # this object holds an id:
  39840. #
  39841. # [association(force_reload = false)]
  39842. # Returns the associated object. +nil+ is returned if none is found.
  39843. # [association=(associate)]
  39844. # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
  39845. # [build_association(attributes = {})]
  39846. # Returns a new object of the associated type that has been instantiated
  39847. # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
  39848. # [create_association(attributes = {})]
  39849. # Returns a new object of the associated type that has been instantiated
  39850. # with +attributes+, linked to this object through a foreign key, and that
  39851. # has already been saved (if it passed the validation).
  39852. # [create_association!(attributes = {})]
  39853. # Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
  39854. # if the record is invalid.
  39855. #
  39856. # (+association+ is replaced with the symbol passed as the first argument, so
  39857. # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
  39858. #
  39859. # === Example
  39860. #
  39861. # A Post class declares <tt>belongs_to :author</tt>, which will add:
  39862. # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
  39863. # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
  39864. # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
  39865. # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
  39866. # * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
  39867. # The declaration can also include an options hash to specialize the behavior of the association.
  39868. #
  39869. # === Options
  39870. #
  39871. # [:class_name]
  39872. # Specify the class name of the association. Use it only if that name can't be inferred
  39873. # from the association name. So <tt>belongs_to :author</tt> will by default be linked to the Author class, but
  39874. # if the real class name is Person, you'll have to specify it with this option.
  39875. # [:foreign_key]
  39876. # Specify the foreign key used for the association. By default this is guessed to be the name
  39877. # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>
  39878. # association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly,
  39879. # <tt>belongs_to :favorite_person, class_name: "Person"</tt> will use a foreign key
  39880. # of "favorite_person_id".
  39881. # [:foreign_type]
  39882. # Specify the column used to store the associated object's type, if this is a polymorphic
  39883. # association. By default this is guessed to be the name of the association with a "_type"
  39884. # suffix. So a class that defines a <tt>belongs_to :taggable, polymorphic: true</tt>
  39885. # association will use "taggable_type" as the default <tt>:foreign_type</tt>.
  39886. # [:primary_key]
  39887. # Specify the method that returns the primary key of associated object used for the association.
  39888. # By default this is id.
  39889. # [:dependent]
  39890. # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
  39891. # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
  39892. # This option should not be specified when <tt>belongs_to</tt> is used in conjunction with
  39893. # a <tt>has_many</tt> relationship on another class because of the potential to leave
  39894. # orphaned records behind.
  39895. # [:counter_cache]
  39896. # Caches the number of belonging objects on the associate class through the use of +increment_counter+
  39897. # and +decrement_counter+. The counter cache is incremented when an object of this
  39898. # class is created and decremented when it's destroyed. This requires that a column
  39899. # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
  39900. # is used on the associate class (such as a Post class) - that is the migration for
  39901. # <tt>#{table_name}_count</tt> is created on the associate class (such that Post.comments_count will
  39902. # return the count cached, see note below). You can also specify a custom counter
  39903. # cache column by providing a column name instead of a +true+/+false+ value to this
  39904. # option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
  39905. # Note: Specifying a counter cache will add it to that model's list of readonly attributes
  39906. # using +attr_readonly+.
  39907. # [:polymorphic]
  39908. # Specify this association is a polymorphic association by passing +true+.
  39909. # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
  39910. # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
  39911. # [:validate]
  39912. # If +false+, don't validate the associated objects when saving the parent object. +false+ by default.
  39913. # [:autosave]
  39914. # If true, always save the associated object or destroy it if marked for destruction, when
  39915. # saving the parent object.
  39916. # If false, never save or destroy the associated object.
  39917. # By default, only save the associated object if it's a new record.
  39918. #
  39919. # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
  39920. # [:touch]
  39921. # If true, the associated object will be touched (the updated_at/on attributes set to now)
  39922. # when this record is either saved or destroyed. If you specify a symbol, that attribute
  39923. # will be updated with the current time in addition to the updated_at/on attribute.
  39924. # [:inverse_of]
  39925. # Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated
  39926. # object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
  39927. # combination with the <tt>:polymorphic</tt> options.
  39928. # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
  39929. #
  39930. # Option examples:
  39931. # belongs_to :firm, foreign_key: "client_of"
  39932. # belongs_to :person, primary_key: "name", foreign_key: "person_name"
  39933. # belongs_to :author, class_name: "Person", foreign_key: "author_id"
  39934. # belongs_to :valid_coupon, ->(o) { where "discounts > #{o.payments_count}" },
  39935. # class_name: "Coupon", foreign_key: "coupon_id"
  39936. # belongs_to :attachable, polymorphic: true
  39937. # belongs_to :project, readonly: true
  39938. # belongs_to :post, counter_cache: true
  39939. # belongs_to :company, touch: true
  39940. # belongs_to :company, touch: :employees_last_updated_at
  39941. def belongs_to(name, scope = nil, options = {})
  39942. Builder::BelongsTo.build(self, name, scope, options)
  39943. end
  39944. # Specifies a many-to-many relationship with another class. This associates two classes via an
  39945. # intermediate join table. Unless the join table is explicitly specified as an option, it is
  39946. # guessed using the lexical order of the class names. So a join between Developer and Project
  39947. # will give the default join table name of "developers_projects" because "D" precedes "P" alphabetically.
  39948. # Note that this precedence is calculated using the <tt><</tt> operator for String. This
  39949. # means that if the strings are of different lengths, and the strings are equal when compared
  39950. # up to the shortest length, then the longer string is considered of higher
  39951. # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
  39952. # to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
  39953. # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
  39954. # custom <tt>:join_table</tt> option if you need to.
  39955. #
  39956. # The join table should not have a primary key or a model associated with it. You must manually generate the
  39957. # join table with a migration such as this:
  39958. #
  39959. # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration
  39960. # def change
  39961. # create_table :developers_projects, id: false do |t|
  39962. # t.integer :developer_id
  39963. # t.integer :project_id
  39964. # end
  39965. # end
  39966. # end
  39967. #
  39968. # It's also a good idea to add indexes to each of those columns to speed up the joins process.
  39969. # However, in MySQL it is advised to add a compound index for both of the columns as MySQL only
  39970. # uses one index per table during the lookup.
  39971. #
  39972. # Adds the following methods for retrieval and query:
  39973. #
  39974. # [collection(force_reload = false)]
  39975. # Returns an array of all the associated objects.
  39976. # An empty array is returned if none are found.
  39977. # [collection<<(object, ...)]
  39978. # Adds one or more objects to the collection by creating associations in the join table
  39979. # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
  39980. # Note that this operation instantly fires update sql without waiting for the save or update call on the
  39981. # parent object.
  39982. # [collection.delete(object, ...)]
  39983. # Removes one or more objects from the collection by removing their associations from the join table.
  39984. # This does not destroy the objects.
  39985. # [collection.destroy(object, ...)]
  39986. # Removes one or more objects from the collection by running destroy on each association in the join table, overriding any dependent option.
  39987. # This does not destroy the objects.
  39988. # [collection=objects]
  39989. # Replaces the collection's content by deleting and adding objects as appropriate.
  39990. # [collection_singular_ids]
  39991. # Returns an array of the associated objects' ids.
  39992. # [collection_singular_ids=ids]
  39993. # Replace the collection by the objects identified by the primary keys in +ids+.
  39994. # [collection.clear]
  39995. # Removes every object from the collection. This does not destroy the objects.
  39996. # [collection.empty?]
  39997. # Returns +true+ if there are no associated objects.
  39998. # [collection.size]
  39999. # Returns the number of associated objects.
  40000. # [collection.find(id)]
  40001. # Finds an associated object responding to the +id+ and that
  40002. # meets the condition that it has to be associated with this object.
  40003. # Uses the same rules as ActiveRecord::Base.find.
  40004. # [collection.exists?(...)]
  40005. # Checks whether an associated object with the given conditions exists.
  40006. # Uses the same rules as ActiveRecord::Base.exists?.
  40007. # [collection.build(attributes = {})]
  40008. # Returns a new object of the collection type that has been instantiated
  40009. # with +attributes+ and linked to this object through the join table, but has not yet been saved.
  40010. # [collection.create(attributes = {})]
  40011. # Returns a new object of the collection type that has been instantiated
  40012. # with +attributes+, linked to this object through the join table, and that has already been
  40013. # saved (if it passed the validation).
  40014. #
  40015. # (+collection+ is replaced with the symbol passed as the first argument, so
  40016. # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
  40017. #
  40018. # === Example
  40019. #
  40020. # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
  40021. # * <tt>Developer#projects</tt>
  40022. # * <tt>Developer#projects<<</tt>
  40023. # * <tt>Developer#projects.delete</tt>
  40024. # * <tt>Developer#projects.destroy</tt>
  40025. # * <tt>Developer#projects=</tt>
  40026. # * <tt>Developer#project_ids</tt>
  40027. # * <tt>Developer#project_ids=</tt>
  40028. # * <tt>Developer#projects.clear</tt>
  40029. # * <tt>Developer#projects.empty?</tt>
  40030. # * <tt>Developer#projects.size</tt>
  40031. # * <tt>Developer#projects.find(id)</tt>
  40032. # * <tt>Developer#projects.exists?(...)</tt>
  40033. # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
  40034. # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
  40035. # The declaration may include an options hash to specialize the behavior of the association.
  40036. #
  40037. # === Options
  40038. #
  40039. # [:class_name]
  40040. # Specify the class name of the association. Use it only if that name can't be inferred
  40041. # from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
  40042. # Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
  40043. # [:join_table]
  40044. # Specify the name of the join table if the default based on lexical order isn't what you want.
  40045. # <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
  40046. # MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work.
  40047. # [:foreign_key]
  40048. # Specify the foreign key used for the association. By default this is guessed to be the name
  40049. # of this class in lower-case and "_id" suffixed. So a Person class that makes
  40050. # a +has_and_belongs_to_many+ association to Project will use "person_id" as the
  40051. # default <tt>:foreign_key</tt>.
  40052. # [:association_foreign_key]
  40053. # Specify the foreign key used for the association on the receiving side of the association.
  40054. # By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
  40055. # So if a Person class makes a +has_and_belongs_to_many+ association to Project,
  40056. # the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
  40057. # [:readonly]
  40058. # If true, all the associated objects are readonly through the association.
  40059. # [:validate]
  40060. # If +false+, don't validate the associated objects when saving the parent object. +true+ by default.
  40061. # [:autosave]
  40062. # If true, always save the associated objects or destroy them if marked for destruction, when
  40063. # saving the parent object.
  40064. # If false, never save or destroy the associated objects.
  40065. # By default, only save associated objects that are new records.
  40066. #
  40067. # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
  40068. #
  40069. # Option examples:
  40070. # has_and_belongs_to_many :projects
  40071. # has_and_belongs_to_many :projects, -> { includes :milestones, :manager }
  40072. # has_and_belongs_to_many :nations, class_name: "Country"
  40073. # has_and_belongs_to_many :categories, join_table: "prods_cats"
  40074. # has_and_belongs_to_many :categories, -> { readonly }
  40075. def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
  40076. Builder::HasAndBelongsToMany.build(self, name, scope, options, &extension)
  40077. end
  40078. end
  40079. end
  40080. end
  40081. module ActiveRecord
  40082. module AttributeAssignment
  40083. extend ActiveSupport::Concern
  40084. include ActiveModel::DeprecatedMassAssignmentSecurity
  40085. include ActiveModel::ForbiddenAttributesProtection
  40086. # Allows you to set all the attributes by passing in a hash of attributes with
  40087. # keys matching the attribute names (which again matches the column names).
  40088. #
  40089. # If the passed hash responds to <tt>permitted?</tt> method and the return value
  40090. # of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
  40091. # exception is raised.
  40092. def assign_attributes(new_attributes)
  40093. return if new_attributes.blank?
  40094. attributes = new_attributes.stringify_keys
  40095. multi_parameter_attributes = []
  40096. nested_parameter_attributes = []
  40097. attributes = sanitize_for_mass_assignment(attributes)
  40098. attributes.each do |k, v|
  40099. if k.include?("(")
  40100. multi_parameter_attributes << [ k, v ]
  40101. elsif v.is_a?(Hash)
  40102. nested_parameter_attributes << [ k, v ]
  40103. else
  40104. _assign_attribute(k, v)
  40105. end
  40106. end
  40107. assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
  40108. assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
  40109. end
  40110. alias attributes= assign_attributes
  40111. private
  40112. def _assign_attribute(k, v)
  40113. public_send("#{k}=", v)
  40114. rescue NoMethodError
  40115. if respond_to?("#{k}=")
  40116. raise
  40117. else
  40118. raise UnknownAttributeError, "unknown attribute: #{k}"
  40119. end
  40120. end
  40121. # Assign any deferred nested attributes after the base attributes have been set.
  40122. def assign_nested_parameter_attributes(pairs)
  40123. pairs.each { |k, v| _assign_attribute(k, v) }
  40124. end
  40125. # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
  40126. # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
  40127. # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
  40128. # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
  40129. # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum and
  40130. # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
  40131. def assign_multiparameter_attributes(pairs)
  40132. execute_callstack_for_multiparameter_attributes(
  40133. extract_callstack_for_multiparameter_attributes(pairs)
  40134. )
  40135. end
  40136. def execute_callstack_for_multiparameter_attributes(callstack)
  40137. errors = []
  40138. callstack.each do |name, values_with_empty_parameters|
  40139. begin
  40140. send("#{name}=", MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value)
  40141. rescue => ex
  40142. errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
  40143. end
  40144. end
  40145. unless errors.empty?
  40146. error_descriptions = errors.map { |ex| ex.message }.join(",")
  40147. raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
  40148. end
  40149. end
  40150. def extract_callstack_for_multiparameter_attributes(pairs)
  40151. attributes = { }
  40152. pairs.each do |(multiparameter_name, value)|
  40153. attribute_name = multiparameter_name.split("(").first
  40154. attributes[attribute_name] ||= {}
  40155. parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
  40156. attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
  40157. end
  40158. attributes
  40159. end
  40160. def type_cast_attribute_value(multiparameter_name, value)
  40161. multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
  40162. end
  40163. def find_parameter_position(multiparameter_name)
  40164. multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
  40165. end
  40166. class MultiparameterAttribute #:nodoc:
  40167. attr_reader :object, :name, :values, :column
  40168. def initialize(object, name, values)
  40169. @object = object
  40170. @name = name
  40171. @values = values
  40172. end
  40173. def read_value
  40174. return if values.values.compact.empty?
  40175. @column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name)
  40176. klass = column.klass
  40177. if klass == Time
  40178. read_time
  40179. elsif klass == Date
  40180. read_date
  40181. else
  40182. read_other(klass)
  40183. end
  40184. end
  40185. private
  40186. def instantiate_time_object(set_values)
  40187. if object.class.send(:create_time_zone_conversion_attribute?, name, column)
  40188. Time.zone.local(*set_values)
  40189. else
  40190. Time.send(object.class.default_timezone, *set_values)
  40191. end
  40192. end
  40193. def read_time
  40194. # If column is a :time (and not :date or :timestamp) there is no need to validate if
  40195. # there are year/month/day fields
  40196. if column.type == :time
  40197. # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
  40198. { 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
  40199. values[key] ||= value
  40200. end
  40201. else
  40202. # else column is a timestamp, so if Date bits were not provided, error
  40203. validate_missing_parameters!([1,2,3])
  40204. # If Date bits were provided but blank, then return nil
  40205. return if blank_date_parameter?
  40206. end
  40207. max_position = extract_max_param(6)
  40208. set_values = values.values_at(*(1..max_position))
  40209. # If Time bits are not there, then default to 0
  40210. (3..5).each { |i| set_values[i] = set_values[i].presence || 0 }
  40211. instantiate_time_object(set_values)
  40212. end
  40213. def read_date
  40214. return if blank_date_parameter?
  40215. set_values = values.values_at(1,2,3)
  40216. begin
  40217. Date.new(*set_values)
  40218. rescue ArgumentError # if Date.new raises an exception on an invalid date
  40219. instantiate_time_object(set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
  40220. end
  40221. end
  40222. def read_other(klass)
  40223. max_position = extract_max_param
  40224. positions = (1..max_position)
  40225. validate_missing_parameters!(positions)
  40226. set_values = values.values_at(*positions)
  40227. klass.new(*set_values)
  40228. end
  40229. # Checks whether some blank date parameter exists. Note that this is different
  40230. # than the validate_missing_parameters! method, since it just checks for blank
  40231. # positions instead of missing ones, and does not raise in case one blank position
  40232. # exists. The caller is responsible to handle the case of this returning true.
  40233. def blank_date_parameter?
  40234. (1..3).any? { |position| values[position].blank? }
  40235. end
  40236. # If some position is not provided, it errors out a missing parameter exception.
  40237. def validate_missing_parameters!(positions)
  40238. if missing_parameter = positions.detect { |position| !values.key?(position) }
  40239. raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})")
  40240. end
  40241. end
  40242. def extract_max_param(upper_cap = 100)
  40243. [values.keys.max, upper_cap].min
  40244. end
  40245. end
  40246. end
  40247. end
  40248. module ActiveRecord
  40249. module AttributeMethods
  40250. # = Active Record Attribute Methods Before Type Cast
  40251. #
  40252. # <tt>ActiveRecord::AttributeMethods::BeforeTypeCast</tt> provides a way to
  40253. # read the value of the attributes before typecasting and deserialization.
  40254. #
  40255. # class Task < ActiveRecord::Base
  40256. # end
  40257. #
  40258. # task = Task.new(id: '1', completed_on: '2012-10-21')
  40259. # task.id # => 1
  40260. # task.completed_on # => Sun, 21 Oct 2012
  40261. #
  40262. # task.attributes_before_type_cast
  40263. # # => {"id"=>"1", "completed_on"=>"2012-10-21", ... }
  40264. # task.read_attribute_before_type_cast('id') # => "1"
  40265. # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
  40266. #
  40267. # In addition to #read_attribute_before_type_cast and #attributes_before_type_cast,
  40268. # it declares a method for all attributes with the <tt>*_before_type_cast</tt>
  40269. # suffix.
  40270. #
  40271. # task.id_before_type_cast # => "1"
  40272. # task.completed_on_before_type_cast # => "2012-10-21"
  40273. module BeforeTypeCast
  40274. extend ActiveSupport::Concern
  40275. included do
  40276. attribute_method_suffix "_before_type_cast"
  40277. end
  40278. # Returns the value of the attribute identified by +attr_name+ before
  40279. # typecasting and deserialization.
  40280. #
  40281. # class Task < ActiveRecord::Base
  40282. # end
  40283. #
  40284. # task = Task.new(id: '1', completed_on: '2012-10-21')
  40285. # task.read_attribute('id') # => 1
  40286. # task.read_attribute_before_type_cast('id') # => '1'
  40287. # task.read_attribute('completed_on') # => Sun, 21 Oct 2012
  40288. # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
  40289. def read_attribute_before_type_cast(attr_name)
  40290. @attributes[attr_name]
  40291. end
  40292. # Returns a hash of attributes before typecasting and deserialization.
  40293. #
  40294. # class Task < ActiveRecord::Base
  40295. # end
  40296. #
  40297. # task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
  40298. # task.attributes
  40299. # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
  40300. # task.attributes_before_type_cast
  40301. # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
  40302. def attributes_before_type_cast
  40303. @attributes
  40304. end
  40305. private
  40306. # Handle *_before_type_cast for method_missing.
  40307. def attribute_before_type_cast(attribute_name)
  40308. read_attribute_before_type_cast(attribute_name)
  40309. end
  40310. end
  40311. end
  40312. end
  40313. require 'active_support/core_ext/module/attribute_accessors'
  40314. module ActiveRecord
  40315. module AttributeMethods
  40316. module Dirty # :nodoc:
  40317. extend ActiveSupport::Concern
  40318. include ActiveModel::Dirty
  40319. included do
  40320. if self < ::ActiveRecord::Timestamp
  40321. raise "You cannot include Dirty after Timestamp"
  40322. end
  40323. class_attribute :partial_writes, instance_writer: false
  40324. self.partial_writes = true
  40325. def self.partial_updates=(v); self.partial_writes = v; end
  40326. def self.partial_updates?; partial_writes?; end
  40327. def self.partial_updates; partial_writes; end
  40328. ActiveSupport::Deprecation.deprecate_methods(
  40329. singleton_class,
  40330. :partial_updates= => :partial_writes=,
  40331. :partial_updates? => :partial_writes?,
  40332. :partial_updates => :partial_writes
  40333. )
  40334. end
  40335. # Attempts to +save+ the record and clears changed attributes if successful.
  40336. def save(*)
  40337. if status = super
  40338. @previously_changed = changes
  40339. @changed_attributes.clear
  40340. end
  40341. status
  40342. end
  40343. # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
  40344. def save!(*)
  40345. super.tap do
  40346. @previously_changed = changes
  40347. @changed_attributes.clear
  40348. end
  40349. end
  40350. # <tt>reload</tt> the record and clears changed attributes.
  40351. def reload(*)
  40352. super.tap do
  40353. @previously_changed.clear
  40354. @changed_attributes.clear
  40355. end
  40356. end
  40357. private
  40358. # Wrap write_attribute to remember original attribute value.
  40359. def write_attribute(attr, value)
  40360. attr = attr.to_s
  40361. # The attribute already has an unsaved change.
  40362. if attribute_changed?(attr)
  40363. old = @changed_attributes[attr]
  40364. @changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
  40365. else
  40366. old = clone_attribute_value(:read_attribute, attr)
  40367. @changed_attributes[attr] = old if _field_changed?(attr, old, value)
  40368. end
  40369. # Carry on.
  40370. super(attr, value)
  40371. end
  40372. def update_record(*)
  40373. partial_writes? ? super(keys_for_partial_write) : super
  40374. end
  40375. def create_record(*)
  40376. partial_writes? ? super(keys_for_partial_write) : super
  40377. end
  40378. # Serialized attributes should always be written in case they've been
  40379. # changed in place.
  40380. def keys_for_partial_write
  40381. changed | (attributes.keys & self.class.serialized_attributes.keys)
  40382. end
  40383. def _field_changed?(attr, old, value)
  40384. if column = column_for_attribute(attr)
  40385. if column.number? && (changes_from_nil_to_empty_string?(column, old, value) ||
  40386. changes_from_zero_to_string?(old, value))
  40387. value = nil
  40388. else
  40389. value = column.type_cast(value)
  40390. end
  40391. end
  40392. old != value
  40393. end
  40394. def changes_from_nil_to_empty_string?(column, old, value)
  40395. # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
  40396. # Hence we don't record it as a change if the value changes from nil to ''.
  40397. # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
  40398. # be typecast back to 0 (''.to_i => 0)
  40399. column.null && (old.nil? || old == 0) && value.blank?
  40400. end
  40401. def changes_from_zero_to_string?(old, value)
  40402. # For columns with old 0 and value non-empty string
  40403. old == 0 && value.is_a?(String) && value.present? && value != '0'
  40404. end
  40405. end
  40406. end
  40407. end
  40408. require 'set'
  40409. module ActiveRecord
  40410. module AttributeMethods
  40411. module PrimaryKey
  40412. extend ActiveSupport::Concern
  40413. # Returns this record's primary key value wrapped in an Array if one is
  40414. # available.
  40415. def to_key
  40416. key = self.id
  40417. [key] if key
  40418. end
  40419. # Returns the primary key value.
  40420. def id
  40421. read_attribute(self.class.primary_key)
  40422. end
  40423. # Sets the primary key value.
  40424. def id=(value)
  40425. write_attribute(self.class.primary_key, value) if self.class.primary_key
  40426. end
  40427. # Queries the primary key value.
  40428. def id?
  40429. query_attribute(self.class.primary_key)
  40430. end
  40431. # Returns the primary key value before type cast.
  40432. def id_before_type_cast
  40433. read_attribute_before_type_cast(self.class.primary_key)
  40434. end
  40435. protected
  40436. def attribute_method?(attr_name)
  40437. attr_name == 'id' || super
  40438. end
  40439. module ClassMethods
  40440. def define_method_attribute(attr_name)
  40441. super
  40442. if attr_name == primary_key && attr_name != 'id'
  40443. generated_attribute_methods.send(:alias_method, :id, primary_key)
  40444. end
  40445. end
  40446. ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast).to_set
  40447. def dangerous_attribute_method?(method_name)
  40448. super && !ID_ATTRIBUTE_METHODS.include?(method_name)
  40449. end
  40450. # Defines the primary key field -- can be overridden in subclasses.
  40451. # Overwriting will negate any effect of the +primary_key_prefix_type+
  40452. # setting, though.
  40453. def primary_key
  40454. @primary_key = reset_primary_key unless defined? @primary_key
  40455. @primary_key
  40456. end
  40457. # Returns a quoted version of the primary key name, used to construct
  40458. # SQL statements.
  40459. def quoted_primary_key
  40460. @quoted_primary_key ||= connection.quote_column_name(primary_key)
  40461. end
  40462. def reset_primary_key #:nodoc:
  40463. if self == base_class
  40464. self.primary_key = get_primary_key(base_class.name)
  40465. else
  40466. self.primary_key = base_class.primary_key
  40467. end
  40468. end
  40469. def get_primary_key(base_name) #:nodoc:
  40470. return 'id' if base_name.blank?
  40471. case primary_key_prefix_type
  40472. when :table_name
  40473. base_name.foreign_key(false)
  40474. when :table_name_with_underscore
  40475. base_name.foreign_key
  40476. else
  40477. if ActiveRecord::Base != self && table_exists?
  40478. connection.schema_cache.primary_keys[table_name]
  40479. else
  40480. 'id'
  40481. end
  40482. end
  40483. end
  40484. # Sets the name of the primary key column.
  40485. #
  40486. # class Project < ActiveRecord::Base
  40487. # self.primary_key = 'sysid'
  40488. # end
  40489. #
  40490. # You can also define the +primary_key+ method yourself:
  40491. #
  40492. # class Project < ActiveRecord::Base
  40493. # def self.primary_key
  40494. # 'foo_' + super
  40495. # end
  40496. # end
  40497. #
  40498. # Project.primary_key # => "foo_id"
  40499. def primary_key=(value)
  40500. @primary_key = value && value.to_s
  40501. @quoted_primary_key = nil
  40502. end
  40503. end
  40504. end
  40505. end
  40506. end
  40507. module ActiveRecord
  40508. module AttributeMethods
  40509. module Query
  40510. extend ActiveSupport::Concern
  40511. included do
  40512. attribute_method_suffix "?"
  40513. end
  40514. def query_attribute(attr_name)
  40515. value = read_attribute(attr_name) { |n| missing_attribute(n, caller) }
  40516. case value
  40517. when true then true
  40518. when false, nil then false
  40519. else
  40520. column = self.class.columns_hash[attr_name]
  40521. if column.nil?
  40522. if Numeric === value || value !~ /[^0-9]/
  40523. !value.to_i.zero?
  40524. else
  40525. return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
  40526. !value.blank?
  40527. end
  40528. elsif column.number?
  40529. !value.zero?
  40530. else
  40531. !value.blank?
  40532. end
  40533. end
  40534. end
  40535. private
  40536. # Handle *? for method_missing.
  40537. def attribute?(attribute_name)
  40538. query_attribute(attribute_name)
  40539. end
  40540. end
  40541. end
  40542. end
  40543. module ActiveRecord
  40544. module AttributeMethods
  40545. module Read
  40546. extend ActiveSupport::Concern
  40547. ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
  40548. included do
  40549. class_attribute :attribute_types_cached_by_default, instance_writer: false
  40550. self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
  40551. end
  40552. module ClassMethods
  40553. # +cache_attributes+ allows you to declare which converted attribute
  40554. # values should be cached. Usually caching only pays off for attributes
  40555. # with expensive conversion methods, like time related columns (e.g.
  40556. # +created_at+, +updated_at+).
  40557. def cache_attributes(*attribute_names)
  40558. cached_attributes.merge attribute_names.map { |attr| attr.to_s }
  40559. end
  40560. # Returns the attributes which are cached. By default time related columns
  40561. # with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
  40562. def cached_attributes
  40563. @cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set
  40564. end
  40565. # Returns +true+ if the provided attribute is being cached.
  40566. def cache_attribute?(attr_name)
  40567. cached_attributes.include?(attr_name)
  40568. end
  40569. protected
  40570. # We want to generate the methods via module_eval rather than
  40571. # define_method, because define_method is slower on dispatch and
  40572. # uses more memory (because it creates a closure).
  40573. #
  40574. # But sometimes the database might return columns with
  40575. # characters that are not allowed in normal method names (like
  40576. # 'my_column(omg)'. So to work around this we first define with
  40577. # the __temp__ identifier, and then use alias method to rename
  40578. # it to what we want.
  40579. #
  40580. # We are also defining a constant to hold the frozen string of
  40581. # the attribute name. Using a constant means that we do not have
  40582. # to allocate an object on each call to the attribute method.
  40583. # Making it frozen means that it doesn't get duped when used to
  40584. # key the @attributes_cache in read_attribute.
  40585. def define_method_attribute(name)
  40586. safe_name = name.unpack('h*').first
  40587. generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
  40588. def __temp__#{safe_name}
  40589. read_attribute(AttrNames::ATTR_#{safe_name}) { |n| missing_attribute(n, caller) }
  40590. end
  40591. alias_method #{name.inspect}, :__temp__#{safe_name}
  40592. undef_method :__temp__#{safe_name}
  40593. STR
  40594. end
  40595. private
  40596. def cacheable_column?(column)
  40597. if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
  40598. ! serialized_attributes.include? column.name
  40599. else
  40600. attribute_types_cached_by_default.include?(column.type)
  40601. end
  40602. end
  40603. end
  40604. # Returns the value of the attribute identified by <tt>attr_name</tt> after
  40605. # it has been typecast (for example, "2004-12-12" in a data column is cast
  40606. # to a date object, like Date.new(2004, 12, 12)).
  40607. def read_attribute(attr_name)
  40608. # If it's cached, just return it
  40609. # We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/3552829.
  40610. name = attr_name.to_s
  40611. @attributes_cache[name] || @attributes_cache.fetch(name) {
  40612. column = @columns_hash.fetch(name) {
  40613. return @attributes.fetch(name) {
  40614. if name == 'id' && self.class.primary_key != name
  40615. read_attribute(self.class.primary_key)
  40616. end
  40617. }
  40618. }
  40619. value = @attributes.fetch(name) {
  40620. return block_given? ? yield(name) : nil
  40621. }
  40622. if self.class.cache_attribute?(name)
  40623. @attributes_cache[name] = column.type_cast(value)
  40624. else
  40625. column.type_cast value
  40626. end
  40627. }
  40628. end
  40629. private
  40630. def attribute(attribute_name)
  40631. read_attribute(attribute_name)
  40632. end
  40633. end
  40634. end
  40635. end
  40636. module ActiveRecord
  40637. module AttributeMethods
  40638. module Serialization
  40639. extend ActiveSupport::Concern
  40640. included do
  40641. # Returns a hash of all the attributes that have been specified for
  40642. # serialization as keys and their class restriction as values.
  40643. class_attribute :serialized_attributes, instance_accessor: false
  40644. self.serialized_attributes = {}
  40645. end
  40646. module ClassMethods
  40647. # If you have an attribute that needs to be saved to the database as an
  40648. # object, and retrieved as the same object, then specify the name of that
  40649. # attribute using this method and it will be handled automatically. The
  40650. # serialization is done through YAML. If +class_name+ is specified, the
  40651. # serialized object must be of that class on retrieval or
  40652. # <tt>SerializationTypeMismatch</tt> will be raised.
  40653. #
  40654. # ==== Parameters
  40655. #
  40656. # * +attr_name+ - The field name that should be serialized.
  40657. # * +class_name+ - Optional, class name that the object type should be equal to.
  40658. #
  40659. # ==== Example
  40660. #
  40661. # # Serialize a preferences attribute.
  40662. # class User < ActiveRecord::Base
  40663. # serialize :preferences
  40664. # end
  40665. def serialize(attr_name, class_name = Object)
  40666. include Behavior
  40667. coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
  40668. class_name
  40669. else
  40670. Coders::YAMLColumn.new(class_name)
  40671. end
  40672. # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
  40673. # has its own hash of own serialized attributes
  40674. self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
  40675. end
  40676. end
  40677. def serialized_attributes
  40678. message = "Instance level serialized_attributes method is deprecated, please use class level method."
  40679. ActiveSupport::Deprecation.warn message
  40680. defined?(@serialized_attributes) ? @serialized_attributes : self.class.serialized_attributes
  40681. end
  40682. class Type # :nodoc:
  40683. def initialize(column)
  40684. @column = column
  40685. end
  40686. def type_cast(value)
  40687. value.unserialized_value
  40688. end
  40689. def type
  40690. @column.type
  40691. end
  40692. end
  40693. class Attribute < Struct.new(:coder, :value, :state) # :nodoc:
  40694. def unserialized_value
  40695. state == :serialized ? unserialize : value
  40696. end
  40697. def serialized_value
  40698. state == :unserialized ? serialize : value
  40699. end
  40700. def unserialize
  40701. self.state = :unserialized
  40702. self.value = coder.load(value)
  40703. end
  40704. def serialize
  40705. self.state = :serialized
  40706. self.value = coder.dump(value)
  40707. end
  40708. end
  40709. # This is only added to the model when serialize is called, which
  40710. # ensures we do not make things slower when serialization is not used.
  40711. module Behavior #:nodoc:
  40712. extend ActiveSupport::Concern
  40713. module ClassMethods
  40714. def initialize_attributes(attributes, options = {})
  40715. serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
  40716. super(attributes, options)
  40717. serialized_attributes.each do |key, coder|
  40718. if attributes.key?(key)
  40719. attributes[key] = Attribute.new(coder, attributes[key], serialized)
  40720. end
  40721. end
  40722. attributes
  40723. end
  40724. end
  40725. def type_cast_attribute_for_write(column, value)
  40726. if column && coder = self.class.serialized_attributes[column.name]
  40727. Attribute.new(coder, value, :unserialized)
  40728. else
  40729. super
  40730. end
  40731. end
  40732. def _field_changed?(attr, old, value)
  40733. if self.class.serialized_attributes.include?(attr)
  40734. old != value
  40735. else
  40736. super
  40737. end
  40738. end
  40739. def read_attribute_before_type_cast(attr_name)
  40740. if self.class.serialized_attributes.include?(attr_name)
  40741. super.unserialized_value
  40742. else
  40743. super
  40744. end
  40745. end
  40746. def attributes_before_type_cast
  40747. super.dup.tap do |attributes|
  40748. self.class.serialized_attributes.each_key do |key|
  40749. if attributes.key?(key)
  40750. attributes[key] = attributes[key].unserialized_value
  40751. end
  40752. end
  40753. end
  40754. end
  40755. def typecasted_attribute_value(name)
  40756. if self.class.serialized_attributes.include?(name)
  40757. @attributes[name].serialized_value
  40758. else
  40759. super
  40760. end
  40761. end
  40762. end
  40763. end
  40764. end
  40765. end
  40766. module ActiveRecord
  40767. module AttributeMethods
  40768. module TimeZoneConversion
  40769. class Type # :nodoc:
  40770. def initialize(column)
  40771. @column = column
  40772. end
  40773. def type_cast(value)
  40774. value = @column.type_cast(value)
  40775. value.acts_like?(:time) ? value.in_time_zone : value
  40776. end
  40777. def type
  40778. @column.type
  40779. end
  40780. end
  40781. extend ActiveSupport::Concern
  40782. included do
  40783. mattr_accessor :time_zone_aware_attributes, instance_writer: false
  40784. self.time_zone_aware_attributes = false
  40785. class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
  40786. self.skip_time_zone_conversion_for_attributes = []
  40787. end
  40788. module ClassMethods
  40789. protected
  40790. # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
  40791. # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
  40792. def define_method_attribute=(attr_name)
  40793. if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
  40794. method_body, line = <<-EOV, __LINE__ + 1
  40795. def #{attr_name}=(time)
  40796. time_with_zone = time.respond_to?(:in_time_zone) ? time.in_time_zone : nil
  40797. previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
  40798. write_attribute(:#{attr_name}, time)
  40799. #{attr_name}_will_change! if previous_time != time_with_zone
  40800. @attributes_cache["#{attr_name}"] = time_with_zone
  40801. end
  40802. EOV
  40803. generated_attribute_methods.module_eval(method_body, __FILE__, line)
  40804. else
  40805. super
  40806. end
  40807. end
  40808. private
  40809. def create_time_zone_conversion_attribute?(name, column)
  40810. time_zone_aware_attributes &&
  40811. !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
  40812. [:datetime, :timestamp].include?(column.type)
  40813. end
  40814. end
  40815. end
  40816. end
  40817. end
  40818. module ActiveRecord
  40819. module AttributeMethods
  40820. module Write
  40821. extend ActiveSupport::Concern
  40822. included do
  40823. attribute_method_suffix "="
  40824. end
  40825. module ClassMethods
  40826. protected
  40827. # See define_method_attribute in read.rb for an explanation of
  40828. # this code.
  40829. def define_method_attribute=(name)
  40830. safe_name = name.unpack('h*').first
  40831. generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
  40832. def __temp__#{safe_name}=(value)
  40833. write_attribute(AttrNames::ATTR_#{safe_name}, value)
  40834. end
  40835. alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
  40836. undef_method :__temp__#{safe_name}=
  40837. STR
  40838. end
  40839. end
  40840. # Updates the attribute identified by <tt>attr_name</tt> with the
  40841. # specified +value+. Empty strings for fixnum and float columns are
  40842. # turned into +nil+.
  40843. def write_attribute(attr_name, value)
  40844. attr_name = attr_name.to_s
  40845. attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
  40846. @attributes_cache.delete(attr_name)
  40847. column = column_for_attribute(attr_name)
  40848. # If we're dealing with a binary column, write the data to the cache
  40849. # so we don't attempt to typecast multiple times.
  40850. if column && column.binary?
  40851. @attributes_cache[attr_name] = value
  40852. end
  40853. if column || @attributes.has_key?(attr_name)
  40854. @attributes[attr_name] = type_cast_attribute_for_write(column, value)
  40855. else
  40856. raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
  40857. end
  40858. end
  40859. alias_method :raw_write_attribute, :write_attribute
  40860. private
  40861. # Handle *= for method_missing.
  40862. def attribute=(attribute_name, value)
  40863. write_attribute(attribute_name, value)
  40864. end
  40865. def type_cast_attribute_for_write(column, value)
  40866. return value unless column
  40867. column.type_cast_for_write value
  40868. end
  40869. end
  40870. end
  40871. end
  40872. require 'active_support/core_ext/enumerable'
  40873. module ActiveRecord
  40874. # = Active Record Attribute Methods
  40875. module AttributeMethods
  40876. extend ActiveSupport::Concern
  40877. include ActiveModel::AttributeMethods
  40878. included do
  40879. include Read
  40880. include Write
  40881. include BeforeTypeCast
  40882. include Query
  40883. include PrimaryKey
  40884. include TimeZoneConversion
  40885. include Dirty
  40886. include Serialization
  40887. end
  40888. module ClassMethods
  40889. # Generates all the attribute related methods for columns in the database
  40890. # accessors, mutators and query methods.
  40891. def define_attribute_methods # :nodoc:
  40892. # Use a mutex; we don't want two thread simaltaneously trying to define
  40893. # attribute methods.
  40894. @attribute_methods_mutex.synchronize do
  40895. return if attribute_methods_generated?
  40896. superclass.define_attribute_methods unless self == base_class
  40897. super(column_names)
  40898. @attribute_methods_generated = true
  40899. end
  40900. end
  40901. def attribute_methods_generated? # :nodoc:
  40902. @attribute_methods_generated ||= false
  40903. end
  40904. def undefine_attribute_methods # :nodoc:
  40905. super if attribute_methods_generated?
  40906. @attribute_methods_generated = false
  40907. end
  40908. # Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
  40909. # \Active \Record method is defined in the model, otherwise +false+.
  40910. #
  40911. # class Person < ActiveRecord::Base
  40912. # def save
  40913. # 'already defined by Active Record'
  40914. # end
  40915. # end
  40916. #
  40917. # Person.instance_method_already_implemented?(:save)
  40918. # # => ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord
  40919. #
  40920. # Person.instance_method_already_implemented?(:name)
  40921. # # => false
  40922. def instance_method_already_implemented?(method_name)
  40923. if dangerous_attribute_method?(method_name)
  40924. raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord"
  40925. end
  40926. if superclass == Base
  40927. super
  40928. else
  40929. # If B < A and A defines its own attribute method, then we don't want to overwrite that.
  40930. defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods)
  40931. defined && !ActiveRecord::Base.method_defined?(method_name) || super
  40932. end
  40933. end
  40934. # A method name is 'dangerous' if it is already defined by Active Record, but
  40935. # not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
  40936. def dangerous_attribute_method?(name) # :nodoc:
  40937. method_defined_within?(name, Base)
  40938. end
  40939. def method_defined_within?(name, klass, sup = klass.superclass) # :nodoc:
  40940. if klass.method_defined?(name) || klass.private_method_defined?(name)
  40941. if sup.method_defined?(name) || sup.private_method_defined?(name)
  40942. klass.instance_method(name).owner != sup.instance_method(name).owner
  40943. else
  40944. true
  40945. end
  40946. else
  40947. false
  40948. end
  40949. end
  40950. # Returns +true+ if +attribute+ is an attribute method and table exists,
  40951. # +false+ otherwise.
  40952. #
  40953. # class Person < ActiveRecord::Base
  40954. # end
  40955. #
  40956. # Person.attribute_method?('name') # => true
  40957. # Person.attribute_method?(:age=) # => true
  40958. # Person.attribute_method?(:nothing) # => false
  40959. def attribute_method?(attribute)
  40960. super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
  40961. end
  40962. # Returns an array of column names as strings if it's not an abstract class and
  40963. # table exists. Otherwise it returns an empty array.
  40964. #
  40965. # class Person < ActiveRecord::Base
  40966. # end
  40967. #
  40968. # Person.attribute_names
  40969. # # => ["id", "created_at", "updated_at", "name", "age"]
  40970. def attribute_names
  40971. @attribute_names ||= if !abstract_class? && table_exists?
  40972. column_names
  40973. else
  40974. []
  40975. end
  40976. end
  40977. end
  40978. # If we haven't generated any methods yet, generate them, then
  40979. # see if we've created the method we're looking for.
  40980. def method_missing(method, *args, &block) # :nodoc:
  40981. unless self.class.attribute_methods_generated?
  40982. self.class.define_attribute_methods
  40983. if respond_to_without_attributes?(method)
  40984. send(method, *args, &block)
  40985. else
  40986. super
  40987. end
  40988. else
  40989. super
  40990. end
  40991. end
  40992. def attribute_missing(match, *args, &block) # :nodoc:
  40993. if self.class.columns_hash[match.attr_name]
  40994. ActiveSupport::Deprecation.warn(
  40995. "The method `#{match.method_name}', matching the attribute `#{match.attr_name}' has " \
  40996. "dispatched through method_missing. This shouldn't happen, because `#{match.attr_name}' " \
  40997. "is a column of the table. If this error has happened through normal usage of Active " \
  40998. "Record (rather than through your own code or external libraries), please report it as " \
  40999. "a bug."
  41000. )
  41001. end
  41002. super
  41003. end
  41004. # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
  41005. # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
  41006. # which will all return +true+. It also define the attribute methods if they have
  41007. # not been generated.
  41008. #
  41009. # class Person < ActiveRecord::Base
  41010. # end
  41011. #
  41012. # person = Person.new
  41013. # person.respond_to(:name) # => true
  41014. # person.respond_to(:name=) # => true
  41015. # person.respond_to(:name?) # => true
  41016. # person.respond_to('age') # => true
  41017. # person.respond_to('age=') # => true
  41018. # person.respond_to('age?') # => true
  41019. # person.respond_to(:nothing) # => false
  41020. def respond_to?(name, include_private = false)
  41021. self.class.define_attribute_methods unless self.class.attribute_methods_generated?
  41022. super
  41023. end
  41024. # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
  41025. #
  41026. # class Person < ActiveRecord::Base
  41027. # end
  41028. #
  41029. # person = Person.new
  41030. # person.has_attribute?(:name) # => true
  41031. # person.has_attribute?('age') # => true
  41032. # person.has_attribute?(:nothing) # => false
  41033. def has_attribute?(attr_name)
  41034. @attributes.has_key?(attr_name.to_s)
  41035. end
  41036. # Returns an array of names for the attributes available on this object.
  41037. #
  41038. # class Person < ActiveRecord::Base
  41039. # end
  41040. #
  41041. # person = Person.new
  41042. # person.attribute_names
  41043. # # => ["id", "created_at", "updated_at", "name", "age"]
  41044. def attribute_names
  41045. @attributes.keys
  41046. end
  41047. # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
  41048. #
  41049. # class Person < ActiveRecord::Base
  41050. # end
  41051. #
  41052. # person = Person.create(name: 'Francesco', age: 22)
  41053. # person.attributes
  41054. # # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
  41055. def attributes
  41056. attribute_names.each_with_object({}) { |name, attrs|
  41057. attrs[name] = read_attribute(name)
  41058. }
  41059. end
  41060. # Returns an <tt>#inspect</tt>-like string for the value of the
  41061. # attribute +attr_name+. String attributes are truncated upto 50
  41062. # characters, and Date and Time attributes are returned in the
  41063. # <tt>:db</tt> format. Other attributes return the value of
  41064. # <tt>#inspect</tt> without modification.
  41065. #
  41066. # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
  41067. #
  41068. # person.attribute_for_inspect(:name)
  41069. # # => "\"David Heinemeier Hansson David Heinemeier Hansson D...\""
  41070. #
  41071. # person.attribute_for_inspect(:created_at)
  41072. # # => "\"2012-10-22 00:15:07\""
  41073. def attribute_for_inspect(attr_name)
  41074. value = read_attribute(attr_name)
  41075. if value.is_a?(String) && value.length > 50
  41076. "#{value[0..50]}...".inspect
  41077. elsif value.is_a?(Date) || value.is_a?(Time)
  41078. %("#{value.to_s(:db)}")
  41079. else
  41080. value.inspect
  41081. end
  41082. end
  41083. # Returns +true+ if the specified +attribute+ has been set by the user or by a
  41084. # database load and is neither +nil+ nor <tt>empty?</tt> (the latter only applies
  41085. # to objects that respond to <tt>empty?</tt>, most notably Strings). Otherwise, +false+.
  41086. # Note that it always returns +true+ with boolean attributes.
  41087. #
  41088. # class Task < ActiveRecord::Base
  41089. # end
  41090. #
  41091. # person = Task.new(title: '', is_done: false)
  41092. # person.attribute_present?(:title) # => false
  41093. # person.attribute_present?(:is_done) # => true
  41094. # person.name = 'Francesco'
  41095. # person.is_done = true
  41096. # person.attribute_present?(:title) # => true
  41097. # person.attribute_present?(:is_done) # => true
  41098. def attribute_present?(attribute)
  41099. value = read_attribute(attribute)
  41100. !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
  41101. end
  41102. # Returns the column object for the named attribute. Returns +nil+ if the
  41103. # named attribute not exists.
  41104. #
  41105. # class Person < ActiveRecord::Base
  41106. # end
  41107. #
  41108. # person = Person.new
  41109. # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
  41110. # # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
  41111. #
  41112. # person.column_for_attribute(:nothing)
  41113. # # => nil
  41114. def column_for_attribute(name)
  41115. # FIXME: should this return a null object for columns that don't exist?
  41116. self.class.columns_hash[name.to_s]
  41117. end
  41118. # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
  41119. # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). It raises
  41120. # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
  41121. #
  41122. # Alias for the <tt>read_attribute</tt> method.
  41123. #
  41124. # class Person < ActiveRecord::Base
  41125. # belongs_to :organization
  41126. # end
  41127. #
  41128. # person = Person.new(name: 'Francesco', age: '22')
  41129. # person[:name] # => "Francesco"
  41130. # person[:age] # => 22
  41131. #
  41132. # person = Person.select('id').first
  41133. # person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
  41134. # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
  41135. def [](attr_name)
  41136. read_attribute(attr_name) { |n| missing_attribute(n, caller) }
  41137. end
  41138. # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
  41139. # (Alias for the protected <tt>write_attribute</tt> method).
  41140. #
  41141. # class Person < ActiveRecord::Base
  41142. # end
  41143. #
  41144. # person = Person.new
  41145. # person[:age] = '22'
  41146. # person[:age] # => 22
  41147. # person[:age] # => Fixnum
  41148. def []=(attr_name, value)
  41149. write_attribute(attr_name, value)
  41150. end
  41151. protected
  41152. def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
  41153. attribute_names.each do |name|
  41154. attributes[name] = clone_attribute_value(reader_method, name)
  41155. end
  41156. attributes
  41157. end
  41158. def clone_attribute_value(reader_method, attribute_name) # :nodoc:
  41159. value = send(reader_method, attribute_name)
  41160. value.duplicable? ? value.clone : value
  41161. rescue TypeError, NoMethodError
  41162. value
  41163. end
  41164. def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
  41165. arel_attributes_with_values(attributes_for_create(attribute_names))
  41166. end
  41167. def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
  41168. arel_attributes_with_values(attributes_for_update(attribute_names))
  41169. end
  41170. def attribute_method?(attr_name) # :nodoc:
  41171. defined?(@attributes) && @attributes.include?(attr_name)
  41172. end
  41173. private
  41174. # Returns a Hash of the Arel::Attributes and attribute values that have been
  41175. # type casted for use in an Arel insert/update method.
  41176. def arel_attributes_with_values(attribute_names)
  41177. attrs = {}
  41178. arel_table = self.class.arel_table
  41179. attribute_names.each do |name|
  41180. attrs[arel_table[name]] = typecasted_attribute_value(name)
  41181. end
  41182. attrs
  41183. end
  41184. # Filters the primary keys and readonly attributes from the attribute names.
  41185. def attributes_for_update(attribute_names)
  41186. attribute_names.select do |name|
  41187. column_for_attribute(name) && !pk_attribute?(name) && !readonly_attribute?(name)
  41188. end
  41189. end
  41190. # Filters out the primary keys, from the attribute names, when the primary
  41191. # key is to be generated (e.g. the id attribute has no value).
  41192. def attributes_for_create(attribute_names)
  41193. attribute_names.select do |name|
  41194. column_for_attribute(name) && !(pk_attribute?(name) && id.nil?)
  41195. end
  41196. end
  41197. def readonly_attribute?(name)
  41198. self.class.readonly_attributes.include?(name)
  41199. end
  41200. def pk_attribute?(name)
  41201. column_for_attribute(name).primary
  41202. end
  41203. def typecasted_attribute_value(name)
  41204. # FIXME: we need @attributes to be used consistently.
  41205. # If the values stored in @attributes were already typecasted, this code
  41206. # could be simplified
  41207. read_attribute(name)
  41208. end
  41209. end
  41210. end
  41211. module ActiveRecord
  41212. # = Active Record Autosave Association
  41213. #
  41214. # +AutosaveAssociation+ is a module that takes care of automatically saving
  41215. # associated records when their parent is saved. In addition to saving, it
  41216. # also destroys any associated records that were marked for destruction.
  41217. # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
  41218. #
  41219. # Saving of the parent, its associations, and the destruction of marked
  41220. # associations, all happen inside a transaction. This should never leave the
  41221. # database in an inconsistent state.
  41222. #
  41223. # If validations for any of the associations fail, their error messages will
  41224. # be applied to the parent.
  41225. #
  41226. # Note that it also means that associations marked for destruction won't
  41227. # be destroyed directly. They will however still be marked for destruction.
  41228. #
  41229. # Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.
  41230. # When the <tt>:autosave</tt> option is not present new associations are saved.
  41231. #
  41232. # == Validation
  41233. #
  41234. # Children records are validated unless <tt>:validate</tt> is +false+.
  41235. #
  41236. # == Callbacks
  41237. #
  41238. # Association with autosave option defines several callbacks on your
  41239. # model (before_save, after_create, after_update). Please note that
  41240. # callbacks are executed in the order they were defined in
  41241. # model. You should avoid modifying the association content, before
  41242. # autosave callbacks are executed. Placing your callbacks after
  41243. # associations is usually a good practice.
  41244. #
  41245. # === One-to-one Example
  41246. #
  41247. # class Post
  41248. # has_one :author, autosave: true
  41249. # end
  41250. #
  41251. # Saving changes to the parent and its associated model can now be performed
  41252. # automatically _and_ atomically:
  41253. #
  41254. # post = Post.find(1)
  41255. # post.title # => "The current global position of migrating ducks"
  41256. # post.author.name # => "alloy"
  41257. #
  41258. # post.title = "On the migration of ducks"
  41259. # post.author.name = "Eloy Duran"
  41260. #
  41261. # post.save
  41262. # post.reload
  41263. # post.title # => "On the migration of ducks"
  41264. # post.author.name # => "Eloy Duran"
  41265. #
  41266. # Destroying an associated model, as part of the parent's save action, is as
  41267. # simple as marking it for destruction:
  41268. #
  41269. # post.author.mark_for_destruction
  41270. # post.author.marked_for_destruction? # => true
  41271. #
  41272. # Note that the model is _not_ yet removed from the database:
  41273. #
  41274. # id = post.author.id
  41275. # Author.find_by_id(id).nil? # => false
  41276. #
  41277. # post.save
  41278. # post.reload.author # => nil
  41279. #
  41280. # Now it _is_ removed from the database:
  41281. #
  41282. # Author.find_by_id(id).nil? # => true
  41283. #
  41284. # === One-to-many Example
  41285. #
  41286. # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
  41287. #
  41288. # class Post
  41289. # has_many :comments # :autosave option is not declared
  41290. # end
  41291. #
  41292. # post = Post.new(title: 'ruby rocks')
  41293. # post.comments.build(body: 'hello world')
  41294. # post.save # => saves both post and comment
  41295. #
  41296. # post = Post.create(title: 'ruby rocks')
  41297. # post.comments.build(body: 'hello world')
  41298. # post.save # => saves both post and comment
  41299. #
  41300. # post = Post.create(title: 'ruby rocks')
  41301. # post.comments.create(body: 'hello world')
  41302. # post.save # => saves both post and comment
  41303. #
  41304. # When <tt>:autosave</tt> is true all children are saved, no matter whether they
  41305. # are new records or not:
  41306. #
  41307. # class Post
  41308. # has_many :comments, autosave: true
  41309. # end
  41310. #
  41311. # post = Post.create(title: 'ruby rocks')
  41312. # post.comments.create(body: 'hello world')
  41313. # post.comments[0].body = 'hi everyone'
  41314. # post.save # => saves both post and comment, with 'hi everyone' as body
  41315. #
  41316. # Destroying one of the associated models as part of the parent's save action
  41317. # is as simple as marking it for destruction:
  41318. #
  41319. # post.comments.last.mark_for_destruction
  41320. # post.comments.last.marked_for_destruction? # => true
  41321. # post.comments.length # => 2
  41322. #
  41323. # Note that the model is _not_ yet removed from the database:
  41324. #
  41325. # id = post.comments.last.id
  41326. # Comment.find_by_id(id).nil? # => false
  41327. #
  41328. # post.save
  41329. # post.reload.comments.length # => 1
  41330. #
  41331. # Now it _is_ removed from the database:
  41332. #
  41333. # Comment.find_by_id(id).nil? # => true
  41334. module AutosaveAssociation
  41335. extend ActiveSupport::Concern
  41336. module AssociationBuilderExtension #:nodoc:
  41337. def build
  41338. model.send(:add_autosave_association_callbacks, reflection)
  41339. super
  41340. end
  41341. end
  41342. included do
  41343. Associations::Builder::Association.class_eval do
  41344. self.valid_options << :autosave
  41345. include AssociationBuilderExtension
  41346. end
  41347. end
  41348. module ClassMethods
  41349. private
  41350. def define_non_cyclic_method(name, reflection, &block)
  41351. define_method(name) do |*args|
  41352. result = true; @_already_called ||= {}
  41353. # Loop prevention for validation of associations
  41354. unless @_already_called[[name, reflection.name]]
  41355. begin
  41356. @_already_called[[name, reflection.name]]=true
  41357. result = instance_eval(&block)
  41358. ensure
  41359. @_already_called[[name, reflection.name]]=false
  41360. end
  41361. end
  41362. result
  41363. end
  41364. end
  41365. # Adds validation and save callbacks for the association as specified by
  41366. # the +reflection+.
  41367. #
  41368. # For performance reasons, we don't check whether to validate at runtime.
  41369. # However the validation and callback methods are lazy and those methods
  41370. # get created when they are invoked for the very first time. However,
  41371. # this can change, for instance, when using nested attributes, which is
  41372. # called _after_ the association has been defined. Since we don't want
  41373. # the callbacks to get defined multiple times, there are guards that
  41374. # check if the save or validation methods have already been defined
  41375. # before actually defining them.
  41376. def add_autosave_association_callbacks(reflection)
  41377. save_method = :"autosave_associated_records_for_#{reflection.name}"
  41378. validation_method = :"validate_associated_records_for_#{reflection.name}"
  41379. collection = reflection.collection?
  41380. unless method_defined?(save_method)
  41381. if collection
  41382. before_save :before_save_collection_association
  41383. define_non_cyclic_method(save_method, reflection) { save_collection_association(reflection) }
  41384. # Doesn't use after_save as that would save associations added in after_create/after_update twice
  41385. after_create save_method
  41386. after_update save_method
  41387. elsif reflection.macro == :has_one
  41388. define_method(save_method) { save_has_one_association(reflection) }
  41389. # Configures two callbacks instead of a single after_save so that
  41390. # the model may rely on their execution order relative to its
  41391. # own callbacks.
  41392. #
  41393. # For example, given that after_creates run before after_saves, if
  41394. # we configured instead an after_save there would be no way to fire
  41395. # a custom after_create callback after the child association gets
  41396. # created.
  41397. after_create save_method
  41398. after_update save_method
  41399. else
  41400. define_non_cyclic_method(save_method, reflection) { save_belongs_to_association(reflection) }
  41401. before_save save_method
  41402. end
  41403. end
  41404. if reflection.validate? && !method_defined?(validation_method)
  41405. method = (collection ? :validate_collection_association : :validate_single_association)
  41406. define_non_cyclic_method(validation_method, reflection) { send(method, reflection) }
  41407. validate validation_method
  41408. end
  41409. end
  41410. end
  41411. # Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
  41412. def reload(options = nil)
  41413. @marked_for_destruction = false
  41414. super
  41415. end
  41416. # Marks this record to be destroyed as part of the parents save transaction.
  41417. # This does _not_ actually destroy the record instantly, rather child record will be destroyed
  41418. # when <tt>parent.save</tt> is called.
  41419. #
  41420. # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
  41421. def mark_for_destruction
  41422. @marked_for_destruction = true
  41423. end
  41424. # Returns whether or not this record will be destroyed as part of the parents save transaction.
  41425. #
  41426. # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
  41427. def marked_for_destruction?
  41428. @marked_for_destruction
  41429. end
  41430. # Returns whether or not this record has been changed in any way (including whether
  41431. # any of its nested autosave associations are likewise changed)
  41432. def changed_for_autosave?
  41433. new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
  41434. end
  41435. private
  41436. # Returns the record for an association collection that should be validated
  41437. # or saved. If +autosave+ is +false+ only new records will be returned,
  41438. # unless the parent is/was a new record itself.
  41439. def associated_records_to_validate_or_save(association, new_record, autosave)
  41440. if new_record
  41441. association && association.target
  41442. elsif autosave
  41443. association.target.find_all { |record| record.changed_for_autosave? }
  41444. else
  41445. association.target.find_all { |record| record.new_record? }
  41446. end
  41447. end
  41448. # go through nested autosave associations that are loaded in memory (without loading
  41449. # any new ones), and return true if is changed for autosave
  41450. def nested_records_changed_for_autosave?
  41451. self.class.reflect_on_all_autosave_associations.any? do |reflection|
  41452. association = association_instance_get(reflection.name)
  41453. association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
  41454. end
  41455. end
  41456. # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
  41457. # turned on for the association.
  41458. def validate_single_association(reflection)
  41459. association = association_instance_get(reflection.name)
  41460. record = association && association.reader
  41461. association_valid?(reflection, record) if record
  41462. end
  41463. # Validate the associated records if <tt>:validate</tt> or
  41464. # <tt>:autosave</tt> is turned on for the association specified by
  41465. # +reflection+.
  41466. def validate_collection_association(reflection)
  41467. if association = association_instance_get(reflection.name)
  41468. if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
  41469. records.each { |record| association_valid?(reflection, record) }
  41470. end
  41471. end
  41472. end
  41473. # Returns whether or not the association is valid and applies any errors to
  41474. # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
  41475. # enabled records if they're marked_for_destruction? or destroyed.
  41476. def association_valid?(reflection, record)
  41477. return true if record.destroyed? || record.marked_for_destruction?
  41478. unless valid = record.valid?(validation_context)
  41479. if reflection.options[:autosave]
  41480. record.errors.each do |attribute, message|
  41481. attribute = "#{reflection.name}.#{attribute}"
  41482. errors[attribute] << message
  41483. errors[attribute].uniq!
  41484. end
  41485. else
  41486. errors.add(reflection.name)
  41487. end
  41488. end
  41489. valid
  41490. end
  41491. # Is used as a before_save callback to check while saving a collection
  41492. # association whether or not the parent was a new record before saving.
  41493. def before_save_collection_association
  41494. @new_record_before_save = new_record?
  41495. true
  41496. end
  41497. # Saves any new associated records, or all loaded autosave associations if
  41498. # <tt>:autosave</tt> is enabled on the association.
  41499. #
  41500. # In addition, it destroys all children that were marked for destruction
  41501. # with mark_for_destruction.
  41502. #
  41503. # This all happens inside a transaction, _if_ the Transactions module is included into
  41504. # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
  41505. def save_collection_association(reflection)
  41506. if association = association_instance_get(reflection.name)
  41507. autosave = reflection.options[:autosave]
  41508. if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
  41509. records_to_destroy = []
  41510. records.each do |record|
  41511. next if record.destroyed?
  41512. saved = true
  41513. if autosave && record.marked_for_destruction?
  41514. records_to_destroy << record
  41515. elsif autosave != false && (@new_record_before_save || record.new_record?)
  41516. if autosave
  41517. saved = association.insert_record(record, false)
  41518. else
  41519. association.insert_record(record) unless reflection.nested?
  41520. end
  41521. elsif autosave
  41522. saved = record.save(:validate => false)
  41523. end
  41524. raise ActiveRecord::Rollback unless saved
  41525. end
  41526. records_to_destroy.each do |record|
  41527. association.destroy(record)
  41528. end
  41529. end
  41530. # reconstruct the scope now that we know the owner's id
  41531. association.send(:reset_scope) if association.respond_to?(:reset_scope)
  41532. end
  41533. end
  41534. # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
  41535. # on the association.
  41536. #
  41537. # In addition, it will destroy the association if it was marked for
  41538. # destruction with mark_for_destruction.
  41539. #
  41540. # This all happens inside a transaction, _if_ the Transactions module is included into
  41541. # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
  41542. def save_has_one_association(reflection)
  41543. association = association_instance_get(reflection.name)
  41544. record = association && association.load_target
  41545. if record && !record.destroyed?
  41546. autosave = reflection.options[:autosave]
  41547. if autosave && record.marked_for_destruction?
  41548. record.destroy
  41549. else
  41550. key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
  41551. if autosave != false && (new_record? || record.new_record? || record[reflection.foreign_key] != key || autosave)
  41552. unless reflection.through_reflection
  41553. record[reflection.foreign_key] = key
  41554. end
  41555. saved = record.save(:validate => !autosave)
  41556. raise ActiveRecord::Rollback if !saved && autosave
  41557. saved
  41558. end
  41559. end
  41560. end
  41561. end
  41562. # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
  41563. #
  41564. # In addition, it will destroy the association if it was marked for destruction.
  41565. def save_belongs_to_association(reflection)
  41566. association = association_instance_get(reflection.name)
  41567. record = association && association.load_target
  41568. if record && !record.destroyed?
  41569. autosave = reflection.options[:autosave]
  41570. if autosave && record.marked_for_destruction?
  41571. self[reflection.foreign_key] = nil
  41572. record.destroy
  41573. elsif autosave != false
  41574. saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
  41575. if association.updated?
  41576. association_id = record.send(reflection.options[:primary_key] || :id)
  41577. self[reflection.foreign_key] = association_id
  41578. association.loaded!
  41579. end
  41580. saved if autosave
  41581. end
  41582. end
  41583. end
  41584. end
  41585. end
  41586. require 'yaml'
  41587. require 'set'
  41588. require 'active_support/benchmarkable'
  41589. require 'active_support/dependencies'
  41590. require 'active_support/descendants_tracker'
  41591. require 'active_support/time'
  41592. require 'active_support/core_ext/class/attribute_accessors'
  41593. require 'active_support/core_ext/class/delegating_attributes'
  41594. require 'active_support/core_ext/array/extract_options'
  41595. require 'active_support/core_ext/hash/deep_merge'
  41596. require 'active_support/core_ext/hash/slice'
  41597. require 'active_support/core_ext/string/behavior'
  41598. require 'active_support/core_ext/kernel/singleton_class'
  41599. require 'active_support/core_ext/module/introspection'
  41600. require 'active_support/core_ext/object/duplicable'
  41601. require 'active_support/core_ext/class/subclasses'
  41602. require 'arel'
  41603. require 'active_record/errors'
  41604. require 'active_record/log_subscriber'
  41605. require 'active_record/explain_subscriber'
  41606. module ActiveRecord #:nodoc:
  41607. # = Active Record
  41608. #
  41609. # Active Record objects don't specify their attributes directly, but rather infer them from
  41610. # the table definition with which they're linked. Adding, removing, and changing attributes
  41611. # and their type is done directly in the database. Any change is instantly reflected in the
  41612. # Active Record objects. The mapping that binds a given Active Record class to a certain
  41613. # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
  41614. #
  41615. # See the mapping rules in table_name and the full example in link:files/activerecord/README_rdoc.html for more insight.
  41616. #
  41617. # == Creation
  41618. #
  41619. # Active Records accept constructor parameters either in a hash or as a block. The hash
  41620. # method is especially useful when you're receiving the data from somewhere else, like an
  41621. # HTTP request. It works like this:
  41622. #
  41623. # user = User.new(name: "David", occupation: "Code Artist")
  41624. # user.name # => "David"
  41625. #
  41626. # You can also use block initialization:
  41627. #
  41628. # user = User.new do |u|
  41629. # u.name = "David"
  41630. # u.occupation = "Code Artist"
  41631. # end
  41632. #
  41633. # And of course you can just create a bare object and specify the attributes after the fact:
  41634. #
  41635. # user = User.new
  41636. # user.name = "David"
  41637. # user.occupation = "Code Artist"
  41638. #
  41639. # == Conditions
  41640. #
  41641. # Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
  41642. # The array form is to be used when the condition input is tainted and requires sanitization. The string form can
  41643. # be used for statements that don't involve tainted data. The hash form works much like the array form, except
  41644. # only equality and range is possible. Examples:
  41645. #
  41646. # class User < ActiveRecord::Base
  41647. # def self.authenticate_unsafely(user_name, password)
  41648. # where("user_name = '#{user_name}' AND password = '#{password}'").first
  41649. # end
  41650. #
  41651. # def self.authenticate_safely(user_name, password)
  41652. # where("user_name = ? AND password = ?", user_name, password).first
  41653. # end
  41654. #
  41655. # def self.authenticate_safely_simply(user_name, password)
  41656. # where(user_name: user_name, password: password).first
  41657. # end
  41658. # end
  41659. #
  41660. # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query
  41661. # and is thus susceptible to SQL-injection attacks if the <tt>user_name</tt> and +password+
  41662. # parameters come directly from an HTTP request. The <tt>authenticate_safely</tt> and
  41663. # <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+
  41664. # before inserting them in the query, which will ensure that an attacker can't escape the
  41665. # query and fake the login (or worse).
  41666. #
  41667. # When using multiple parameters in the conditions, it can easily become hard to read exactly
  41668. # what the fourth or fifth question mark is supposed to represent. In those cases, you can
  41669. # resort to named bind variables instead. That's done by replacing the question marks with
  41670. # symbols and supplying a hash with values for the matching symbol keys:
  41671. #
  41672. # Company.where(
  41673. # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
  41674. # { id: 3, name: "37signals", division: "First", accounting_date: '2005-01-01' }
  41675. # ).first
  41676. #
  41677. # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
  41678. # operator. For instance:
  41679. #
  41680. # Student.where(first_name: "Harvey", status: 1)
  41681. # Student.where(params[:student])
  41682. #
  41683. # A range may be used in the hash to use the SQL BETWEEN operator:
  41684. #
  41685. # Student.where(grade: 9..12)
  41686. #
  41687. # An array may be used in the hash to use the SQL IN operator:
  41688. #
  41689. # Student.where(grade: [9,11,12])
  41690. #
  41691. # When joining tables, nested hashes or keys written in the form 'table_name.column_name'
  41692. # can be used to qualify the table name of a particular condition. For instance:
  41693. #
  41694. # Student.joins(:schools).where(schools: { category: 'public' })
  41695. # Student.joins(:schools).where('schools.category' => 'public' )
  41696. #
  41697. # == Overwriting default accessors
  41698. #
  41699. # All column values are automatically available through basic accessors on the Active Record
  41700. # object, but sometimes you want to specialize this behavior. This can be done by overwriting
  41701. # the default accessors (using the same name as the attribute) and calling
  41702. # <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually
  41703. # change things.
  41704. #
  41705. # class Song < ActiveRecord::Base
  41706. # # Uses an integer of seconds to hold the length of the song
  41707. #
  41708. # def length=(minutes)
  41709. # write_attribute(:length, minutes.to_i * 60)
  41710. # end
  41711. #
  41712. # def length
  41713. # read_attribute(:length) / 60
  41714. # end
  41715. # end
  41716. #
  41717. # You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
  41718. # instead of <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
  41719. #
  41720. # == Attribute query methods
  41721. #
  41722. # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
  41723. # Query methods allow you to test whether an attribute value is present.
  41724. #
  41725. # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
  41726. # to determine whether the user has a name:
  41727. #
  41728. # user = User.new(name: "David")
  41729. # user.name? # => true
  41730. #
  41731. # anonymous = User.new(name: "")
  41732. # anonymous.name? # => false
  41733. #
  41734. # == Accessing attributes before they have been typecasted
  41735. #
  41736. # Sometimes you want to be able to read the raw attribute data without having the column-determined
  41737. # typecast run its course first. That can be done by using the <tt><attribute>_before_type_cast</tt>
  41738. # accessors that all attributes have. For example, if your Account model has a <tt>balance</tt> attribute,
  41739. # you can call <tt>account.balance_before_type_cast</tt> or <tt>account.id_before_type_cast</tt>.
  41740. #
  41741. # This is especially useful in validation situations where the user might supply a string for an
  41742. # integer field and you want to display the original string back in an error message. Accessing the
  41743. # attribute normally would typecast the string to 0, which isn't what you want.
  41744. #
  41745. # == Dynamic attribute-based finders
  41746. #
  41747. # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects
  41748. # by simple queries without turning to SQL. They work by appending the name of an attribute
  41749. # to <tt>find_by_</tt> like <tt>Person.find_by_user_name</tt>.
  41750. # Instead of writing <tt>Person.where(user_name: user_name).first</tt>, you just do
  41751. # <tt>Person.find_by_user_name(user_name)</tt>.
  41752. #
  41753. # It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an
  41754. # <tt>ActiveRecord::RecordNotFound</tt> error if they do not return any records,
  41755. # like <tt>Person.find_by_last_name!</tt>.
  41756. #
  41757. # It's also possible to use multiple attributes in the same find by separating them with "_and_".
  41758. #
  41759. # Person.where(user_name: user_name, password: password).first
  41760. # Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
  41761. #
  41762. # It's even possible to call these dynamic finder methods on relations and named scopes.
  41763. #
  41764. # Payment.order("created_on").find_by_amount(50)
  41765. #
  41766. # == Saving arrays, hashes, and other non-mappable objects in text columns
  41767. #
  41768. # Active Record can serialize any object in text columns using YAML. To do so, you must
  41769. # specify this with a call to the class method +serialize+.
  41770. # This makes it possible to store arrays, hashes, and other non-mappable objects without doing
  41771. # any additional work.
  41772. #
  41773. # class User < ActiveRecord::Base
  41774. # serialize :preferences
  41775. # end
  41776. #
  41777. # user = User.create(preferences: { "background" => "black", "display" => large })
  41778. # User.find(user.id).preferences # => { "background" => "black", "display" => large }
  41779. #
  41780. # You can also specify a class option as the second parameter that'll raise an exception
  41781. # if a serialized object is retrieved as a descendant of a class not in the hierarchy.
  41782. #
  41783. # class User < ActiveRecord::Base
  41784. # serialize :preferences, Hash
  41785. # end
  41786. #
  41787. # user = User.create(preferences: %w( one two three ))
  41788. # User.find(user.id).preferences # raises SerializationTypeMismatch
  41789. #
  41790. # When you specify a class option, the default value for that attribute will be a new
  41791. # instance of that class.
  41792. #
  41793. # class User < ActiveRecord::Base
  41794. # serialize :preferences, OpenStruct
  41795. # end
  41796. #
  41797. # user = User.new
  41798. # user.preferences.theme_color = "red"
  41799. #
  41800. #
  41801. # == Single table inheritance
  41802. #
  41803. # Active Record allows inheritance by storing the name of the class in a column that by
  41804. # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
  41805. # This means that an inheritance looking like this:
  41806. #
  41807. # class Company < ActiveRecord::Base; end
  41808. # class Firm < Company; end
  41809. # class Client < Company; end
  41810. # class PriorityClient < Client; end
  41811. #
  41812. # When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
  41813. # the companies table with type = "Firm". You can then fetch this row again using
  41814. # <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
  41815. #
  41816. # If you don't have a type column defined in your table, single-table inheritance won't
  41817. # be triggered. In that case, it'll work just like normal subclasses with no special magic
  41818. # for differentiating between them or reloading the right type with find.
  41819. #
  41820. # Note, all the attributes for all the cases are kept in the same table. Read more:
  41821. # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
  41822. #
  41823. # == Connection to multiple databases in different models
  41824. #
  41825. # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved
  41826. # by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this
  41827. # connection. But you can also set a class-specific connection. For example, if Course is an
  41828. # ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
  41829. # and Course and all of its subclasses will use this connection instead.
  41830. #
  41831. # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is
  41832. # a Hash indexed by the class. If a connection is requested, the retrieve_connection method
  41833. # will go up the class-hierarchy until a connection is found in the connection pool.
  41834. #
  41835. # == Exceptions
  41836. #
  41837. # * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
  41838. # * AdapterNotSpecified - The configuration hash used in <tt>establish_connection</tt> didn't include an
  41839. # <tt>:adapter</tt> key.
  41840. # * AdapterNotFound - The <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a
  41841. # non-existent adapter
  41842. # (or a bad spelling of an existing one).
  41843. # * AssociationTypeMismatch - The object assigned to the association wasn't of the type
  41844. # specified in the association definition.
  41845. # * AttributeAssignmentError - An error occurred while doing a mass assignment through the
  41846. # <tt>attributes=</tt> method.
  41847. # You can inspect the +attribute+ property of the exception object to determine which attribute
  41848. # triggered the error.
  41849. # * ConnectionNotEstablished - No connection has been established. Use <tt>establish_connection</tt>
  41850. # before querying.
  41851. # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
  41852. # <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of
  41853. # AttributeAssignmentError
  41854. # objects that should be inspected to determine which attributes triggered the errors.
  41855. # * RecordInvalid - raised by save! and create! when the record is invalid.
  41856. # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
  41857. # or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
  41858. # nothing was found, please check its documentation for further details.
  41859. # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
  41860. # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
  41861. #
  41862. # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
  41863. # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
  41864. # instances in the current object space.
  41865. class Base
  41866. extend ActiveModel::Naming
  41867. extend ActiveSupport::Benchmarkable
  41868. extend ActiveSupport::DescendantsTracker
  41869. extend ConnectionHandling
  41870. extend QueryCache::ClassMethods
  41871. extend Querying
  41872. extend Translation
  41873. extend DynamicMatchers
  41874. extend Explain
  41875. include Persistence
  41876. include ReadonlyAttributes
  41877. include ModelSchema
  41878. include Inheritance
  41879. include Scoping
  41880. include Sanitization
  41881. include AttributeAssignment
  41882. include ActiveModel::Conversion
  41883. include Integration
  41884. include Validations
  41885. include CounterCache
  41886. include Locking::Optimistic
  41887. include Locking::Pessimistic
  41888. include AttributeMethods
  41889. include Callbacks
  41890. include Timestamp
  41891. include Associations
  41892. include ActiveModel::SecurePassword
  41893. include AutosaveAssociation
  41894. include NestedAttributes
  41895. include Aggregations
  41896. include Transactions
  41897. include Reflection
  41898. include Serialization
  41899. include Store
  41900. include Core
  41901. end
  41902. ActiveSupport.run_load_hooks(:active_record, Base)
  41903. end
  41904. module ActiveRecord
  41905. # = Active Record Callbacks
  41906. #
  41907. # Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
  41908. # before or after an alteration of the object state. This can be used to make sure that associated and
  41909. # dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
  41910. # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
  41911. # the <tt>Base#save</tt> call for a new record:
  41912. #
  41913. # * (-) <tt>save</tt>
  41914. # * (-) <tt>valid</tt>
  41915. # * (1) <tt>before_validation</tt>
  41916. # * (-) <tt>validate</tt>
  41917. # * (2) <tt>after_validation</tt>
  41918. # * (3) <tt>before_save</tt>
  41919. # * (4) <tt>before_create</tt>
  41920. # * (-) <tt>create</tt>
  41921. # * (5) <tt>after_create</tt>
  41922. # * (6) <tt>after_save</tt>
  41923. # * (7) <tt>after_commit</tt>
  41924. #
  41925. # Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued.
  41926. # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
  41927. # <tt>after_rollback</tt>.
  41928. #
  41929. # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
  41930. # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
  41931. # are instantiated as well.
  41932. #
  41933. # That's a total of twelve callbacks, which gives you immense power to react and prepare for each state in the
  41934. # Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
  41935. # except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
  41936. #
  41937. # Examples:
  41938. # class CreditCard < ActiveRecord::Base
  41939. # # Strip everything but digits, so the user can specify "555 234 34" or
  41940. # # "5552-3434" and both will mean "55523434"
  41941. # before_validation(on: :create) do
  41942. # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
  41943. # end
  41944. # end
  41945. #
  41946. # class Subscription < ActiveRecord::Base
  41947. # before_create :record_signup
  41948. #
  41949. # private
  41950. # def record_signup
  41951. # self.signed_up_on = Date.today
  41952. # end
  41953. # end
  41954. #
  41955. # class Firm < ActiveRecord::Base
  41956. # # Destroys the associated clients and people when the firm is destroyed
  41957. # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
  41958. # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
  41959. # end
  41960. #
  41961. # == Inheritable callback queues
  41962. #
  41963. # Besides the overwritable callback methods, it's also possible to register callbacks through the
  41964. # use of the callback macros. Their main advantage is that the macros add behavior into a callback
  41965. # queue that is kept intact down through an inheritance hierarchy.
  41966. #
  41967. # class Topic < ActiveRecord::Base
  41968. # before_destroy :destroy_author
  41969. # end
  41970. #
  41971. # class Reply < Topic
  41972. # before_destroy :destroy_readers
  41973. # end
  41974. #
  41975. # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
  41976. # run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation
  41977. # where the +before_destroy+ method is overridden:
  41978. #
  41979. # class Topic < ActiveRecord::Base
  41980. # def before_destroy() destroy_author end
  41981. # end
  41982. #
  41983. # class Reply < Topic
  41984. # def before_destroy() destroy_readers end
  41985. # end
  41986. #
  41987. # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
  41988. # So, use the callback macros when you want to ensure that a certain callback is called for the entire
  41989. # hierarchy, and use the regular overwriteable methods when you want to leave it up to each descendant
  41990. # to decide whether they want to call +super+ and trigger the inherited callbacks.
  41991. #
  41992. # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
  41993. # callbacks before specifying the associations. Otherwise, you might trigger the loading of a
  41994. # child before the parent has registered the callbacks and they won't be inherited.
  41995. #
  41996. # == Types of callbacks
  41997. #
  41998. # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
  41999. # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
  42000. # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
  42001. # creating mix-ins), and inline eval methods are deprecated.
  42002. #
  42003. # The method reference callbacks work by specifying a protected or private method available in the object, like this:
  42004. #
  42005. # class Topic < ActiveRecord::Base
  42006. # before_destroy :delete_parents
  42007. #
  42008. # private
  42009. # def delete_parents
  42010. # self.class.delete_all "parent_id = #{id}"
  42011. # end
  42012. # end
  42013. #
  42014. # The callback objects have methods named after the callback called with the record as the only parameter, such as:
  42015. #
  42016. # class BankAccount < ActiveRecord::Base
  42017. # before_save EncryptionWrapper.new
  42018. # after_save EncryptionWrapper.new
  42019. # after_initialize EncryptionWrapper.new
  42020. # end
  42021. #
  42022. # class EncryptionWrapper
  42023. # def before_save(record)
  42024. # record.credit_card_number = encrypt(record.credit_card_number)
  42025. # end
  42026. #
  42027. # def after_save(record)
  42028. # record.credit_card_number = decrypt(record.credit_card_number)
  42029. # end
  42030. #
  42031. # alias_method :after_find, :after_save
  42032. #
  42033. # private
  42034. # def encrypt(value)
  42035. # # Secrecy is committed
  42036. # end
  42037. #
  42038. # def decrypt(value)
  42039. # # Secrecy is unveiled
  42040. # end
  42041. # end
  42042. #
  42043. # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
  42044. # a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
  42045. # initialization data such as the name of the attribute to work with:
  42046. #
  42047. # class BankAccount < ActiveRecord::Base
  42048. # before_save EncryptionWrapper.new("credit_card_number")
  42049. # after_save EncryptionWrapper.new("credit_card_number")
  42050. # after_initialize EncryptionWrapper.new("credit_card_number")
  42051. # end
  42052. #
  42053. # class EncryptionWrapper
  42054. # def initialize(attribute)
  42055. # @attribute = attribute
  42056. # end
  42057. #
  42058. # def before_save(record)
  42059. # record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
  42060. # end
  42061. #
  42062. # def after_save(record)
  42063. # record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
  42064. # end
  42065. #
  42066. # alias_method :after_find, :after_save
  42067. #
  42068. # private
  42069. # def encrypt(value)
  42070. # # Secrecy is committed
  42071. # end
  42072. #
  42073. # def decrypt(value)
  42074. # # Secrecy is unveiled
  42075. # end
  42076. # end
  42077. #
  42078. # The callback macros usually accept a symbol for the method they're supposed to run, but you can also
  42079. # pass a "method string", which will then be evaluated within the binding of the callback. Example:
  42080. #
  42081. # class Topic < ActiveRecord::Base
  42082. # before_destroy 'self.class.delete_all "parent_id = #{id}"'
  42083. # end
  42084. #
  42085. # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
  42086. # is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
  42087. #
  42088. # class Topic < ActiveRecord::Base
  42089. # before_destroy 'self.class.delete_all "parent_id = #{id}"',
  42090. # 'puts "Evaluated after parents are destroyed"'
  42091. # end
  42092. #
  42093. # == <tt>before_validation*</tt> returning statements
  42094. #
  42095. # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be
  42096. # aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
  42097. # ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
  42098. #
  42099. # == Canceling callbacks
  42100. #
  42101. # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
  42102. # cancelled. If an <tt>after_*</tt> callback returns +false+, all the later callbacks are cancelled.
  42103. # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
  42104. # methods on the model, which are called last.
  42105. #
  42106. # == Ordering callbacks
  42107. #
  42108. # Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
  42109. # callback (+log_children+ in this case) should be executed before the children get destroyed by the +dependent: destroy+ option.
  42110. #
  42111. # Let's look at the code below:
  42112. #
  42113. # class Topic < ActiveRecord::Base
  42114. # has_many :children, dependent: destroy
  42115. #
  42116. # before_destroy :log_children
  42117. #
  42118. # private
  42119. # def log_children
  42120. # # Child processing
  42121. # end
  42122. # end
  42123. #
  42124. # In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
  42125. # because the +destroy+ callback gets executed first. You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
  42126. #
  42127. # class Topic < ActiveRecord::Base
  42128. # has_many :children, dependent: destroy
  42129. #
  42130. # before_destroy :log_children, prepend: true
  42131. #
  42132. # private
  42133. # def log_children
  42134. # # Child processing
  42135. # end
  42136. # end
  42137. #
  42138. # This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
  42139. #
  42140. # == Transactions
  42141. #
  42142. # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
  42143. # within a transaction. That includes <tt>after_*</tt> hooks. If everything
  42144. # goes fine a COMMIT is executed once the chain has been completed.
  42145. #
  42146. # If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
  42147. # can also trigger a ROLLBACK raising an exception in any of the callbacks,
  42148. # including <tt>after_*</tt> hooks. Note, however, that in that case the client
  42149. # needs to be aware of it because an ordinary +save+ will raise such exception
  42150. # instead of quietly returning +false+.
  42151. #
  42152. # == Debugging callbacks
  42153. #
  42154. # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
  42155. # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
  42156. # defines what part of the chain the callback runs in.
  42157. #
  42158. # To find all callbacks in the before_save callback chain:
  42159. #
  42160. # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
  42161. #
  42162. # Returns an array of callback objects that form the before_save chain.
  42163. #
  42164. # To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
  42165. #
  42166. # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
  42167. #
  42168. # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
  42169. #
  42170. module Callbacks
  42171. extend ActiveSupport::Concern
  42172. CALLBACKS = [
  42173. :after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
  42174. :before_save, :around_save, :after_save, :before_create, :around_create,
  42175. :after_create, :before_update, :around_update, :after_update,
  42176. :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
  42177. ]
  42178. module ClassMethods
  42179. include ActiveModel::Callbacks
  42180. end
  42181. included do
  42182. include ActiveModel::Validations::Callbacks
  42183. define_model_callbacks :initialize, :find, :touch, :only => :after
  42184. define_model_callbacks :save, :create, :update, :destroy
  42185. end
  42186. def destroy #:nodoc:
  42187. run_callbacks(:destroy) { super }
  42188. end
  42189. def touch(*) #:nodoc:
  42190. run_callbacks(:touch) { super }
  42191. end
  42192. private
  42193. def create_or_update #:nodoc:
  42194. run_callbacks(:save) { super }
  42195. end
  42196. def create_record #:nodoc:
  42197. run_callbacks(:create) { super }
  42198. end
  42199. def update_record(*) #:nodoc:
  42200. run_callbacks(:update) { super }
  42201. end
  42202. end
  42203. end
  42204. require 'yaml'
  42205. module ActiveRecord
  42206. module Coders # :nodoc:
  42207. class YAMLColumn # :nodoc:
  42208. RESCUE_ERRORS = [ ArgumentError, Psych::SyntaxError ]
  42209. attr_accessor :object_class
  42210. def initialize(object_class = Object)
  42211. @object_class = object_class
  42212. end
  42213. def dump(obj)
  42214. return if obj.nil?
  42215. unless obj.is_a?(object_class)
  42216. raise SerializationTypeMismatch,
  42217. "Attribute was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
  42218. end
  42219. YAML.dump obj
  42220. end
  42221. def load(yaml)
  42222. return object_class.new if object_class != Object && yaml.nil?
  42223. return yaml unless yaml.is_a?(String) && yaml =~ /^---/
  42224. begin
  42225. obj = YAML.load(yaml)
  42226. unless obj.is_a?(object_class) || obj.nil?
  42227. raise SerializationTypeMismatch,
  42228. "Attribute was supposed to be a #{object_class}, but was a #{obj.class}"
  42229. end
  42230. obj ||= object_class.new if object_class != Object
  42231. obj
  42232. rescue *RESCUE_ERRORS
  42233. yaml
  42234. end
  42235. end
  42236. end
  42237. end
  42238. end
  42239. require 'thread'
  42240. require 'thread_safe'
  42241. require 'monitor'
  42242. require 'set'
  42243. module ActiveRecord
  42244. # Raised when a connection could not be obtained within the connection
  42245. # acquisition timeout period: because max connections in pool
  42246. # are in use.
  42247. class ConnectionTimeoutError < ConnectionNotEstablished
  42248. end
  42249. module ConnectionAdapters
  42250. # Connection pool base class for managing Active Record database
  42251. # connections.
  42252. #
  42253. # == Introduction
  42254. #
  42255. # A connection pool synchronizes thread access to a limited number of
  42256. # database connections. The basic idea is that each thread checks out a
  42257. # database connection from the pool, uses that connection, and checks the
  42258. # connection back in. ConnectionPool is completely thread-safe, and will
  42259. # ensure that a connection cannot be used by two threads at the same time,
  42260. # as long as ConnectionPool's contract is correctly followed. It will also
  42261. # handle cases in which there are more threads than connections: if all
  42262. # connections have been checked out, and a thread tries to checkout a
  42263. # connection anyway, then ConnectionPool will wait until some other thread
  42264. # has checked in a connection.
  42265. #
  42266. # == Obtaining (checking out) a connection
  42267. #
  42268. # Connections can be obtained and used from a connection pool in several
  42269. # ways:
  42270. #
  42271. # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
  42272. # earlier (pre-connection-pooling). Eventually, when you're done with
  42273. # the connection(s) and wish it to be returned to the pool, you call
  42274. # ActiveRecord::Base.clear_active_connections!. This will be the
  42275. # default behavior for Active Record when used in conjunction with
  42276. # Action Pack's request handling cycle.
  42277. # 2. Manually check out a connection from the pool with
  42278. # ActiveRecord::Base.connection_pool.checkout. You are responsible for
  42279. # returning this connection to the pool when finished by calling
  42280. # ActiveRecord::Base.connection_pool.checkin(connection).
  42281. # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
  42282. # obtains a connection, yields it as the sole argument to the block,
  42283. # and returns it to the pool after the block completes.
  42284. #
  42285. # Connections in the pool are actually AbstractAdapter objects (or objects
  42286. # compatible with AbstractAdapter's interface).
  42287. #
  42288. # == Options
  42289. #
  42290. # There are several connection-pooling-related options that you can add to
  42291. # your database connection configuration:
  42292. #
  42293. # * +pool+: number indicating size of connection pool (default 5)
  42294. # * +checkout_timeout+: number of seconds to block and wait for a connection
  42295. # before giving up and raising a timeout error (default 5 seconds).
  42296. # * +reaping_frequency+: frequency in seconds to periodically run the
  42297. # Reaper, which attempts to find and close dead connections, which can
  42298. # occur if a programmer forgets to close a connection at the end of a
  42299. # thread or a thread dies unexpectedly. (Default nil, which means don't
  42300. # run the Reaper).
  42301. # * +dead_connection_timeout+: number of seconds from last checkout
  42302. # after which the Reaper will consider a connection reapable. (default
  42303. # 5 seconds).
  42304. class ConnectionPool
  42305. # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
  42306. # with which it shares a Monitor. But could be a generic Queue.
  42307. #
  42308. # The Queue in stdlib's 'thread' could replace this class except
  42309. # stdlib's doesn't support waiting with a timeout.
  42310. class Queue
  42311. def initialize(lock = Monitor.new)
  42312. @lock = lock
  42313. @cond = @lock.new_cond
  42314. @num_waiting = 0
  42315. @queue = []
  42316. end
  42317. # Test if any threads are currently waiting on the queue.
  42318. def any_waiting?
  42319. synchronize do
  42320. @num_waiting > 0
  42321. end
  42322. end
  42323. # Return the number of threads currently waiting on this
  42324. # queue.
  42325. def num_waiting
  42326. synchronize do
  42327. @num_waiting
  42328. end
  42329. end
  42330. # Add +element+ to the queue. Never blocks.
  42331. def add(element)
  42332. synchronize do
  42333. @queue.push element
  42334. @cond.signal
  42335. end
  42336. end
  42337. # If +element+ is in the queue, remove and return it, or nil.
  42338. def delete(element)
  42339. synchronize do
  42340. @queue.delete(element)
  42341. end
  42342. end
  42343. # Remove all elements from the queue.
  42344. def clear
  42345. synchronize do
  42346. @queue.clear
  42347. end
  42348. end
  42349. # Remove the head of the queue.
  42350. #
  42351. # If +timeout+ is not given, remove and return the head the
  42352. # queue if the number of available elements is strictly
  42353. # greater than the number of threads currently waiting (that
  42354. # is, don't jump ahead in line). Otherwise, return nil.
  42355. #
  42356. # If +timeout+ is given, block if it there is no element
  42357. # available, waiting up to +timeout+ seconds for an element to
  42358. # become available.
  42359. #
  42360. # Raises:
  42361. # - ConnectionTimeoutError if +timeout+ is given and no element
  42362. # becomes available after +timeout+ seconds,
  42363. def poll(timeout = nil)
  42364. synchronize do
  42365. if timeout
  42366. no_wait_poll || wait_poll(timeout)
  42367. else
  42368. no_wait_poll
  42369. end
  42370. end
  42371. end
  42372. private
  42373. def synchronize(&block)
  42374. @lock.synchronize(&block)
  42375. end
  42376. # Test if the queue currently contains any elements.
  42377. def any?
  42378. !@queue.empty?
  42379. end
  42380. # A thread can remove an element from the queue without
  42381. # waiting if an only if the number of currently available
  42382. # connections is strictly greater than the number of waiting
  42383. # threads.
  42384. def can_remove_no_wait?
  42385. @queue.size > @num_waiting
  42386. end
  42387. # Removes and returns the head of the queue if possible, or nil.
  42388. def remove
  42389. @queue.shift
  42390. end
  42391. # Remove and return the head the queue if the number of
  42392. # available elements is strictly greater than the number of
  42393. # threads currently waiting. Otherwise, return nil.
  42394. def no_wait_poll
  42395. remove if can_remove_no_wait?
  42396. end
  42397. # Waits on the queue up to +timeout+ seconds, then removes and
  42398. # returns the head of the queue.
  42399. def wait_poll(timeout)
  42400. @num_waiting += 1
  42401. t0 = Time.now
  42402. elapsed = 0
  42403. loop do
  42404. @cond.wait(timeout - elapsed)
  42405. return remove if any?
  42406. elapsed = Time.now - t0
  42407. if elapsed >= timeout
  42408. msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
  42409. [timeout, elapsed]
  42410. raise ConnectionTimeoutError, msg
  42411. end
  42412. end
  42413. ensure
  42414. @num_waiting -= 1
  42415. end
  42416. end
  42417. # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
  42418. # A reaper instantiated with a nil frequency will never reap the
  42419. # connection pool.
  42420. #
  42421. # Configure the frequency by setting "reaping_frequency" in your
  42422. # database yaml file.
  42423. class Reaper
  42424. attr_reader :pool, :frequency
  42425. def initialize(pool, frequency)
  42426. @pool = pool
  42427. @frequency = frequency
  42428. end
  42429. def run
  42430. return unless frequency
  42431. Thread.new(frequency, pool) { |t, p|
  42432. while true
  42433. sleep t
  42434. p.reap
  42435. end
  42436. }
  42437. end
  42438. end
  42439. include MonitorMixin
  42440. attr_accessor :automatic_reconnect, :checkout_timeout, :dead_connection_timeout
  42441. attr_reader :spec, :connections, :size, :reaper
  42442. # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
  42443. # object which describes database connection information (e.g. adapter,
  42444. # host name, username, password, etc), as well as the maximum size for
  42445. # this ConnectionPool.
  42446. #
  42447. # The default ConnectionPool maximum size is 5.
  42448. def initialize(spec)
  42449. super()
  42450. @spec = spec
  42451. @checkout_timeout = spec.config[:checkout_timeout] || 5
  42452. @dead_connection_timeout = spec.config[:dead_connection_timeout] || 5
  42453. @reaper = Reaper.new self, spec.config[:reaping_frequency]
  42454. @reaper.run
  42455. # default max pool size to 5
  42456. @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
  42457. # The cache of reserved connections mapped to threads
  42458. @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
  42459. @connections = []
  42460. @automatic_reconnect = true
  42461. @available = Queue.new self
  42462. end
  42463. # Hack for tests to be able to add connections. Do not call outside of tests
  42464. def insert_connection_for_test!(c) #:nodoc:
  42465. synchronize do
  42466. @connections << c
  42467. @available.add c
  42468. end
  42469. end
  42470. # Retrieve the connection associated with the current thread, or call
  42471. # #checkout to obtain one if necessary.
  42472. #
  42473. # #connection can be called any number of times; the connection is
  42474. # held in a hash keyed by the thread id.
  42475. def connection
  42476. # this is correctly done double-checked locking
  42477. # (ThreadSafe::Cache's lookups have volatile semantics)
  42478. @reserved_connections[current_connection_id] || synchronize do
  42479. @reserved_connections[current_connection_id] ||= checkout
  42480. end
  42481. end
  42482. # Is there an open connection that is being used for the current thread?
  42483. def active_connection?
  42484. synchronize do
  42485. @reserved_connections.fetch(current_connection_id) {
  42486. return false
  42487. }.in_use?
  42488. end
  42489. end
  42490. # Signal that the thread is finished with the current connection.
  42491. # #release_connection releases the connection-thread association
  42492. # and returns the connection to the pool.
  42493. def release_connection(with_id = current_connection_id)
  42494. synchronize do
  42495. conn = @reserved_connections.delete(with_id)
  42496. checkin conn if conn
  42497. end
  42498. end
  42499. # If a connection already exists yield it to the block. If no connection
  42500. # exists checkout a connection, yield it to the block, and checkin the
  42501. # connection when finished.
  42502. def with_connection
  42503. connection_id = current_connection_id
  42504. fresh_connection = true unless active_connection?
  42505. yield connection
  42506. ensure
  42507. release_connection(connection_id) if fresh_connection
  42508. end
  42509. # Returns true if a connection has already been opened.
  42510. def connected?
  42511. synchronize { @connections.any? }
  42512. end
  42513. # Disconnects all connections in the pool, and clears the pool.
  42514. def disconnect!
  42515. synchronize do
  42516. @reserved_connections.clear
  42517. @connections.each do |conn|
  42518. checkin conn
  42519. conn.disconnect!
  42520. end
  42521. @connections = []
  42522. @available.clear
  42523. end
  42524. end
  42525. # Clears the cache which maps classes.
  42526. def clear_reloadable_connections!
  42527. synchronize do
  42528. @reserved_connections.clear
  42529. @connections.each do |conn|
  42530. checkin conn
  42531. conn.disconnect! if conn.requires_reloading?
  42532. end
  42533. @connections.delete_if do |conn|
  42534. conn.requires_reloading?
  42535. end
  42536. @available.clear
  42537. @connections.each do |conn|
  42538. @available.add conn
  42539. end
  42540. end
  42541. end
  42542. def clear_stale_cached_connections! # :nodoc:
  42543. reap
  42544. end
  42545. deprecate :clear_stale_cached_connections! => "Please use #reap instead"
  42546. # Check-out a database connection from the pool, indicating that you want
  42547. # to use it. You should call #checkin when you no longer need this.
  42548. #
  42549. # This is done by either returning and leasing existing connection, or by
  42550. # creating a new connection and leasing it.
  42551. #
  42552. # If all connections are leased and the pool is at capacity (meaning the
  42553. # number of currently leased connections is greater than or equal to the
  42554. # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
  42555. #
  42556. # Returns: an AbstractAdapter object.
  42557. #
  42558. # Raises:
  42559. # - ConnectionTimeoutError: no connection can be obtained from the pool.
  42560. def checkout
  42561. synchronize do
  42562. conn = acquire_connection
  42563. conn.lease
  42564. checkout_and_verify(conn)
  42565. end
  42566. end
  42567. # Check-in a database connection back into the pool, indicating that you
  42568. # no longer need this connection.
  42569. #
  42570. # +conn+: an AbstractAdapter object, which was obtained by earlier by
  42571. # calling +checkout+ on this pool.
  42572. def checkin(conn)
  42573. synchronize do
  42574. conn.run_callbacks :checkin do
  42575. conn.expire
  42576. end
  42577. release conn
  42578. @available.add conn
  42579. end
  42580. end
  42581. # Remove a connection from the connection pool. The connection will
  42582. # remain open and active but will no longer be managed by this pool.
  42583. def remove(conn)
  42584. synchronize do
  42585. @connections.delete conn
  42586. @available.delete conn
  42587. # FIXME: we might want to store the key on the connection so that removing
  42588. # from the reserved hash will be a little easier.
  42589. release conn
  42590. @available.add checkout_new_connection if @available.any_waiting?
  42591. end
  42592. end
  42593. # Removes dead connections from the pool. A dead connection can occur
  42594. # if a programmer forgets to close a connection at the end of a thread
  42595. # or a thread dies unexpectedly.
  42596. def reap
  42597. synchronize do
  42598. stale = Time.now - @dead_connection_timeout
  42599. connections.dup.each do |conn|
  42600. remove conn if conn.in_use? && stale > conn.last_use && !conn.active?
  42601. end
  42602. end
  42603. end
  42604. private
  42605. # Acquire a connection by one of 1) immediately removing one
  42606. # from the queue of available connections, 2) creating a new
  42607. # connection if the pool is not at capacity, 3) waiting on the
  42608. # queue for a connection to become available.
  42609. #
  42610. # Raises:
  42611. # - ConnectionTimeoutError if a connection could not be acquired
  42612. def acquire_connection
  42613. if conn = @available.poll
  42614. conn
  42615. elsif @connections.size < @size
  42616. checkout_new_connection
  42617. else
  42618. @available.poll(@checkout_timeout)
  42619. end
  42620. end
  42621. def release(conn)
  42622. thread_id = if @reserved_connections[current_connection_id] == conn
  42623. current_connection_id
  42624. else
  42625. @reserved_connections.keys.find { |k|
  42626. @reserved_connections[k] == conn
  42627. }
  42628. end
  42629. @reserved_connections.delete thread_id if thread_id
  42630. end
  42631. def new_connection
  42632. Base.send(spec.adapter_method, spec.config)
  42633. end
  42634. def current_connection_id #:nodoc:
  42635. Base.connection_id ||= Thread.current.object_id
  42636. end
  42637. def checkout_new_connection
  42638. raise ConnectionNotEstablished unless @automatic_reconnect
  42639. c = new_connection
  42640. c.pool = self
  42641. @connections << c
  42642. c
  42643. end
  42644. def checkout_and_verify(c)
  42645. c.run_callbacks :checkout do
  42646. c.verify!
  42647. end
  42648. c
  42649. end
  42650. end
  42651. # ConnectionHandler is a collection of ConnectionPool objects. It is used
  42652. # for keeping separate connection pools for Active Record models that connect
  42653. # to different databases.
  42654. #
  42655. # For example, suppose that you have 5 models, with the following hierarchy:
  42656. #
  42657. # |
  42658. # +-- Book
  42659. # | |
  42660. # | +-- ScaryBook
  42661. # | +-- GoodBook
  42662. # +-- Author
  42663. # +-- BankAccount
  42664. #
  42665. # Suppose that Book is to connect to a separate database (i.e. one other
  42666. # than the default database). Then Book, ScaryBook and GoodBook will all use
  42667. # the same connection pool. Likewise, Author and BankAccount will use the
  42668. # same connection pool. However, the connection pool used by Author/BankAccount
  42669. # is not the same as the one used by Book/ScaryBook/GoodBook.
  42670. #
  42671. # Normally there is only a single ConnectionHandler instance, accessible via
  42672. # ActiveRecord::Base.connection_handler. Active Record models use this to
  42673. # determine the connection pool that they should use.
  42674. class ConnectionHandler
  42675. def initialize
  42676. # These caches are keyed by klass.name, NOT klass. Keying them by klass
  42677. # alone would lead to memory leaks in development mode as all previous
  42678. # instances of the class would stay in memory.
  42679. @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
  42680. h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
  42681. end
  42682. @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
  42683. h[k] = ThreadSafe::Cache.new
  42684. end
  42685. end
  42686. def connection_pool_list
  42687. owner_to_pool.values.compact
  42688. end
  42689. def connection_pools
  42690. ActiveSupport::Deprecation.warn(
  42691. "In the next release, this will return the same as #connection_pool_list. " \
  42692. "(An array of pools, rather than a hash mapping specs to pools.)"
  42693. )
  42694. Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
  42695. end
  42696. def establish_connection(owner, spec)
  42697. @class_to_pool.clear
  42698. raise RuntimeError, "Anonymous class is not allowed." unless owner.name
  42699. owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
  42700. end
  42701. # Returns true if there are any active connections among the connection
  42702. # pools that the ConnectionHandler is managing.
  42703. def active_connections?
  42704. connection_pool_list.any?(&:active_connection?)
  42705. end
  42706. # Returns any connections in use by the current thread back to the pool,
  42707. # and also returns connections to the pool cached by threads that are no
  42708. # longer alive.
  42709. def clear_active_connections!
  42710. connection_pool_list.each(&:release_connection)
  42711. end
  42712. # Clears the cache which maps classes.
  42713. def clear_reloadable_connections!
  42714. connection_pool_list.each(&:clear_reloadable_connections!)
  42715. end
  42716. def clear_all_connections!
  42717. connection_pool_list.each(&:disconnect!)
  42718. end
  42719. # Locate the connection of the nearest super class. This can be an
  42720. # active or defined connection: if it is the latter, it will be
  42721. # opened and set as the active connection for the class it was defined
  42722. # for (not necessarily the current class).
  42723. def retrieve_connection(klass) #:nodoc:
  42724. pool = retrieve_connection_pool(klass)
  42725. (pool && pool.connection) or raise ConnectionNotEstablished
  42726. end
  42727. # Returns true if a connection that's accessible to this class has
  42728. # already been opened.
  42729. def connected?(klass)
  42730. conn = retrieve_connection_pool(klass)
  42731. conn && conn.connected?
  42732. end
  42733. # Remove the connection for this class. This will close the active
  42734. # connection and the defined connection (if they exist). The result
  42735. # can be used as an argument for establish_connection, for easily
  42736. # re-establishing the connection.
  42737. def remove_connection(owner)
  42738. if pool = owner_to_pool.delete(owner.name)
  42739. @class_to_pool.clear
  42740. pool.automatic_reconnect = false
  42741. pool.disconnect!
  42742. pool.spec.config
  42743. end
  42744. end
  42745. # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
  42746. # This makes retrieving the connection pool O(1) once the process is warm.
  42747. # When a connection is established or removed, we invalidate the cache.
  42748. #
  42749. # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
  42750. # However, benchmarking (https://gist.github.com/3552829) showed that #fetch is
  42751. # significantly slower than #[]. So in the nil case, no caching will take place,
  42752. # but that's ok since the nil case is not the common one that we wish to optimise
  42753. # for.
  42754. def retrieve_connection_pool(klass)
  42755. class_to_pool[klass.name] ||= begin
  42756. until pool = pool_for(klass)
  42757. klass = klass.superclass
  42758. break unless klass <= Base
  42759. end
  42760. class_to_pool[klass.name] = pool
  42761. end
  42762. end
  42763. private
  42764. def owner_to_pool
  42765. @owner_to_pool[Process.pid]
  42766. end
  42767. def class_to_pool
  42768. @class_to_pool[Process.pid]
  42769. end
  42770. def pool_for(owner)
  42771. owner_to_pool.fetch(owner.name) {
  42772. if ancestor_pool = pool_from_any_process_for(owner)
  42773. # A connection was established in an ancestor process that must have
  42774. # subsequently forked. We can't reuse the connection, but we can copy
  42775. # the specification and establish a new connection with it.
  42776. establish_connection owner, ancestor_pool.spec
  42777. else
  42778. owner_to_pool[owner.name] = nil
  42779. end
  42780. }
  42781. end
  42782. def pool_from_any_process_for(owner)
  42783. owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
  42784. owner_to_pool && owner_to_pool[owner.name]
  42785. end
  42786. end
  42787. class ConnectionManagement
  42788. def initialize(app)
  42789. @app = app
  42790. end
  42791. def call(env)
  42792. testing = env.key?('rack.test')
  42793. response = @app.call(env)
  42794. response[2] = ::Rack::BodyProxy.new(response[2]) do
  42795. ActiveRecord::Base.clear_active_connections! unless testing
  42796. end
  42797. response
  42798. rescue
  42799. ActiveRecord::Base.clear_active_connections! unless testing
  42800. raise
  42801. end
  42802. end
  42803. end
  42804. end
  42805. module ActiveRecord
  42806. module ConnectionAdapters # :nodoc:
  42807. module DatabaseLimits
  42808. # Returns the maximum length of a table alias.
  42809. def table_alias_length
  42810. 255
  42811. end
  42812. # Returns the maximum length of a column name.
  42813. def column_name_length
  42814. 64
  42815. end
  42816. # Returns the maximum length of a table name.
  42817. def table_name_length
  42818. 64
  42819. end
  42820. # Returns the maximum length of an index name.
  42821. def index_name_length
  42822. 64
  42823. end
  42824. # Returns the maximum number of columns per table.
  42825. def columns_per_table
  42826. 1024
  42827. end
  42828. # Returns the maximum number of indexes per table.
  42829. def indexes_per_table
  42830. 16
  42831. end
  42832. # Returns the maximum number of columns in a multicolumn index.
  42833. def columns_per_multicolumn_index
  42834. 16
  42835. end
  42836. # Returns the maximum number of elements in an IN (x,y,z) clause.
  42837. # nil means no limit.
  42838. def in_clause_length
  42839. nil
  42840. end
  42841. # Returns the maximum length of an SQL query.
  42842. def sql_query_length
  42843. 1048575
  42844. end
  42845. # Returns maximum number of joins in a single query.
  42846. def joins_per_query
  42847. 256
  42848. end
  42849. end
  42850. end
  42851. end
  42852. module ActiveRecord
  42853. module ConnectionAdapters # :nodoc:
  42854. module DatabaseStatements
  42855. def initialize
  42856. super
  42857. reset_transaction
  42858. end
  42859. # Converts an arel AST to SQL
  42860. def to_sql(arel, binds = [])
  42861. if arel.respond_to?(:ast)
  42862. binds = binds.dup
  42863. visitor.accept(arel.ast) do
  42864. quote(*binds.shift.reverse)
  42865. end
  42866. else
  42867. arel
  42868. end
  42869. end
  42870. # Returns an array of record hashes with the column names as keys and
  42871. # column values as values.
  42872. def select_all(arel, name = nil, binds = [])
  42873. select(to_sql(arel, binds), name, binds)
  42874. end
  42875. # Returns a record hash with the column names as keys and column values
  42876. # as values.
  42877. def select_one(arel, name = nil, binds = [])
  42878. result = select_all(arel, name, binds)
  42879. result.first if result
  42880. end
  42881. # Returns a single value from a record
  42882. def select_value(arel, name = nil, binds = [])
  42883. if result = select_one(arel, name, binds)
  42884. result.values.first
  42885. end
  42886. end
  42887. # Returns an array of the values of the first column in a select:
  42888. # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
  42889. def select_values(arel, name = nil)
  42890. result = select_rows(to_sql(arel, []), name)
  42891. result.map { |v| v[0] }
  42892. end
  42893. # Returns an array of arrays containing the field values.
  42894. # Order is the same as that returned by +columns+.
  42895. def select_rows(sql, name = nil)
  42896. end
  42897. undef_method :select_rows
  42898. # Executes the SQL statement in the context of this connection.
  42899. def execute(sql, name = nil)
  42900. end
  42901. undef_method :execute
  42902. # Executes +sql+ statement in the context of this connection using
  42903. # +binds+ as the bind substitutes. +name+ is logged along with
  42904. # the executed +sql+ statement.
  42905. def exec_query(sql, name = 'SQL', binds = [])
  42906. end
  42907. # Executes insert +sql+ statement in the context of this connection using
  42908. # +binds+ as the bind substitutes. +name+ is logged along with
  42909. # the executed +sql+ statement.
  42910. def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
  42911. exec_query(sql, name, binds)
  42912. end
  42913. # Executes delete +sql+ statement in the context of this connection using
  42914. # +binds+ as the bind substitutes. +name+ is logged along with
  42915. # the executed +sql+ statement.
  42916. def exec_delete(sql, name, binds)
  42917. exec_query(sql, name, binds)
  42918. end
  42919. # Executes update +sql+ statement in the context of this connection using
  42920. # +binds+ as the bind substitutes. +name+ is logged along with
  42921. # the executed +sql+ statement.
  42922. def exec_update(sql, name, binds)
  42923. exec_query(sql, name, binds)
  42924. end
  42925. # Returns the last auto-generated ID from the affected table.
  42926. #
  42927. # +id_value+ will be returned unless the value is nil, in
  42928. # which case the database will attempt to calculate the last inserted
  42929. # id and return that value.
  42930. #
  42931. # If the next id was calculated in advance (as in Oracle), it should be
  42932. # passed in as +id_value+.
  42933. def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
  42934. sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
  42935. value = exec_insert(sql, name, binds, pk, sequence_name)
  42936. id_value || last_inserted_id(value)
  42937. end
  42938. # Executes the update statement and returns the number of rows affected.
  42939. def update(arel, name = nil, binds = [])
  42940. exec_update(to_sql(arel, binds), name, binds)
  42941. end
  42942. # Executes the delete statement and returns the number of rows affected.
  42943. def delete(arel, name = nil, binds = [])
  42944. exec_delete(to_sql(arel, binds), name, binds)
  42945. end
  42946. # Returns +true+ when the connection adapter supports prepared statement
  42947. # caching, otherwise returns +false+
  42948. def supports_statement_cache?
  42949. false
  42950. end
  42951. # Runs the given block in a database transaction, and returns the result
  42952. # of the block.
  42953. #
  42954. # == Nested transactions support
  42955. #
  42956. # Most databases don't support true nested transactions. At the time of
  42957. # writing, the only database that supports true nested transactions that
  42958. # we're aware of, is MS-SQL.
  42959. #
  42960. # In order to get around this problem, #transaction will emulate the effect
  42961. # of nested transactions, by using savepoints:
  42962. # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
  42963. # Savepoints are supported by MySQL and PostgreSQL, but not SQLite3.
  42964. #
  42965. # It is safe to call this method if a database transaction is already open,
  42966. # i.e. if #transaction is called within another #transaction block. In case
  42967. # of a nested call, #transaction will behave as follows:
  42968. #
  42969. # - The block will be run without doing anything. All database statements
  42970. # that happen within the block are effectively appended to the already
  42971. # open database transaction.
  42972. # - However, if +:requires_new+ is set, the block will be wrapped in a
  42973. # database savepoint acting as a sub-transaction.
  42974. #
  42975. # === Caveats
  42976. #
  42977. # MySQL doesn't support DDL transactions. If you perform a DDL operation,
  42978. # then any created savepoints will be automatically released. For example,
  42979. # if you've created a savepoint, then you execute a CREATE TABLE statement,
  42980. # then the savepoint that was created will be automatically released.
  42981. #
  42982. # This means that, on MySQL, you shouldn't execute DDL operations inside
  42983. # a #transaction call that you know might create a savepoint. Otherwise,
  42984. # #transaction will raise exceptions when it tries to release the
  42985. # already-automatically-released savepoints:
  42986. #
  42987. # Model.connection.transaction do # BEGIN
  42988. # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
  42989. # Model.connection.create_table(...)
  42990. # # active_record_1 now automatically released
  42991. # end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
  42992. # end
  42993. #
  42994. # == Transaction isolation
  42995. #
  42996. # If your database supports setting the isolation level for a transaction, you can set
  42997. # it like so:
  42998. #
  42999. # Post.transaction(isolation: :serializable) do
  43000. # # ...
  43001. # end
  43002. #
  43003. # Valid isolation levels are:
  43004. #
  43005. # * <tt>:read_uncommitted</tt>
  43006. # * <tt>:read_committed</tt>
  43007. # * <tt>:repeatable_read</tt>
  43008. # * <tt>:serializable</tt>
  43009. #
  43010. # You should consult the documentation for your database to understand the
  43011. # semantics of these different levels:
  43012. #
  43013. # * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
  43014. # * https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
  43015. #
  43016. # An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
  43017. #
  43018. # * The adapter does not support setting the isolation level
  43019. # * You are joining an existing open transaction
  43020. # * You are creating a nested (savepoint) transaction
  43021. #
  43022. # The mysql, mysql2 and postgresql adapters support setting the transaction
  43023. # isolation level. However, support is disabled for mysql versions below 5,
  43024. # because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
  43025. # which means the isolation level gets persisted outside the transaction.
  43026. def transaction(options = {})
  43027. options.assert_valid_keys :requires_new, :joinable, :isolation
  43028. if !options[:requires_new] && current_transaction.joinable?
  43029. if options[:isolation]
  43030. raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
  43031. end
  43032. yield
  43033. else
  43034. within_new_transaction(options) { yield }
  43035. end
  43036. rescue ActiveRecord::Rollback
  43037. # rollbacks are silently swallowed
  43038. end
  43039. def within_new_transaction(options = {}) #:nodoc:
  43040. transaction = begin_transaction(options)
  43041. yield
  43042. rescue Exception => error
  43043. rollback_transaction if transaction
  43044. raise
  43045. ensure
  43046. begin
  43047. commit_transaction unless error
  43048. rescue Exception
  43049. rollback_transaction
  43050. raise
  43051. end
  43052. end
  43053. def current_transaction #:nodoc:
  43054. @transaction
  43055. end
  43056. def transaction_open?
  43057. @transaction.open?
  43058. end
  43059. def begin_transaction(options = {}) #:nodoc:
  43060. @transaction = @transaction.begin(options)
  43061. end
  43062. def commit_transaction #:nodoc:
  43063. @transaction = @transaction.commit
  43064. end
  43065. def rollback_transaction #:nodoc:
  43066. @transaction = @transaction.rollback
  43067. end
  43068. def reset_transaction #:nodoc:
  43069. @transaction = ClosedTransaction.new(self)
  43070. end
  43071. # Register a record with the current transaction so that its after_commit and after_rollback callbacks
  43072. # can be called.
  43073. def add_transaction_record(record)
  43074. @transaction.add_record(record)
  43075. end
  43076. # Begins the transaction (and turns off auto-committing).
  43077. def begin_db_transaction() end
  43078. def transaction_isolation_levels
  43079. {
  43080. read_uncommitted: "READ UNCOMMITTED",
  43081. read_committed: "READ COMMITTED",
  43082. repeatable_read: "REPEATABLE READ",
  43083. serializable: "SERIALIZABLE"
  43084. }
  43085. end
  43086. # Begins the transaction with the isolation level set. Raises an error by
  43087. # default; adapters that support setting the isolation level should implement
  43088. # this method.
  43089. def begin_isolated_db_transaction(isolation)
  43090. raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
  43091. end
  43092. # Commits the transaction (and turns on auto-committing).
  43093. def commit_db_transaction() end
  43094. # Rolls back the transaction (and turns on auto-committing). Must be
  43095. # done if the transaction block raises an exception or returns false.
  43096. def rollback_db_transaction() end
  43097. def default_sequence_name(table, column)
  43098. nil
  43099. end
  43100. # Set the sequence to the max value of the table's column.
  43101. def reset_sequence!(table, column, sequence = nil)
  43102. # Do nothing by default. Implement for PostgreSQL, Oracle, ...
  43103. end
  43104. # Inserts the given fixture into the table. Overridden in adapters that require
  43105. # something beyond a simple insert (eg. Oracle).
  43106. def insert_fixture(fixture, table_name)
  43107. columns = schema_cache.columns_hash(table_name)
  43108. key_list = []
  43109. value_list = fixture.map do |name, value|
  43110. key_list << quote_column_name(name)
  43111. quote(value, columns[name])
  43112. end
  43113. execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
  43114. end
  43115. def empty_insert_statement_value
  43116. "DEFAULT VALUES"
  43117. end
  43118. def case_sensitive_equality_operator
  43119. "="
  43120. end
  43121. def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  43122. "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
  43123. end
  43124. # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
  43125. #
  43126. # The +limit+ may be anything that can evaluate to a string via #to_s. It
  43127. # should look like an integer, or a comma-delimited list of integers, or
  43128. # an Arel SQL literal.
  43129. #
  43130. # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
  43131. # Returns the sanitized limit parameter, either as an integer, or as a
  43132. # string which contains a comma-delimited list of integers.
  43133. def sanitize_limit(limit)
  43134. if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
  43135. limit
  43136. elsif limit.to_s =~ /,/
  43137. Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
  43138. else
  43139. Integer(limit)
  43140. end
  43141. end
  43142. # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
  43143. # on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
  43144. # an UPDATE statement, so in the mysql adapters we redefine this to do that.
  43145. def join_to_update(update, select) #:nodoc:
  43146. key = update.key
  43147. subselect = subquery_for(key, select)
  43148. update.where key.in(subselect)
  43149. end
  43150. def join_to_delete(delete, select, key) #:nodoc:
  43151. subselect = subquery_for(key, select)
  43152. delete.where key.in(subselect)
  43153. end
  43154. protected
  43155. # Return a subquery for the given key using the join information.
  43156. def subquery_for(key, select)
  43157. subselect = select.clone
  43158. subselect.projections = [key]
  43159. subselect
  43160. end
  43161. # Returns an array of record hashes with the column names as keys and
  43162. # column values as values.
  43163. def select(sql, name = nil, binds = [])
  43164. end
  43165. undef_method :select
  43166. # Returns the last auto-generated ID from the affected table.
  43167. def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
  43168. execute(sql, name)
  43169. id_value
  43170. end
  43171. # Executes the update statement and returns the number of rows affected.
  43172. def update_sql(sql, name = nil)
  43173. execute(sql, name)
  43174. end
  43175. # Executes the delete statement and returns the number of rows affected.
  43176. def delete_sql(sql, name = nil)
  43177. update_sql(sql, name)
  43178. end
  43179. def sql_for_insert(sql, pk, id_value, sequence_name, binds)
  43180. [sql, binds]
  43181. end
  43182. def last_inserted_id(result)
  43183. row = result.rows.first
  43184. row && row.first
  43185. end
  43186. end
  43187. end
  43188. end
  43189. module ActiveRecord
  43190. module ConnectionAdapters # :nodoc:
  43191. module QueryCache
  43192. class << self
  43193. def included(base) #:nodoc:
  43194. dirties_query_cache base, :insert, :update, :delete
  43195. end
  43196. def dirties_query_cache(base, *method_names)
  43197. method_names.each do |method_name|
  43198. base.class_eval <<-end_code, __FILE__, __LINE__ + 1
  43199. def #{method_name}(*) # def update_with_query_dirty(*)
  43200. clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
  43201. super # super
  43202. end # end
  43203. end_code
  43204. end
  43205. end
  43206. end
  43207. attr_reader :query_cache, :query_cache_enabled
  43208. # Enable the query cache within the block.
  43209. def cache
  43210. old, @query_cache_enabled = @query_cache_enabled, true
  43211. yield
  43212. ensure
  43213. clear_query_cache
  43214. @query_cache_enabled = old
  43215. end
  43216. def enable_query_cache!
  43217. @query_cache_enabled = true
  43218. end
  43219. def disable_query_cache!
  43220. @query_cache_enabled = false
  43221. end
  43222. # Disable the query cache within the block.
  43223. def uncached
  43224. old, @query_cache_enabled = @query_cache_enabled, false
  43225. yield
  43226. ensure
  43227. @query_cache_enabled = old
  43228. end
  43229. # Clears the query cache.
  43230. #
  43231. # One reason you may wish to call this method explicitly is between queries
  43232. # that ask the database to randomize results. Otherwise the cache would see
  43233. # the same SQL query and repeatedly return the same result each time, silently
  43234. # undermining the randomness you were expecting.
  43235. def clear_query_cache
  43236. @query_cache.clear
  43237. end
  43238. def select_all(arel, name = nil, binds = [])
  43239. if @query_cache_enabled && !locked?(arel)
  43240. sql = to_sql(arel, binds)
  43241. cache_sql(sql, binds) { super(sql, name, binds) }
  43242. else
  43243. super
  43244. end
  43245. end
  43246. private
  43247. def cache_sql(sql, binds)
  43248. result =
  43249. if @query_cache[sql].key?(binds)
  43250. ActiveSupport::Notifications.instrument("sql.active_record",
  43251. :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
  43252. @query_cache[sql][binds]
  43253. else
  43254. @query_cache[sql][binds] = yield
  43255. end
  43256. # FIXME: we should guarantee that all cached items are Result
  43257. # objects. Then we can avoid this conditional
  43258. if ActiveRecord::Result === result
  43259. result.dup
  43260. else
  43261. result.collect { |row| row.dup }
  43262. end
  43263. end
  43264. # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
  43265. # queries should not be cached.
  43266. def locked?(arel)
  43267. arel.respond_to?(:locked) && arel.locked
  43268. end
  43269. end
  43270. end
  43271. end
  43272. require 'active_support/core_ext/big_decimal/conversions'
  43273. module ActiveRecord
  43274. module ConnectionAdapters # :nodoc:
  43275. module Quoting
  43276. # Quotes the column value to help prevent
  43277. # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
  43278. def quote(value, column = nil)
  43279. # records are quoted as their primary key
  43280. return value.quoted_id if value.respond_to?(:quoted_id)
  43281. case value
  43282. when String, ActiveSupport::Multibyte::Chars
  43283. value = value.to_s
  43284. return "'#{quote_string(value)}'" unless column
  43285. case column.type
  43286. when :binary then "'#{quote_string(column.string_to_binary(value))}'"
  43287. when :integer then value.to_i.to_s
  43288. when :float then value.to_f.to_s
  43289. else
  43290. "'#{quote_string(value)}'"
  43291. end
  43292. when true, false
  43293. if column && column.type == :integer
  43294. value ? '1' : '0'
  43295. elsif column && [:text, :string, :binary].include?(column.type)
  43296. value ? "'1'" : "'0'"
  43297. else
  43298. value ? quoted_true : quoted_false
  43299. end
  43300. # BigDecimals need to be put in a non-normalized form and quoted.
  43301. when nil then "NULL"
  43302. when Numeric, ActiveSupport::Duration
  43303. value = BigDecimal === value ? value.to_s('F') : value.to_s
  43304. if column && ![:integer, :float, :decimal].include?(column.type)
  43305. value = "'#{value}'"
  43306. end
  43307. value
  43308. when Date, Time then "'#{quoted_date(value)}'"
  43309. when Symbol then "'#{quote_string(value.to_s)}'"
  43310. when Class then "'#{value.to_s}'"
  43311. else
  43312. "'#{quote_string(YAML.dump(value))}'"
  43313. end
  43314. end
  43315. # Cast a +value+ to a type that the database understands. For example,
  43316. # SQLite does not understand dates, so this method will convert a Date
  43317. # to a String.
  43318. def type_cast(value, column)
  43319. return value.id if value.respond_to?(:quoted_id)
  43320. case value
  43321. when String, ActiveSupport::Multibyte::Chars
  43322. value = value.to_s
  43323. return value unless column
  43324. case column.type
  43325. when :binary then value
  43326. when :integer then value.to_i
  43327. when :float then value.to_f
  43328. else
  43329. value
  43330. end
  43331. when true, false
  43332. if column && column.type == :integer
  43333. value ? 1 : 0
  43334. else
  43335. value ? 't' : 'f'
  43336. end
  43337. # BigDecimals need to be put in a non-normalized form and quoted.
  43338. when nil then nil
  43339. when BigDecimal then value.to_s('F')
  43340. when Numeric then value
  43341. when Date, Time then quoted_date(value)
  43342. when Symbol then value.to_s
  43343. else
  43344. to_type = column ? " to #{column.type}" : ""
  43345. raise TypeError, "can't cast #{value.class}#{to_type}"
  43346. end
  43347. end
  43348. # Quotes a string, escaping any ' (single quote) and \ (backslash)
  43349. # characters.
  43350. def quote_string(s)
  43351. s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
  43352. end
  43353. # Quotes the column name. Defaults to no quoting.
  43354. def quote_column_name(column_name)
  43355. column_name
  43356. end
  43357. # Quotes the table name. Defaults to column name quoting.
  43358. def quote_table_name(table_name)
  43359. quote_column_name(table_name)
  43360. end
  43361. # Override to return the quoted table name for assignment. Defaults to
  43362. # table quoting.
  43363. #
  43364. # This works for mysql and mysql2 where table.column can be used to
  43365. # resolve ambiguity.
  43366. #
  43367. # We override this in the sqlite and postgresql adapters to use only
  43368. # the column name (as per syntax requirements).
  43369. def quote_table_name_for_assignment(table, attr)
  43370. quote_table_name("#{table}.#{attr}")
  43371. end
  43372. def quoted_true
  43373. "'t'"
  43374. end
  43375. def quoted_false
  43376. "'f'"
  43377. end
  43378. def quoted_date(value)
  43379. if value.acts_like?(:time)
  43380. zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
  43381. if value.respond_to?(zone_conversion_method)
  43382. value = value.send(zone_conversion_method)
  43383. end
  43384. end
  43385. value.to_s(:db)
  43386. end
  43387. end
  43388. end
  43389. end
  43390. require 'date'
  43391. require 'set'
  43392. require 'bigdecimal'
  43393. require 'bigdecimal/util'
  43394. module ActiveRecord
  43395. module ConnectionAdapters #:nodoc:
  43396. # Abstract representation of an index definition on a table. Instances of
  43397. # this type are typically created and returned by methods in database
  43398. # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
  43399. class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where) #:nodoc:
  43400. end
  43401. # Abstract representation of a column definition. Instances of this type
  43402. # are typically created by methods in TableDefinition, and added to the
  43403. # +columns+ attribute of said TableDefinition object, in order to be used
  43404. # for generating a number of table creation or table changing SQL statements.
  43405. class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
  43406. def string_to_binary(value)
  43407. value
  43408. end
  43409. def sql_type
  43410. base.type_to_sql(type.to_sym, limit, precision, scale)
  43411. end
  43412. def to_sql
  43413. column_sql = "#{base.quote_column_name(name)} #{sql_type}"
  43414. column_options = {}
  43415. column_options[:null] = null unless null.nil?
  43416. column_options[:default] = default unless default.nil?
  43417. add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
  43418. column_sql
  43419. end
  43420. private
  43421. def add_column_options!(sql, options)
  43422. base.add_column_options!(sql, options.merge(:column => self))
  43423. end
  43424. end
  43425. # Represents the schema of an SQL table in an abstract way. This class
  43426. # provides methods for manipulating the schema representation.
  43427. #
  43428. # Inside migration files, the +t+ object in +create_table+ and
  43429. # +change_table+ is actually of this type:
  43430. #
  43431. # class SomeMigration < ActiveRecord::Migration
  43432. # def up
  43433. # create_table :foo do |t|
  43434. # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
  43435. # end
  43436. # end
  43437. #
  43438. # def down
  43439. # ...
  43440. # end
  43441. # end
  43442. #
  43443. # The table definitions
  43444. # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
  43445. class TableDefinition
  43446. # An array of ColumnDefinition objects, representing the column changes
  43447. # that have been defined.
  43448. attr_accessor :columns, :indexes
  43449. def initialize(base)
  43450. @columns = []
  43451. @columns_hash = {}
  43452. @indexes = {}
  43453. @base = base
  43454. end
  43455. def xml(*args)
  43456. raise NotImplementedError unless %w{
  43457. sqlite mysql mysql2
  43458. }.include? @base.adapter_name.downcase
  43459. options = args.extract_options!
  43460. column(args[0], :text, options)
  43461. end
  43462. # Appends a primary key definition to the table definition.
  43463. # Can be called multiple times, but this is probably not a good idea.
  43464. def primary_key(name)
  43465. column(name, :primary_key)
  43466. end
  43467. # Returns a ColumnDefinition for the column with name +name+.
  43468. def [](name)
  43469. @columns_hash[name.to_s]
  43470. end
  43471. # Instantiates a new column for the table.
  43472. # The +type+ parameter is normally one of the migrations native types,
  43473. # which is one of the following:
  43474. # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
  43475. # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
  43476. # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
  43477. # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
  43478. #
  43479. # You may use a type not in this list as long as it is supported by your
  43480. # database (for example, "polygon" in MySQL), but this will not be database
  43481. # agnostic and should usually be avoided.
  43482. #
  43483. # Available options are (none of these exists by default):
  43484. # * <tt>:limit</tt> -
  43485. # Requests a maximum column length. This is number of characters for <tt>:string</tt> and
  43486. # <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns.
  43487. # * <tt>:default</tt> -
  43488. # The column's default value. Use nil for NULL.
  43489. # * <tt>:null</tt> -
  43490. # Allows or disallows +NULL+ values in the column. This option could
  43491. # have been named <tt>:null_allowed</tt>.
  43492. # * <tt>:precision</tt> -
  43493. # Specifies the precision for a <tt>:decimal</tt> column.
  43494. # * <tt>:scale</tt> -
  43495. # Specifies the scale for a <tt>:decimal</tt> column.
  43496. #
  43497. # For clarity's sake: the precision is the number of significant digits,
  43498. # while the scale is the number of digits that can be stored following
  43499. # the decimal point. For example, the number 123.45 has a precision of 5
  43500. # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
  43501. # range from -999.99 to 999.99.
  43502. #
  43503. # Please be aware of different RDBMS implementations behavior with
  43504. # <tt>:decimal</tt> columns:
  43505. # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
  43506. # <tt>:precision</tt>, and makes no comments about the requirements of
  43507. # <tt>:precision</tt>.
  43508. # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
  43509. # Default is (10,0).
  43510. # * PostgreSQL: <tt>:precision</tt> [1..infinity],
  43511. # <tt>:scale</tt> [0..infinity]. No default.
  43512. # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
  43513. # Internal storage as strings. No default.
  43514. # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
  43515. # but the maximum supported <tt>:precision</tt> is 16. No default.
  43516. # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
  43517. # Default is (38,0).
  43518. # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
  43519. # Default unknown.
  43520. # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
  43521. # Default (9,0). Internal types NUMERIC and DECIMAL have different
  43522. # storage rules, decimal being better.
  43523. # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
  43524. # Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
  43525. # NUMERIC is 19, and DECIMAL is 38.
  43526. # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
  43527. # Default (38,0).
  43528. # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
  43529. # Default (38,0).
  43530. # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
  43531. #
  43532. # This method returns <tt>self</tt>.
  43533. #
  43534. # == Examples
  43535. # # Assuming +td+ is an instance of TableDefinition
  43536. # td.column(:granted, :boolean)
  43537. # # granted BOOLEAN
  43538. #
  43539. # td.column(:picture, :binary, limit: 2.megabytes)
  43540. # # => picture BLOB(2097152)
  43541. #
  43542. # td.column(:sales_stage, :string, limit: 20, default: 'new', null: false)
  43543. # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
  43544. #
  43545. # td.column(:bill_gates_money, :decimal, precision: 15, scale: 2)
  43546. # # => bill_gates_money DECIMAL(15,2)
  43547. #
  43548. # td.column(:sensor_reading, :decimal, precision: 30, scale: 20)
  43549. # # => sensor_reading DECIMAL(30,20)
  43550. #
  43551. # # While <tt>:scale</tt> defaults to zero on most databases, it
  43552. # # probably wouldn't hurt to include it.
  43553. # td.column(:huge_integer, :decimal, precision: 30)
  43554. # # => huge_integer DECIMAL(30)
  43555. #
  43556. # # Defines a column with a database-specific type.
  43557. # td.column(:foo, 'polygon')
  43558. # # => foo polygon
  43559. #
  43560. # == Short-hand examples
  43561. #
  43562. # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
  43563. # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
  43564. # in a single statement.
  43565. #
  43566. # What can be written like this with the regular calls to column:
  43567. #
  43568. # create_table :products do |t|
  43569. # t.column :shop_id, :integer
  43570. # t.column :creator_id, :integer
  43571. # t.column :name, :string, default: "Untitled"
  43572. # t.column :value, :string, default: "Untitled"
  43573. # t.column :created_at, :datetime
  43574. # t.column :updated_at, :datetime
  43575. # end
  43576. #
  43577. # can also be written as follows using the short-hand:
  43578. #
  43579. # create_table :products do |t|
  43580. # t.integer :shop_id, :creator_id
  43581. # t.string :name, :value, default: "Untitled"
  43582. # t.timestamps
  43583. # end
  43584. #
  43585. # There's a short-hand method for each of the type values declared at the top. And then there's
  43586. # TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes.
  43587. #
  43588. # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
  43589. # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
  43590. # options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option
  43591. # will also create an index, similar to calling <tt>add_index</tt>. So what can be written like this:
  43592. #
  43593. # create_table :taggings do |t|
  43594. # t.integer :tag_id, :tagger_id, :taggable_id
  43595. # t.string :tagger_type
  43596. # t.string :taggable_type, default: 'Photo'
  43597. # end
  43598. # add_index :taggings, :tag_id, name: 'index_taggings_on_tag_id'
  43599. # add_index :taggings, [:tagger_id, :tagger_type]
  43600. #
  43601. # Can also be written as follows using references:
  43602. #
  43603. # create_table :taggings do |t|
  43604. # t.references :tag, index: { name: 'index_taggings_on_tag_id' }
  43605. # t.references :tagger, polymorphic: true, index: true
  43606. # t.references :taggable, polymorphic: { default: 'Photo' }
  43607. # end
  43608. def column(name, type, options = {})
  43609. name = name.to_s
  43610. type = type.to_sym
  43611. if primary_key_column_name == name
  43612. raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
  43613. end
  43614. column = self[name] || new_column_definition(@base, name, type)
  43615. limit = options.fetch(:limit) do
  43616. native[type][:limit] if native[type].is_a?(Hash)
  43617. end
  43618. column.limit = limit
  43619. column.precision = options[:precision]
  43620. column.scale = options[:scale]
  43621. column.default = options[:default]
  43622. column.null = options[:null]
  43623. self
  43624. end
  43625. [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
  43626. define_method column_type do |*args|
  43627. options = args.extract_options!
  43628. column_names = args
  43629. column_names.each { |name| column(name, column_type, options) }
  43630. end
  43631. end
  43632. # Adds index options to the indexes hash, keyed by column name
  43633. # This is primarily used to track indexes that need to be created after the table
  43634. #
  43635. # index(:account_id, name: 'index_projects_on_account_id')
  43636. def index(column_name, options = {})
  43637. indexes[column_name] = options
  43638. end
  43639. # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
  43640. # <tt>:updated_at</tt> to the table.
  43641. def timestamps(*args)
  43642. options = args.extract_options!
  43643. column(:created_at, :datetime, options)
  43644. column(:updated_at, :datetime, options)
  43645. end
  43646. def references(*args)
  43647. options = args.extract_options!
  43648. polymorphic = options.delete(:polymorphic)
  43649. index_options = options.delete(:index)
  43650. args.each do |col|
  43651. column("#{col}_id", :integer, options)
  43652. column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
  43653. index(polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
  43654. end
  43655. end
  43656. alias :belongs_to :references
  43657. # Returns a String whose contents are the column definitions
  43658. # concatenated together. This string can then be prepended and appended to
  43659. # to generate the final SQL to create the table.
  43660. def to_sql
  43661. @columns.map { |c| c.to_sql } * ', '
  43662. end
  43663. private
  43664. def new_column_definition(base, name, type)
  43665. definition = ColumnDefinition.new base, name, type
  43666. @columns << definition
  43667. @columns_hash[name] = definition
  43668. definition
  43669. end
  43670. def primary_key_column_name
  43671. primary_key_column = columns.detect { |c| c.type == :primary_key }
  43672. primary_key_column && primary_key_column.name
  43673. end
  43674. def native
  43675. @base.native_database_types
  43676. end
  43677. end
  43678. # Represents an SQL table in an abstract way for updating a table.
  43679. # Also see TableDefinition and SchemaStatements#create_table
  43680. #
  43681. # Available transformations are:
  43682. #
  43683. # change_table :table do |t|
  43684. # t.column
  43685. # t.index
  43686. # t.rename_index
  43687. # t.timestamps
  43688. # t.change
  43689. # t.change_default
  43690. # t.rename
  43691. # t.references
  43692. # t.belongs_to
  43693. # t.string
  43694. # t.text
  43695. # t.integer
  43696. # t.float
  43697. # t.decimal
  43698. # t.datetime
  43699. # t.timestamp
  43700. # t.time
  43701. # t.date
  43702. # t.binary
  43703. # t.boolean
  43704. # t.remove
  43705. # t.remove_references
  43706. # t.remove_belongs_to
  43707. # t.remove_index
  43708. # t.remove_timestamps
  43709. # end
  43710. #
  43711. class Table
  43712. def initialize(table_name, base)
  43713. @table_name = table_name
  43714. @base = base
  43715. end
  43716. # Adds a new column to the named table.
  43717. # See TableDefinition#column for details of the options you can use.
  43718. #
  43719. # ====== Creating a simple column
  43720. # t.column(:name, :string)
  43721. def column(column_name, type, options = {})
  43722. @base.add_column(@table_name, column_name, type, options)
  43723. end
  43724. # Checks to see if a column exists. See SchemaStatements#column_exists?
  43725. def column_exists?(column_name, type = nil, options = {})
  43726. @base.column_exists?(@table_name, column_name, type, options)
  43727. end
  43728. # Adds a new index to the table. +column_name+ can be a single Symbol, or
  43729. # an Array of Symbols. See SchemaStatements#add_index
  43730. #
  43731. # ====== Creating a simple index
  43732. # t.index(:name)
  43733. # ====== Creating a unique index
  43734. # t.index([:branch_id, :party_id], unique: true)
  43735. # ====== Creating a named index
  43736. # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
  43737. def index(column_name, options = {})
  43738. @base.add_index(@table_name, column_name, options)
  43739. end
  43740. # Checks to see if an index exists. See SchemaStatements#index_exists?
  43741. def index_exists?(column_name, options = {})
  43742. @base.index_exists?(@table_name, column_name, options)
  43743. end
  43744. # Renames the given index on the table.
  43745. #
  43746. # t.rename_index(:user_id, :account_id)
  43747. def rename_index(index_name, new_index_name)
  43748. @base.rename_index(@table_name, index_name, new_index_name)
  43749. end
  43750. # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
  43751. #
  43752. # t.timestamps
  43753. def timestamps
  43754. @base.add_timestamps(@table_name)
  43755. end
  43756. # Changes the column's definition according to the new options.
  43757. # See TableDefinition#column for details of the options you can use.
  43758. #
  43759. # t.change(:name, :string, limit: 80)
  43760. # t.change(:description, :text)
  43761. def change(column_name, type, options = {})
  43762. @base.change_column(@table_name, column_name, type, options)
  43763. end
  43764. # Sets a new default value for a column. See SchemaStatements#change_column_default
  43765. #
  43766. # t.change_default(:qualification, 'new')
  43767. # t.change_default(:authorized, 1)
  43768. def change_default(column_name, default)
  43769. @base.change_column_default(@table_name, column_name, default)
  43770. end
  43771. # Removes the column(s) from the table definition.
  43772. #
  43773. # t.remove(:qualification)
  43774. # t.remove(:qualification, :experience)
  43775. def remove(*column_names)
  43776. @base.remove_columns(@table_name, *column_names)
  43777. end
  43778. # Removes the given index from the table.
  43779. #
  43780. # ====== Remove the index_table_name_on_column in the table_name table
  43781. # t.remove_index :column
  43782. # ====== Remove the index named index_table_name_on_branch_id in the table_name table
  43783. # t.remove_index column: :branch_id
  43784. # ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
  43785. # t.remove_index column: [:branch_id, :party_id]
  43786. # ====== Remove the index named by_branch_party in the table_name table
  43787. # t.remove_index name: :by_branch_party
  43788. def remove_index(options = {})
  43789. @base.remove_index(@table_name, options)
  43790. end
  43791. # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
  43792. #
  43793. # t.remove_timestamps
  43794. def remove_timestamps
  43795. @base.remove_timestamps(@table_name)
  43796. end
  43797. # Renames a column.
  43798. #
  43799. # t.rename(:description, :name)
  43800. def rename(column_name, new_column_name)
  43801. @base.rename_column(@table_name, column_name, new_column_name)
  43802. end
  43803. # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
  43804. # <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
  43805. #
  43806. # t.references(:user)
  43807. # t.belongs_to(:supplier, polymorphic: true)
  43808. #
  43809. def references(*args)
  43810. options = args.extract_options!
  43811. args.each do |ref_name|
  43812. @base.add_reference(@table_name, ref_name, options)
  43813. end
  43814. end
  43815. alias :belongs_to :references
  43816. # Removes a reference. Optionally removes a +type+ column.
  43817. # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
  43818. #
  43819. # t.remove_references(:user)
  43820. # t.remove_belongs_to(:supplier, polymorphic: true)
  43821. #
  43822. def remove_references(*args)
  43823. options = args.extract_options!
  43824. args.each do |ref_name|
  43825. @base.remove_reference(@table_name, ref_name, options)
  43826. end
  43827. end
  43828. alias :remove_belongs_to :remove_references
  43829. # Adds a column or columns of a specified type
  43830. #
  43831. # t.string(:goat)
  43832. # t.string(:goat, :sheep)
  43833. [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
  43834. define_method column_type do |*args|
  43835. options = args.extract_options!
  43836. args.each do |name|
  43837. @base.add_column(@table_name, name, column_type, options)
  43838. end
  43839. end
  43840. end
  43841. private
  43842. def native
  43843. @base.native_database_types
  43844. end
  43845. end
  43846. end
  43847. end
  43848. module ActiveRecord
  43849. module ConnectionAdapters # :nodoc:
  43850. # The goal of this module is to move Adapter specific column
  43851. # definitions to the Adapter instead of having it in the schema
  43852. # dumper itself. This code represents the normal case.
  43853. # We can then redefine how certain data types may be handled in the schema dumper on the
  43854. # Adapter level by over-writing this code inside the database spececific adapters
  43855. module ColumnDumper
  43856. def column_spec(column, types)
  43857. spec = prepare_column_options(column, types)
  43858. (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.to_s}: ")}
  43859. spec
  43860. end
  43861. # This can be overridden on a Adapter level basis to support other
  43862. # extended datatypes (Example: Adding an array option in the
  43863. # PostgreSQLAdapter)
  43864. def prepare_column_options(column, types)
  43865. spec = {}
  43866. spec[:name] = column.name.inspect
  43867. # AR has an optimization which handles zero-scale decimals as integers. This
  43868. # code ensures that the dumper still dumps the column as a decimal.
  43869. spec[:type] = if column.type == :integer && /^(numeric|decimal)/ =~ column.sql_type
  43870. 'decimal'
  43871. else
  43872. column.type.to_s
  43873. end
  43874. spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit] && spec[:type] != 'decimal'
  43875. spec[:precision] = column.precision.inspect if column.precision
  43876. spec[:scale] = column.scale.inspect if column.scale
  43877. spec[:null] = 'false' unless column.null
  43878. spec[:default] = default_string(column.default) if column.has_default?
  43879. spec
  43880. end
  43881. # Lists the valid migration options
  43882. def migration_keys
  43883. [:name, :limit, :precision, :scale, :default, :null]
  43884. end
  43885. private
  43886. def default_string(value)
  43887. case value
  43888. when BigDecimal
  43889. value.to_s
  43890. when Date, DateTime, Time
  43891. "'#{value.to_s(:db)}'"
  43892. when Range
  43893. # infinity dumps as Infinity, which causes uninitialized constant error
  43894. value.inspect.gsub('Infinity', '::Float::INFINITY')
  43895. else
  43896. value.inspect
  43897. end
  43898. end
  43899. end
  43900. end
  43901. end
  43902. require 'active_record/migration/join_table'
  43903. module ActiveRecord
  43904. module ConnectionAdapters # :nodoc:
  43905. module SchemaStatements
  43906. include ActiveRecord::Migration::JoinTable
  43907. # Returns a Hash of mappings from the abstract data types to the native
  43908. # database types. See TableDefinition#column for details on the recognized
  43909. # abstract data types.
  43910. def native_database_types
  43911. {}
  43912. end
  43913. # Truncates a table alias according to the limits of the current adapter.
  43914. def table_alias_for(table_name)
  43915. table_name[0...table_alias_length].tr('.', '_')
  43916. end
  43917. # Checks to see if the table +table_name+ exists on the database.
  43918. #
  43919. # table_exists?(:developers)
  43920. def table_exists?(table_name)
  43921. tables.include?(table_name.to_s)
  43922. end
  43923. # Returns an array of indexes for the given table.
  43924. # def indexes(table_name, name = nil) end
  43925. # Checks to see if an index exists on a table for a given index definition.
  43926. #
  43927. # # Check an index exists
  43928. # index_exists?(:suppliers, :company_id)
  43929. #
  43930. # # Check an index on multiple columns exists
  43931. # index_exists?(:suppliers, [:company_id, :company_type])
  43932. #
  43933. # # Check a unique index exists
  43934. # index_exists?(:suppliers, :company_id, unique: true)
  43935. #
  43936. # # Check an index with a custom name exists
  43937. # index_exists?(:suppliers, :company_id, name: "idx_company_id"
  43938. def index_exists?(table_name, column_name, options = {})
  43939. column_names = Array(column_name)
  43940. index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
  43941. if options[:unique]
  43942. indexes(table_name).any?{ |i| i.unique && i.name == index_name }
  43943. else
  43944. indexes(table_name).any?{ |i| i.name == index_name }
  43945. end
  43946. end
  43947. # Returns an array of Column objects for the table specified by +table_name+.
  43948. # See the concrete implementation for details on the expected parameter values.
  43949. def columns(table_name) end
  43950. # Checks to see if a column exists in a given table.
  43951. #
  43952. # # Check a column exists
  43953. # column_exists?(:suppliers, :name)
  43954. #
  43955. # # Check a column exists of a particular type
  43956. # column_exists?(:suppliers, :name, :string)
  43957. #
  43958. # # Check a column exists with a specific definition
  43959. # column_exists?(:suppliers, :name, :string, limit: 100)
  43960. # column_exists?(:suppliers, :name, :string, default: 'default')
  43961. # column_exists?(:suppliers, :name, :string, null: false)
  43962. # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
  43963. def column_exists?(table_name, column_name, type = nil, options = {})
  43964. columns(table_name).any?{ |c| c.name == column_name.to_s &&
  43965. (!type || c.type == type) &&
  43966. (!options.key?(:limit) || c.limit == options[:limit]) &&
  43967. (!options.key?(:precision) || c.precision == options[:precision]) &&
  43968. (!options.key?(:scale) || c.scale == options[:scale]) &&
  43969. (!options.key?(:default) || c.default == options[:default]) &&
  43970. (!options.key?(:null) || c.null == options[:null]) }
  43971. end
  43972. # Creates a new table with the name +table_name+. +table_name+ may either
  43973. # be a String or a Symbol.
  43974. #
  43975. # There are two ways to work with +create_table+. You can use the block
  43976. # form or the regular form, like this:
  43977. #
  43978. # === Block form
  43979. # # create_table() passes a TableDefinition object to the block.
  43980. # # This form will not only create the table, but also columns for the
  43981. # # table.
  43982. #
  43983. # create_table(:suppliers) do |t|
  43984. # t.column :name, :string, limit: 60
  43985. # # Other fields here
  43986. # end
  43987. #
  43988. # === Block form, with shorthand
  43989. # # You can also use the column types as method calls, rather than calling the column method.
  43990. # create_table(:suppliers) do |t|
  43991. # t.string :name, limit: 60
  43992. # # Other fields here
  43993. # end
  43994. #
  43995. # === Regular form
  43996. # # Creates a table called 'suppliers' with no columns.
  43997. # create_table(:suppliers)
  43998. # # Add a column to 'suppliers'.
  43999. # add_column(:suppliers, :name, :string, {limit: 60})
  44000. #
  44001. # The +options+ hash can include the following keys:
  44002. # [<tt>:id</tt>]
  44003. # Whether to automatically add a primary key column. Defaults to true.
  44004. # Join tables for +has_and_belongs_to_many+ should set it to false.
  44005. # [<tt>:primary_key</tt>]
  44006. # The name of the primary key, if one is to be added automatically.
  44007. # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
  44008. #
  44009. # Also note that this just sets the primary key in the table. You additionally
  44010. # need to configure the primary key in the model via +self.primary_key=+.
  44011. # Models do NOT auto-detect the primary key from their table definition.
  44012. #
  44013. # [<tt>:options</tt>]
  44014. # Any extra options you want appended to the table definition.
  44015. # [<tt>:temporary</tt>]
  44016. # Make a temporary table.
  44017. # [<tt>:force</tt>]
  44018. # Set to true to drop the table before creating it.
  44019. # Defaults to false.
  44020. #
  44021. # ====== Add a backend specific option to the generated SQL (MySQL)
  44022. # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
  44023. # generates:
  44024. # CREATE TABLE suppliers (
  44025. # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
  44026. # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  44027. #
  44028. # ====== Rename the primary key column
  44029. # create_table(:objects, primary_key: 'guid') do |t|
  44030. # t.column :name, :string, limit: 80
  44031. # end
  44032. # generates:
  44033. # CREATE TABLE objects (
  44034. # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
  44035. # name varchar(80)
  44036. # )
  44037. #
  44038. # ====== Do not add a primary key column
  44039. # create_table(:categories_suppliers, id: false) do |t|
  44040. # t.column :category_id, :integer
  44041. # t.column :supplier_id, :integer
  44042. # end
  44043. # generates:
  44044. # CREATE TABLE categories_suppliers (
  44045. # category_id int,
  44046. # supplier_id int
  44047. # )
  44048. #
  44049. # See also TableDefinition#column for details on how to create columns.
  44050. def create_table(table_name, options = {})
  44051. td = table_definition
  44052. td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
  44053. yield td if block_given?
  44054. if options[:force] && table_exists?(table_name)
  44055. drop_table(table_name, options)
  44056. end
  44057. create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
  44058. create_sql << "#{quote_table_name(table_name)} ("
  44059. create_sql << td.to_sql
  44060. create_sql << ") #{options[:options]}"
  44061. execute create_sql
  44062. td.indexes.each_pair { |c,o| add_index table_name, c, o }
  44063. end
  44064. # Creates a new join table with the name created using the lexical order of the first two
  44065. # arguments. These arguments can be a String or a Symbol.
  44066. #
  44067. # # Creates a table called 'assemblies_parts' with no id.
  44068. # create_join_table(:assemblies, :parts)
  44069. #
  44070. # You can pass a +options+ hash can include the following keys:
  44071. # [<tt>:table_name</tt>]
  44072. # Sets the table name overriding the default
  44073. # [<tt>:column_options</tt>]
  44074. # Any extra options you want appended to the columns definition.
  44075. # [<tt>:options</tt>]
  44076. # Any extra options you want appended to the table definition.
  44077. # [<tt>:temporary</tt>]
  44078. # Make a temporary table.
  44079. # [<tt>:force</tt>]
  44080. # Set to true to drop the table before creating it.
  44081. # Defaults to false.
  44082. #
  44083. # ====== Add a backend specific option to the generated SQL (MySQL)
  44084. # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
  44085. # generates:
  44086. # CREATE TABLE assemblies_parts (
  44087. # assembly_id int NOT NULL,
  44088. # part_id int NOT NULL,
  44089. # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  44090. def create_join_table(table_1, table_2, options = {})
  44091. join_table_name = find_join_table_name(table_1, table_2, options)
  44092. column_options = options.delete(:column_options) || {}
  44093. column_options.reverse_merge!(null: false)
  44094. t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
  44095. create_table(join_table_name, options.merge!(id: false)) do |td|
  44096. td.integer t1_column, column_options
  44097. td.integer t2_column, column_options
  44098. yield td if block_given?
  44099. end
  44100. end
  44101. # Drops the join table specified by the given arguments.
  44102. # See create_join_table for details.
  44103. #
  44104. # Although this command ignores the block if one is given, it can be helpful
  44105. # to provide one in a migration's +change+ method so it can be reverted.
  44106. # In that case, the block will be used by create_join_table.
  44107. def drop_join_table(table_1, table_2, options = {})
  44108. join_table_name = find_join_table_name(table_1, table_2, options)
  44109. drop_table(join_table_name)
  44110. end
  44111. # A block for changing columns in +table+.
  44112. #
  44113. # # change_table() yields a Table instance
  44114. # change_table(:suppliers) do |t|
  44115. # t.column :name, :string, limit: 60
  44116. # # Other column alterations here
  44117. # end
  44118. #
  44119. # The +options+ hash can include the following keys:
  44120. # [<tt>:bulk</tt>]
  44121. # Set this to true to make this a bulk alter query, such as
  44122. # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
  44123. #
  44124. # Defaults to false.
  44125. #
  44126. # ====== Add a column
  44127. # change_table(:suppliers) do |t|
  44128. # t.column :name, :string, limit: 60
  44129. # end
  44130. #
  44131. # ====== Add 2 integer columns
  44132. # change_table(:suppliers) do |t|
  44133. # t.integer :width, :height, null: false, default: 0
  44134. # end
  44135. #
  44136. # ====== Add created_at/updated_at columns
  44137. # change_table(:suppliers) do |t|
  44138. # t.timestamps
  44139. # end
  44140. #
  44141. # ====== Add a foreign key column
  44142. # change_table(:suppliers) do |t|
  44143. # t.references :company
  44144. # end
  44145. #
  44146. # Creates a <tt>company_id(integer)</tt> column
  44147. #
  44148. # ====== Add a polymorphic foreign key column
  44149. # change_table(:suppliers) do |t|
  44150. # t.belongs_to :company, polymorphic: true
  44151. # end
  44152. #
  44153. # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
  44154. #
  44155. # ====== Remove a column
  44156. # change_table(:suppliers) do |t|
  44157. # t.remove :company
  44158. # end
  44159. #
  44160. # ====== Remove several columns
  44161. # change_table(:suppliers) do |t|
  44162. # t.remove :company_id
  44163. # t.remove :width, :height
  44164. # end
  44165. #
  44166. # ====== Remove an index
  44167. # change_table(:suppliers) do |t|
  44168. # t.remove_index :company_id
  44169. # end
  44170. #
  44171. # See also Table for details on
  44172. # all of the various column transformation
  44173. def change_table(table_name, options = {})
  44174. if supports_bulk_alter? && options[:bulk]
  44175. recorder = ActiveRecord::Migration::CommandRecorder.new(self)
  44176. yield Table.new(table_name, recorder)
  44177. bulk_change_table(table_name, recorder.commands)
  44178. else
  44179. yield Table.new(table_name, self)
  44180. end
  44181. end
  44182. # Renames a table.
  44183. #
  44184. # rename_table('octopuses', 'octopi')
  44185. def rename_table(table_name, new_name)
  44186. raise NotImplementedError, "rename_table is not implemented"
  44187. end
  44188. # Drops a table from the database.
  44189. #
  44190. # Although this command ignores +options+ and the block if one is given, it can be helpful
  44191. # to provide these in a migration's +change+ method so it can be reverted.
  44192. # In that case, +options+ and the block will be used by create_table.
  44193. def drop_table(table_name, options = {})
  44194. execute "DROP TABLE #{quote_table_name(table_name)}"
  44195. end
  44196. # Adds a new column to the named table.
  44197. # See TableDefinition#column for details of the options you can use.
  44198. def add_column(table_name, column_name, type, options = {})
  44199. add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  44200. add_column_options!(add_column_sql, options)
  44201. execute(add_column_sql)
  44202. end
  44203. # Removes the given columns from the table definition.
  44204. #
  44205. # remove_columns(:suppliers, :qualification, :experience)
  44206. def remove_columns(table_name, *column_names)
  44207. raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
  44208. column_names.each do |column_name|
  44209. remove_column(table_name, column_name)
  44210. end
  44211. end
  44212. # Removes the column from the table definition.
  44213. #
  44214. # remove_column(:suppliers, :qualification)
  44215. #
  44216. # The +type+ and +options+ parameters will be ignored if present. It can be helpful
  44217. # to provide these in a migration's +change+ method so it can be reverted.
  44218. # In that case, +type+ and +options+ will be used by add_column.
  44219. def remove_column(table_name, column_name, type = nil, options = {})
  44220. execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
  44221. end
  44222. # Changes the column's definition according to the new options.
  44223. # See TableDefinition#column for details of the options you can use.
  44224. #
  44225. # change_column(:suppliers, :name, :string, limit: 80)
  44226. # change_column(:accounts, :description, :text)
  44227. def change_column(table_name, column_name, type, options = {})
  44228. raise NotImplementedError, "change_column is not implemented"
  44229. end
  44230. # Sets a new default value for a column.
  44231. #
  44232. # change_column_default(:suppliers, :qualification, 'new')
  44233. # change_column_default(:accounts, :authorized, 1)
  44234. # change_column_default(:users, :email, nil)
  44235. def change_column_default(table_name, column_name, default)
  44236. raise NotImplementedError, "change_column_default is not implemented"
  44237. end
  44238. # Renames a column.
  44239. #
  44240. # rename_column(:suppliers, :description, :name)
  44241. def rename_column(table_name, column_name, new_column_name)
  44242. raise NotImplementedError, "rename_column is not implemented"
  44243. end
  44244. # Adds a new index to the table. +column_name+ can be a single Symbol, or
  44245. # an Array of Symbols.
  44246. #
  44247. # The index will be named after the table and the column name(s), unless
  44248. # you pass <tt>:name</tt> as an option.
  44249. #
  44250. # ====== Creating a simple index
  44251. # add_index(:suppliers, :name)
  44252. # generates
  44253. # CREATE INDEX suppliers_name_index ON suppliers(name)
  44254. #
  44255. # ====== Creating a unique index
  44256. # add_index(:accounts, [:branch_id, :party_id], unique: true)
  44257. # generates
  44258. # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
  44259. #
  44260. # ====== Creating a named index
  44261. # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
  44262. # generates
  44263. # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
  44264. #
  44265. # ====== Creating an index with specific key length
  44266. # add_index(:accounts, :name, name: 'by_name', length: 10)
  44267. # generates
  44268. # CREATE INDEX by_name ON accounts(name(10))
  44269. #
  44270. # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
  44271. # generates
  44272. # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
  44273. #
  44274. # Note: SQLite doesn't support index length
  44275. #
  44276. # ====== Creating an index with a sort order (desc or asc, asc is the default)
  44277. # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
  44278. # generates
  44279. # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
  44280. #
  44281. # Note: mysql doesn't yet support index order (it accepts the syntax but ignores it)
  44282. #
  44283. # ====== Creating a partial index
  44284. # add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
  44285. # generates
  44286. # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
  44287. #
  44288. # Note: only supported by PostgreSQL
  44289. #
  44290. def add_index(table_name, column_name, options = {})
  44291. index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
  44292. execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
  44293. end
  44294. # Remove the given index from the table.
  44295. #
  44296. # Remove the index_accounts_on_column in the accounts table.
  44297. # remove_index :accounts, :column
  44298. # Remove the index named index_accounts_on_branch_id in the accounts table.
  44299. # remove_index :accounts, column: :branch_id
  44300. # Remove the index named index_accounts_on_branch_id_and_party_id in the accounts table.
  44301. # remove_index :accounts, column: [:branch_id, :party_id]
  44302. # Remove the index named by_branch_party in the accounts table.
  44303. # remove_index :accounts, name: :by_branch_party
  44304. def remove_index(table_name, options = {})
  44305. remove_index!(table_name, index_name_for_remove(table_name, options))
  44306. end
  44307. def remove_index!(table_name, index_name) #:nodoc:
  44308. execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
  44309. end
  44310. # Rename an index.
  44311. #
  44312. # Rename the index_people_on_last_name index to index_users_on_last_name
  44313. # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
  44314. def rename_index(table_name, old_name, new_name)
  44315. # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
  44316. old_index_def = indexes(table_name).detect { |i| i.name == old_name }
  44317. return unless old_index_def
  44318. remove_index(table_name, :name => old_name)
  44319. add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique)
  44320. end
  44321. def index_name(table_name, options) #:nodoc:
  44322. if Hash === options
  44323. if options[:column]
  44324. "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
  44325. elsif options[:name]
  44326. options[:name]
  44327. else
  44328. raise ArgumentError, "You must specify the index name"
  44329. end
  44330. else
  44331. index_name(table_name, :column => options)
  44332. end
  44333. end
  44334. # Verify the existence of an index with a given name.
  44335. #
  44336. # The default argument is returned if the underlying implementation does not define the indexes method,
  44337. # as there's no way to determine the correct answer in that case.
  44338. def index_name_exists?(table_name, index_name, default)
  44339. return default unless respond_to?(:indexes)
  44340. index_name = index_name.to_s
  44341. indexes(table_name).detect { |i| i.name == index_name }
  44342. end
  44343. # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
  44344. # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
  44345. #
  44346. # ====== Create a user_id column
  44347. # add_reference(:products, :user)
  44348. #
  44349. # ====== Create a supplier_id and supplier_type columns
  44350. # add_belongs_to(:products, :supplier, polymorphic: true)
  44351. #
  44352. # ====== Create a supplier_id, supplier_type columns and appropriate index
  44353. # add_reference(:products, :supplier, polymorphic: true, index: true)
  44354. #
  44355. def add_reference(table_name, ref_name, options = {})
  44356. polymorphic = options.delete(:polymorphic)
  44357. index_options = options.delete(:index)
  44358. add_column(table_name, "#{ref_name}_id", :integer, options)
  44359. add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
  44360. add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
  44361. end
  44362. alias :add_belongs_to :add_reference
  44363. # Removes the reference(s). Also removes a +type+ column if one exists.
  44364. # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
  44365. #
  44366. # ====== Remove the reference
  44367. # remove_reference(:products, :user, index: true)
  44368. #
  44369. # ====== Remove polymorphic reference
  44370. # remove_reference(:products, :supplier, polymorphic: true)
  44371. #
  44372. def remove_reference(table_name, ref_name, options = {})
  44373. remove_column(table_name, "#{ref_name}_id")
  44374. remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
  44375. end
  44376. alias :remove_belongs_to :remove_reference
  44377. # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
  44378. # entire structure of the database.
  44379. def structure_dump
  44380. end
  44381. def dump_schema_information #:nodoc:
  44382. sm_table = ActiveRecord::Migrator.schema_migrations_table_name
  44383. ActiveRecord::SchemaMigration.order('version').map { |sm|
  44384. "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
  44385. }.join "\n\n"
  44386. end
  44387. # Should not be called normally, but this operation is non-destructive.
  44388. # The migrations module handles this automatically.
  44389. def initialize_schema_migrations_table
  44390. ActiveRecord::SchemaMigration.create_table
  44391. end
  44392. def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
  44393. migrations_paths = Array(migrations_paths)
  44394. version = version.to_i
  44395. sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
  44396. migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
  44397. paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
  44398. versions = Dir[*paths].map do |filename|
  44399. filename.split('/').last.split('_').first.to_i
  44400. end
  44401. unless migrated.include?(version)
  44402. execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
  44403. end
  44404. inserted = Set.new
  44405. (versions - migrated).each do |v|
  44406. if inserted.include?(v)
  44407. raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
  44408. elsif v < version
  44409. execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
  44410. inserted << v
  44411. end
  44412. end
  44413. end
  44414. def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
  44415. if native = native_database_types[type.to_sym]
  44416. column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
  44417. if type == :decimal # ignore limit, use precision and scale
  44418. scale ||= native[:scale]
  44419. if precision ||= native[:precision]
  44420. if scale
  44421. column_type_sql << "(#{precision},#{scale})"
  44422. else
  44423. column_type_sql << "(#{precision})"
  44424. end
  44425. elsif scale
  44426. raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
  44427. end
  44428. elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
  44429. column_type_sql << "(#{limit})"
  44430. end
  44431. column_type_sql
  44432. else
  44433. type
  44434. end
  44435. end
  44436. def add_column_options!(sql, options) #:nodoc:
  44437. sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
  44438. # must explicitly check for :null to allow change_column to work on migrations
  44439. if options[:null] == false
  44440. sql << " NOT NULL"
  44441. end
  44442. end
  44443. # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
  44444. # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
  44445. #
  44446. # distinct("posts.id", "posts.created_at desc")
  44447. def distinct(columns, order_by)
  44448. "DISTINCT #{columns}"
  44449. end
  44450. # Adds timestamps (created_at and updated_at) columns to the named table.
  44451. #
  44452. # add_timestamps(:suppliers)
  44453. def add_timestamps(table_name)
  44454. add_column table_name, :created_at, :datetime
  44455. add_column table_name, :updated_at, :datetime
  44456. end
  44457. # Removes the timestamp columns (created_at and updated_at) from the table definition.
  44458. #
  44459. # remove_timestamps(:suppliers)
  44460. def remove_timestamps(table_name)
  44461. remove_column table_name, :updated_at
  44462. remove_column table_name, :created_at
  44463. end
  44464. protected
  44465. def add_index_sort_order(option_strings, column_names, options = {})
  44466. if options.is_a?(Hash) && order = options[:order]
  44467. case order
  44468. when Hash
  44469. column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
  44470. when String
  44471. column_names.each {|name| option_strings[name] += " #{order.upcase}"}
  44472. end
  44473. end
  44474. return option_strings
  44475. end
  44476. # Overridden by the mysql adapter for supporting index lengths
  44477. def quoted_columns_for_index(column_names, options = {})
  44478. option_strings = Hash[column_names.map {|name| [name, '']}]
  44479. # add index sort order if supported
  44480. if supports_index_sort_order?
  44481. option_strings = add_index_sort_order(option_strings, column_names, options)
  44482. end
  44483. column_names.map {|name| quote_column_name(name) + option_strings[name]}
  44484. end
  44485. def options_include_default?(options)
  44486. options.include?(:default) && !(options[:null] == false && options[:default].nil?)
  44487. end
  44488. def add_index_options(table_name, column_name, options = {})
  44489. column_names = Array(column_name)
  44490. index_name = index_name(table_name, column: column_names)
  44491. if Hash === options # legacy support, since this param was a string
  44492. options.assert_valid_keys(:unique, :order, :name, :where, :length)
  44493. index_type = options[:unique] ? "UNIQUE" : ""
  44494. index_name = options[:name].to_s if options.key?(:name)
  44495. if supports_partial_index?
  44496. index_options = options[:where] ? " WHERE #{options[:where]}" : ""
  44497. end
  44498. else
  44499. if options
  44500. message = "Passing a string as third argument of `add_index` is deprecated and will" +
  44501. " be removed in Rails 4.1." +
  44502. " Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead"
  44503. ActiveSupport::Deprecation.warn message
  44504. end
  44505. index_type = options
  44506. end
  44507. if index_name.length > index_name_length
  44508. raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
  44509. end
  44510. if index_name_exists?(table_name, index_name, false)
  44511. raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
  44512. end
  44513. index_columns = quoted_columns_for_index(column_names, options).join(", ")
  44514. [index_name, index_type, index_columns, index_options]
  44515. end
  44516. def index_name_for_remove(table_name, options = {})
  44517. index_name = index_name(table_name, options)
  44518. unless index_name_exists?(table_name, index_name, true)
  44519. raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
  44520. end
  44521. index_name
  44522. end
  44523. def columns_for_remove(table_name, *column_names)
  44524. ActiveSupport::Deprecation.warn("columns_for_remove is deprecated and will be removed in the future")
  44525. raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.blank?
  44526. column_names.map {|column_name| quote_column_name(column_name) }
  44527. end
  44528. private
  44529. def table_definition
  44530. TableDefinition.new(self)
  44531. end
  44532. end
  44533. end
  44534. end
  44535. module ActiveRecord
  44536. module ConnectionAdapters
  44537. class Transaction #:nodoc:
  44538. attr_reader :connection
  44539. def initialize(connection)
  44540. @connection = connection
  44541. @state = TransactionState.new
  44542. end
  44543. def state
  44544. @state
  44545. end
  44546. end
  44547. class TransactionState
  44548. VALID_STATES = Set.new([:committed, :rolledback, nil])
  44549. def initialize(state = nil)
  44550. @state = state
  44551. end
  44552. def committed?
  44553. @state == :committed
  44554. end
  44555. def rolledback?
  44556. @state == :rolledback
  44557. end
  44558. def set_state(state)
  44559. if !VALID_STATES.include?(state)
  44560. raise ArgumentError, "Invalid transaction state: #{state}"
  44561. end
  44562. @state = state
  44563. end
  44564. end
  44565. class ClosedTransaction < Transaction #:nodoc:
  44566. def number
  44567. 0
  44568. end
  44569. def begin(options = {})
  44570. RealTransaction.new(connection, self, options)
  44571. end
  44572. def closed?
  44573. true
  44574. end
  44575. def open?
  44576. false
  44577. end
  44578. def joinable?
  44579. false
  44580. end
  44581. # This is a noop when there are no open transactions
  44582. def add_record(record)
  44583. end
  44584. end
  44585. class OpenTransaction < Transaction #:nodoc:
  44586. attr_reader :parent, :records
  44587. attr_writer :joinable
  44588. def initialize(connection, parent, options = {})
  44589. super connection
  44590. @parent = parent
  44591. @records = []
  44592. @finishing = false
  44593. @joinable = options.fetch(:joinable, true)
  44594. end
  44595. # This state is necesarry so that we correctly handle stuff that might
  44596. # happen in a commit/rollback. But it's kinda distasteful. Maybe we can
  44597. # find a better way to structure it in the future.
  44598. def finishing?
  44599. @finishing
  44600. end
  44601. def joinable?
  44602. @joinable && !finishing?
  44603. end
  44604. def number
  44605. if finishing?
  44606. parent.number
  44607. else
  44608. parent.number + 1
  44609. end
  44610. end
  44611. def begin(options = {})
  44612. if finishing?
  44613. parent.begin
  44614. else
  44615. SavepointTransaction.new(connection, self, options)
  44616. end
  44617. end
  44618. def rollback
  44619. @finishing = true
  44620. perform_rollback
  44621. parent
  44622. end
  44623. def commit
  44624. @finishing = true
  44625. perform_commit
  44626. parent
  44627. end
  44628. def add_record(record)
  44629. records << record
  44630. end
  44631. def rollback_records
  44632. @state.set_state(:rolledback)
  44633. records.uniq.each do |record|
  44634. begin
  44635. record.rolledback!(parent.closed?)
  44636. rescue => e
  44637. record.logger.error(e) if record.respond_to?(:logger) && record.logger
  44638. end
  44639. end
  44640. end
  44641. def commit_records
  44642. @state.set_state(:committed)
  44643. records.uniq.each do |record|
  44644. begin
  44645. record.committed!
  44646. rescue => e
  44647. record.logger.error(e) if record.respond_to?(:logger) && record.logger
  44648. end
  44649. end
  44650. end
  44651. def closed?
  44652. false
  44653. end
  44654. def open?
  44655. true
  44656. end
  44657. end
  44658. class RealTransaction < OpenTransaction #:nodoc:
  44659. def initialize(connection, parent, options = {})
  44660. super
  44661. if options[:isolation]
  44662. connection.begin_isolated_db_transaction(options[:isolation])
  44663. else
  44664. connection.begin_db_transaction
  44665. end
  44666. end
  44667. def perform_rollback
  44668. connection.rollback_db_transaction
  44669. rollback_records
  44670. end
  44671. def perform_commit
  44672. connection.commit_db_transaction
  44673. commit_records
  44674. end
  44675. end
  44676. class SavepointTransaction < OpenTransaction #:nodoc:
  44677. def initialize(connection, parent, options = {})
  44678. if options[:isolation]
  44679. raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
  44680. end
  44681. super
  44682. connection.create_savepoint
  44683. end
  44684. def perform_rollback
  44685. connection.rollback_to_savepoint
  44686. rollback_records
  44687. end
  44688. def perform_commit
  44689. connection.release_savepoint
  44690. records.each { |r| parent.add_record(r) }
  44691. end
  44692. end
  44693. end
  44694. end
  44695. require 'date'
  44696. require 'bigdecimal'
  44697. require 'bigdecimal/util'
  44698. require 'active_support/core_ext/benchmark'
  44699. require 'active_record/connection_adapters/schema_cache'
  44700. require 'active_record/connection_adapters/abstract/schema_dumper'
  44701. require 'monitor'
  44702. module ActiveRecord
  44703. module ConnectionAdapters # :nodoc:
  44704. extend ActiveSupport::Autoload
  44705. autoload :Column
  44706. autoload :ConnectionSpecification
  44707. autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do
  44708. autoload :IndexDefinition
  44709. autoload :ColumnDefinition
  44710. autoload :TableDefinition
  44711. autoload :Table
  44712. end
  44713. autoload_at 'active_record/connection_adapters/abstract/connection_pool' do
  44714. autoload :ConnectionHandler
  44715. autoload :ConnectionManagement
  44716. end
  44717. autoload_under 'abstract' do
  44718. autoload :SchemaStatements
  44719. autoload :DatabaseStatements
  44720. autoload :DatabaseLimits
  44721. autoload :Quoting
  44722. autoload :ConnectionPool
  44723. autoload :QueryCache
  44724. end
  44725. autoload_at 'active_record/connection_adapters/abstract/transaction' do
  44726. autoload :ClosedTransaction
  44727. autoload :RealTransaction
  44728. autoload :SavepointTransaction
  44729. end
  44730. # Active Record supports multiple database systems. AbstractAdapter and
  44731. # related classes form the abstraction layer which makes this possible.
  44732. # An AbstractAdapter represents a connection to a database, and provides an
  44733. # abstract interface for database-specific functionality such as establishing
  44734. # a connection, escaping values, building the right SQL fragments for ':offset'
  44735. # and ':limit' options, etc.
  44736. #
  44737. # All the concrete database adapters follow the interface laid down in this class.
  44738. # ActiveRecord::Base.connection returns an AbstractAdapter object, which
  44739. # you can use.
  44740. #
  44741. # Most of the methods in the adapter are useful during migrations. Most
  44742. # notably, the instance methods provided by SchemaStatement are very useful.
  44743. class AbstractAdapter
  44744. include Quoting, DatabaseStatements, SchemaStatements
  44745. include DatabaseLimits
  44746. include QueryCache
  44747. include ActiveSupport::Callbacks
  44748. include MonitorMixin
  44749. include ColumnDumper
  44750. define_callbacks :checkout, :checkin
  44751. attr_accessor :visitor, :pool
  44752. attr_reader :schema_cache, :last_use, :in_use, :logger
  44753. alias :in_use? :in_use
  44754. def initialize(connection, logger = nil, pool = nil) #:nodoc:
  44755. super()
  44756. @connection = connection
  44757. @in_use = false
  44758. @instrumenter = ActiveSupport::Notifications.instrumenter
  44759. @last_use = false
  44760. @logger = logger
  44761. @pool = pool
  44762. @query_cache = Hash.new { |h,sql| h[sql] = {} }
  44763. @query_cache_enabled = false
  44764. @schema_cache = SchemaCache.new self
  44765. @visitor = nil
  44766. end
  44767. def lease
  44768. synchronize do
  44769. unless in_use
  44770. @in_use = true
  44771. @last_use = Time.now
  44772. end
  44773. end
  44774. end
  44775. def schema_cache=(cache)
  44776. cache.connection = self
  44777. @schema_cache = cache
  44778. end
  44779. def expire
  44780. @in_use = false
  44781. end
  44782. # Returns the human-readable name of the adapter. Use mixed case - one
  44783. # can always use downcase if needed.
  44784. def adapter_name
  44785. 'Abstract'
  44786. end
  44787. # Does this adapter support migrations? Backend specific, as the
  44788. # abstract adapter always returns +false+.
  44789. def supports_migrations?
  44790. false
  44791. end
  44792. # Can this adapter determine the primary key for tables not attached
  44793. # to an Active Record class, such as join tables? Backend specific, as
  44794. # the abstract adapter always returns +false+.
  44795. def supports_primary_key?
  44796. false
  44797. end
  44798. # Does this adapter support using DISTINCT within COUNT? This is +true+
  44799. # for all adapters except sqlite.
  44800. def supports_count_distinct?
  44801. true
  44802. end
  44803. # Does this adapter support DDL rollbacks in transactions? That is, would
  44804. # CREATE TABLE or ALTER TABLE get rolled back by a transaction? PostgreSQL,
  44805. # SQL Server, and others support this. MySQL and others do not.
  44806. def supports_ddl_transactions?
  44807. false
  44808. end
  44809. def supports_bulk_alter?
  44810. false
  44811. end
  44812. # Does this adapter support savepoints? PostgreSQL and MySQL do,
  44813. # SQLite < 3.6.8 does not.
  44814. def supports_savepoints?
  44815. false
  44816. end
  44817. # Should primary key values be selected from their corresponding
  44818. # sequence before the insert statement? If true, next_sequence_value
  44819. # is called before each insert to set the record's primary key.
  44820. # This is false for all adapters but Firebird.
  44821. def prefetch_primary_key?(table_name = nil)
  44822. false
  44823. end
  44824. # Does this adapter support index sort order?
  44825. def supports_index_sort_order?
  44826. false
  44827. end
  44828. # Does this adapter support partial indices?
  44829. def supports_partial_index?
  44830. false
  44831. end
  44832. # Does this adapter support explain? As of this writing sqlite3,
  44833. # mysql2, and postgresql are the only ones that do.
  44834. def supports_explain?
  44835. false
  44836. end
  44837. # Does this adapter support setting the isolation level for a transaction?
  44838. def supports_transaction_isolation?
  44839. false
  44840. end
  44841. # Does this adapter support database extensions? As of this writing only
  44842. # postgresql does.
  44843. def supports_extensions?
  44844. false
  44845. end
  44846. # A list of extensions, to be filled in by adapters that support them. At
  44847. # the moment only postgresql does.
  44848. def extensions
  44849. []
  44850. end
  44851. # QUOTING ==================================================
  44852. # Returns a bind substitution value given a +column+ and list of current
  44853. # +binds+.
  44854. def substitute_at(column, index)
  44855. Arel::Nodes::BindParam.new '?'
  44856. end
  44857. # REFERENTIAL INTEGRITY ====================================
  44858. # Override to turn off referential integrity while executing <tt>&block</tt>.
  44859. def disable_referential_integrity
  44860. yield
  44861. end
  44862. # CONNECTION MANAGEMENT ====================================
  44863. # Checks whether the connection to the database is still active. This includes
  44864. # checking whether the database is actually capable of responding, i.e. whether
  44865. # the connection isn't stale.
  44866. def active?
  44867. end
  44868. # Disconnects from the database if already connected, and establishes a
  44869. # new connection with the database. Implementors should call super if they
  44870. # override the default implementation.
  44871. def reconnect!
  44872. clear_cache!
  44873. reset_transaction
  44874. end
  44875. # Disconnects from the database if already connected. Otherwise, this
  44876. # method does nothing.
  44877. def disconnect!
  44878. clear_cache!
  44879. reset_transaction
  44880. end
  44881. # Reset the state of this connection, directing the DBMS to clear
  44882. # transactions and other connection-related server-side state. Usually a
  44883. # database-dependent operation.
  44884. #
  44885. # The default implementation does nothing; the implementation should be
  44886. # overridden by concrete adapters.
  44887. def reset!
  44888. # this should be overridden by concrete adapters
  44889. end
  44890. ###
  44891. # Clear any caching the database adapter may be doing, for example
  44892. # clearing the prepared statement cache. This is database specific.
  44893. def clear_cache!
  44894. # this should be overridden by concrete adapters
  44895. end
  44896. # Returns true if its required to reload the connection between requests for development mode.
  44897. # This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
  44898. def requires_reloading?
  44899. false
  44900. end
  44901. # Checks whether the connection to the database is still active (i.e. not stale).
  44902. # This is done under the hood by calling <tt>active?</tt>. If the connection
  44903. # is no longer active, then this method will reconnect to the database.
  44904. def verify!(*ignored)
  44905. reconnect! unless active?
  44906. end
  44907. # Provides access to the underlying database driver for this adapter. For
  44908. # example, this method returns a Mysql object in case of MysqlAdapter,
  44909. # and a PGconn object in case of PostgreSQLAdapter.
  44910. #
  44911. # This is useful for when you need to call a proprietary method such as
  44912. # PostgreSQL's lo_* methods.
  44913. def raw_connection
  44914. @connection
  44915. end
  44916. def open_transactions
  44917. @transaction.number
  44918. end
  44919. def increment_open_transactions
  44920. ActiveSupport::Deprecation.warn "#increment_open_transactions is deprecated and has no effect"
  44921. end
  44922. def decrement_open_transactions
  44923. ActiveSupport::Deprecation.warn "#decrement_open_transactions is deprecated and has no effect"
  44924. end
  44925. def transaction_joinable=(joinable)
  44926. message = "#transaction_joinable= is deprecated. Please pass the :joinable option to #begin_transaction instead."
  44927. ActiveSupport::Deprecation.warn message
  44928. @transaction.joinable = joinable
  44929. end
  44930. def create_savepoint
  44931. end
  44932. def rollback_to_savepoint
  44933. end
  44934. def release_savepoint
  44935. end
  44936. def case_sensitive_modifier(node)
  44937. node
  44938. end
  44939. def case_insensitive_comparison(table, attribute, column, value)
  44940. table[attribute].lower.eq(table.lower(value))
  44941. end
  44942. def current_savepoint_name
  44943. "active_record_#{open_transactions}"
  44944. end
  44945. # Check the connection back in to the connection pool
  44946. def close
  44947. pool.checkin self
  44948. end
  44949. protected
  44950. def log(sql, name = "SQL", binds = [])
  44951. @instrumenter.instrument(
  44952. "sql.active_record",
  44953. :sql => sql,
  44954. :name => name,
  44955. :connection_id => object_id,
  44956. :binds => binds) { yield }
  44957. rescue => e
  44958. message = "#{e.class.name}: #{e.message}: #{sql}"
  44959. @logger.error message if @logger
  44960. exception = translate_exception(e, message)
  44961. exception.set_backtrace e.backtrace
  44962. raise exception
  44963. end
  44964. def translate_exception(exception, message)
  44965. # override in derived class
  44966. ActiveRecord::StatementInvalid.new(message)
  44967. end
  44968. end
  44969. end
  44970. end
  44971. require 'arel/visitors/bind_visitor'
  44972. module ActiveRecord
  44973. module ConnectionAdapters
  44974. class AbstractMysqlAdapter < AbstractAdapter
  44975. class Column < ConnectionAdapters::Column # :nodoc:
  44976. attr_reader :collation, :strict
  44977. def initialize(name, default, sql_type = nil, null = true, collation = nil, strict = false)
  44978. @strict = strict
  44979. @collation = collation
  44980. super(name, default, sql_type, null)
  44981. end
  44982. def extract_default(default)
  44983. if blob_or_text_column?
  44984. if default.blank?
  44985. null || strict ? nil : ''
  44986. else
  44987. raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
  44988. end
  44989. elsif missing_default_forged_as_empty_string?(default)
  44990. nil
  44991. else
  44992. super
  44993. end
  44994. end
  44995. def has_default?
  44996. return false if blob_or_text_column? #mysql forbids defaults on blob and text columns
  44997. super
  44998. end
  44999. def blob_or_text_column?
  45000. sql_type =~ /blob/i || type == :text
  45001. end
  45002. # Must return the relevant concrete adapter
  45003. def adapter
  45004. raise NotImplementedError
  45005. end
  45006. def case_sensitive?
  45007. collation && !collation.match(/_ci$/)
  45008. end
  45009. private
  45010. def simplified_type(field_type)
  45011. return :boolean if adapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
  45012. case field_type
  45013. when /enum/i, /set/i then :string
  45014. when /year/i then :integer
  45015. when /bit/i then :binary
  45016. else
  45017. super
  45018. end
  45019. end
  45020. def extract_limit(sql_type)
  45021. case sql_type
  45022. when /blob|text/i
  45023. case sql_type
  45024. when /tiny/i
  45025. 255
  45026. when /medium/i
  45027. 16777215
  45028. when /long/i
  45029. 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
  45030. else
  45031. super # we could return 65535 here, but we leave it undecorated by default
  45032. end
  45033. when /^bigint/i; 8
  45034. when /^int/i; 4
  45035. when /^mediumint/i; 3
  45036. when /^smallint/i; 2
  45037. when /^tinyint/i; 1
  45038. when /^enum\((.+)\)/i
  45039. $1.split(',').map{|enum| enum.strip.length - 2}.max
  45040. else
  45041. super
  45042. end
  45043. end
  45044. # MySQL misreports NOT NULL column default when none is given.
  45045. # We can't detect this for columns which may have a legitimate ''
  45046. # default (string) but we can for others (integer, datetime, boolean,
  45047. # and the rest).
  45048. #
  45049. # Test whether the column has default '', is not null, and is not
  45050. # a type allowing default ''.
  45051. def missing_default_forged_as_empty_string?(default)
  45052. type != :string && !null && default == ''
  45053. end
  45054. end
  45055. ##
  45056. # :singleton-method:
  45057. # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
  45058. # as boolean. If you wish to disable this emulation (which was the default
  45059. # behavior in versions 0.13.1 and earlier) you can add the following line
  45060. # to your application.rb file:
  45061. #
  45062. # ActiveRecord::ConnectionAdapters::Mysql[2]Adapter.emulate_booleans = false
  45063. class_attribute :emulate_booleans
  45064. self.emulate_booleans = true
  45065. LOST_CONNECTION_ERROR_MESSAGES = [
  45066. "Server shutdown in progress",
  45067. "Broken pipe",
  45068. "Lost connection to MySQL server during query",
  45069. "MySQL server has gone away" ]
  45070. QUOTED_TRUE, QUOTED_FALSE = '1', '0'
  45071. NATIVE_DATABASE_TYPES = {
  45072. :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
  45073. :string => { :name => "varchar", :limit => 255 },
  45074. :text => { :name => "text" },
  45075. :integer => { :name => "int", :limit => 4 },
  45076. :float => { :name => "float" },
  45077. :decimal => { :name => "decimal" },
  45078. :datetime => { :name => "datetime" },
  45079. :timestamp => { :name => "datetime" },
  45080. :time => { :name => "time" },
  45081. :date => { :name => "date" },
  45082. :binary => { :name => "blob" },
  45083. :boolean => { :name => "tinyint", :limit => 1 }
  45084. }
  45085. class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
  45086. include Arel::Visitors::BindVisitor
  45087. end
  45088. # FIXME: Make the first parameter more similar for the two adapters
  45089. def initialize(connection, logger, connection_options, config)
  45090. super(connection, logger)
  45091. @connection_options, @config = connection_options, config
  45092. @quoted_column_names, @quoted_table_names = {}, {}
  45093. if config.fetch(:prepared_statements) { true }
  45094. @visitor = Arel::Visitors::MySQL.new self
  45095. else
  45096. @visitor = BindSubstitution.new self
  45097. end
  45098. end
  45099. def adapter_name #:nodoc:
  45100. self.class::ADAPTER_NAME
  45101. end
  45102. # Returns true, since this connection adapter supports migrations.
  45103. def supports_migrations?
  45104. true
  45105. end
  45106. def supports_primary_key?
  45107. true
  45108. end
  45109. # Returns true, since this connection adapter supports savepoints.
  45110. def supports_savepoints?
  45111. true
  45112. end
  45113. def supports_bulk_alter? #:nodoc:
  45114. true
  45115. end
  45116. # Technically MySQL allows to create indexes with the sort order syntax
  45117. # but at the moment (5.5) it doesn't yet implement them
  45118. def supports_index_sort_order?
  45119. true
  45120. end
  45121. # MySQL 4 technically support transaction isolation, but it is affected by a bug
  45122. # where the transaction level gets persisted for the whole session:
  45123. #
  45124. # http://bugs.mysql.com/bug.php?id=39170
  45125. def supports_transaction_isolation?
  45126. version[0] >= 5
  45127. end
  45128. def native_database_types
  45129. NATIVE_DATABASE_TYPES
  45130. end
  45131. # HELPER METHODS ===========================================
  45132. # The two drivers have slightly different ways of yielding hashes of results, so
  45133. # this method must be implemented to provide a uniform interface.
  45134. def each_hash(result) # :nodoc:
  45135. raise NotImplementedError
  45136. end
  45137. # Overridden by the adapters to instantiate their specific Column type.
  45138. def new_column(field, default, type, null, collation) # :nodoc:
  45139. Column.new(field, default, type, null, collation)
  45140. end
  45141. # Must return the Mysql error number from the exception, if the exception has an
  45142. # error number.
  45143. def error_number(exception) # :nodoc:
  45144. raise NotImplementedError
  45145. end
  45146. # QUOTING ==================================================
  45147. def quote(value, column = nil)
  45148. if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
  45149. s = column.class.string_to_binary(value).unpack("H*")[0]
  45150. "x'#{s}'"
  45151. else
  45152. super
  45153. end
  45154. end
  45155. def quote_column_name(name) #:nodoc:
  45156. @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
  45157. end
  45158. def quote_table_name(name) #:nodoc:
  45159. @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
  45160. end
  45161. def quoted_true
  45162. QUOTED_TRUE
  45163. end
  45164. def quoted_false
  45165. QUOTED_FALSE
  45166. end
  45167. # REFERENTIAL INTEGRITY ====================================
  45168. def disable_referential_integrity(&block) #:nodoc:
  45169. old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
  45170. begin
  45171. update("SET FOREIGN_KEY_CHECKS = 0")
  45172. yield
  45173. ensure
  45174. update("SET FOREIGN_KEY_CHECKS = #{old}")
  45175. end
  45176. end
  45177. # DATABASE STATEMENTS ======================================
  45178. # Executes the SQL statement in the context of this connection.
  45179. def execute(sql, name = nil)
  45180. if name == :skip_logging
  45181. @connection.query(sql)
  45182. else
  45183. log(sql, name) { @connection.query(sql) }
  45184. end
  45185. rescue ActiveRecord::StatementInvalid => exception
  45186. if exception.message.split(":").first =~ /Packets out of order/
  45187. raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
  45188. else
  45189. raise
  45190. end
  45191. end
  45192. # MysqlAdapter has to free a result after using it, so we use this method to write
  45193. # stuff in an abstract way without concerning ourselves about whether it needs to be
  45194. # explicitly freed or not.
  45195. def execute_and_free(sql, name = nil) #:nodoc:
  45196. yield execute(sql, name)
  45197. end
  45198. def update_sql(sql, name = nil) #:nodoc:
  45199. super
  45200. @connection.affected_rows
  45201. end
  45202. def begin_db_transaction
  45203. execute "BEGIN"
  45204. rescue
  45205. # Transactions aren't supported
  45206. end
  45207. def begin_isolated_db_transaction(isolation)
  45208. execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
  45209. begin_db_transaction
  45210. rescue
  45211. # Transactions aren't supported
  45212. end
  45213. def commit_db_transaction #:nodoc:
  45214. execute "COMMIT"
  45215. rescue
  45216. # Transactions aren't supported
  45217. end
  45218. def rollback_db_transaction #:nodoc:
  45219. execute "ROLLBACK"
  45220. rescue
  45221. # Transactions aren't supported
  45222. end
  45223. def create_savepoint
  45224. execute("SAVEPOINT #{current_savepoint_name}")
  45225. end
  45226. def rollback_to_savepoint
  45227. execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
  45228. end
  45229. def release_savepoint
  45230. execute("RELEASE SAVEPOINT #{current_savepoint_name}")
  45231. end
  45232. # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
  45233. # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
  45234. # these, we must use a subquery.
  45235. def join_to_update(update, select) #:nodoc:
  45236. if select.limit || select.offset || select.orders.any?
  45237. super
  45238. else
  45239. update.table select.source
  45240. update.wheres = select.constraints
  45241. end
  45242. end
  45243. def empty_insert_statement_value
  45244. "VALUES ()"
  45245. end
  45246. # SCHEMA STATEMENTS ========================================
  45247. def structure_dump #:nodoc:
  45248. if supports_views?
  45249. sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
  45250. else
  45251. sql = "SHOW TABLES"
  45252. end
  45253. select_all(sql, 'SCHEMA').map { |table|
  45254. table.delete('Table_type')
  45255. sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
  45256. exec_query(sql, 'SCHEMA').first['Create Table'] + ";\n\n"
  45257. }.join
  45258. end
  45259. # Drops the database specified on the +name+ attribute
  45260. # and creates it again using the provided +options+.
  45261. def recreate_database(name, options = {})
  45262. drop_database(name)
  45263. create_database(name, options)
  45264. end
  45265. # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
  45266. # Charset defaults to utf8.
  45267. #
  45268. # Example:
  45269. # create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
  45270. # create_database 'matt_development'
  45271. # create_database 'matt_development', charset: :big5
  45272. def create_database(name, options = {})
  45273. if options[:collation]
  45274. execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
  45275. else
  45276. execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
  45277. end
  45278. end
  45279. # Drops a MySQL database.
  45280. #
  45281. # Example:
  45282. # drop_database('sebastian_development')
  45283. def drop_database(name) #:nodoc:
  45284. execute "DROP DATABASE IF EXISTS `#{name}`"
  45285. end
  45286. def current_database
  45287. select_value 'SELECT DATABASE() as db'
  45288. end
  45289. # Returns the database character set.
  45290. def charset
  45291. show_variable 'character_set_database'
  45292. end
  45293. # Returns the database collation strategy.
  45294. def collation
  45295. show_variable 'collation_database'
  45296. end
  45297. def tables(name = nil, database = nil, like = nil) #:nodoc:
  45298. sql = "SHOW TABLES "
  45299. sql << "IN #{quote_table_name(database)} " if database
  45300. sql << "LIKE #{quote(like)}" if like
  45301. execute_and_free(sql, 'SCHEMA') do |result|
  45302. result.collect { |field| field.first }
  45303. end
  45304. end
  45305. def table_exists?(name)
  45306. return false unless name
  45307. return true if tables(nil, nil, name).any?
  45308. name = name.to_s
  45309. schema, table = name.split('.', 2)
  45310. unless table # A table was provided without a schema
  45311. table = schema
  45312. schema = nil
  45313. end
  45314. tables(nil, schema, table).any?
  45315. end
  45316. # Returns an array of indexes for the given table.
  45317. def indexes(table_name, name = nil) #:nodoc:
  45318. indexes = []
  45319. current_index = nil
  45320. execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
  45321. each_hash(result) do |row|
  45322. if current_index != row[:Key_name]
  45323. next if row[:Key_name] == 'PRIMARY' # skip the primary key
  45324. current_index = row[:Key_name]
  45325. indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [])
  45326. end
  45327. indexes.last.columns << row[:Column_name]
  45328. indexes.last.lengths << row[:Sub_part]
  45329. end
  45330. end
  45331. indexes
  45332. end
  45333. # Returns an array of +Column+ objects for the table specified by +table_name+.
  45334. def columns(table_name)#:nodoc:
  45335. sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
  45336. execute_and_free(sql, 'SCHEMA') do |result|
  45337. each_hash(result).map do |field|
  45338. new_column(field[:Field], field[:Default], field[:Type], field[:Null] == "YES", field[:Collation])
  45339. end
  45340. end
  45341. end
  45342. def create_table(table_name, options = {}) #:nodoc:
  45343. super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
  45344. end
  45345. def bulk_change_table(table_name, operations) #:nodoc:
  45346. sqls = operations.map do |command, args|
  45347. table, arguments = args.shift, args
  45348. method = :"#{command}_sql"
  45349. if respond_to?(method, true)
  45350. send(method, table, *arguments)
  45351. else
  45352. raise "Unknown method called : #{method}(#{arguments.inspect})"
  45353. end
  45354. end.flatten.join(", ")
  45355. execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
  45356. end
  45357. # Renames a table.
  45358. #
  45359. # Example:
  45360. # rename_table('octopuses', 'octopi')
  45361. def rename_table(table_name, new_name)
  45362. execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
  45363. end
  45364. def add_column(table_name, column_name, type, options = {})
  45365. execute("ALTER TABLE #{quote_table_name(table_name)} #{add_column_sql(table_name, column_name, type, options)}")
  45366. end
  45367. def change_column_default(table_name, column_name, default)
  45368. column = column_for(table_name, column_name)
  45369. change_column table_name, column_name, column.sql_type, :default => default
  45370. end
  45371. def change_column_null(table_name, column_name, null, default = nil)
  45372. column = column_for(table_name, column_name)
  45373. unless null || default.nil?
  45374. execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  45375. end
  45376. change_column table_name, column_name, column.sql_type, :null => null
  45377. end
  45378. def change_column(table_name, column_name, type, options = {}) #:nodoc:
  45379. execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
  45380. end
  45381. def rename_column(table_name, column_name, new_column_name) #:nodoc:
  45382. execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
  45383. end
  45384. # Maps logical Rails types to MySQL-specific data types.
  45385. def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  45386. case type.to_s
  45387. when 'binary'
  45388. case limit
  45389. when 0..0xfff; "varbinary(#{limit})"
  45390. when nil; "blob"
  45391. when 0x1000..0xffffffff; "blob(#{limit})"
  45392. else raise(ActiveRecordError, "No binary type has character length #{limit}")
  45393. end
  45394. when 'integer'
  45395. case limit
  45396. when 1; 'tinyint'
  45397. when 2; 'smallint'
  45398. when 3; 'mediumint'
  45399. when nil, 4, 11; 'int(11)' # compatibility with MySQL default
  45400. when 5..8; 'bigint'
  45401. else raise(ActiveRecordError, "No integer type has byte size #{limit}")
  45402. end
  45403. when 'text'
  45404. case limit
  45405. when 0..0xff; 'tinytext'
  45406. when nil, 0x100..0xffff; 'text'
  45407. when 0x10000..0xffffff; 'mediumtext'
  45408. when 0x1000000..0xffffffff; 'longtext'
  45409. else raise(ActiveRecordError, "No text type has character length #{limit}")
  45410. end
  45411. else
  45412. super
  45413. end
  45414. end
  45415. def add_column_position!(sql, options)
  45416. if options[:first]
  45417. sql << " FIRST"
  45418. elsif options[:after]
  45419. sql << " AFTER #{quote_column_name(options[:after])}"
  45420. end
  45421. end
  45422. # SHOW VARIABLES LIKE 'name'
  45423. def show_variable(name)
  45424. variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
  45425. variables.first['Value'] unless variables.empty?
  45426. end
  45427. # Returns a table's primary key and belonging sequence.
  45428. def pk_and_sequence_for(table)
  45429. execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
  45430. create_table = each_hash(result).first[:"Create Table"]
  45431. if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
  45432. keys = $1.split(",").map { |key| key.delete('`"') }
  45433. keys.length == 1 ? [keys.first, nil] : nil
  45434. else
  45435. nil
  45436. end
  45437. end
  45438. end
  45439. # Returns just a table's primary key
  45440. def primary_key(table)
  45441. pk_and_sequence = pk_and_sequence_for(table)
  45442. pk_and_sequence && pk_and_sequence.first
  45443. end
  45444. def case_sensitive_modifier(node)
  45445. Arel::Nodes::Bin.new(node)
  45446. end
  45447. def case_insensitive_comparison(table, attribute, column, value)
  45448. if column.case_sensitive?
  45449. super
  45450. else
  45451. table[attribute].eq(value)
  45452. end
  45453. end
  45454. def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  45455. where_sql
  45456. end
  45457. def strict_mode?
  45458. @config.fetch(:strict, true)
  45459. end
  45460. protected
  45461. # MySQL is too stupid to create a temporary table for use subquery, so we have
  45462. # to give it some prompting in the form of a subsubquery. Ugh!
  45463. def subquery_for(key, select)
  45464. subsubselect = select.clone
  45465. subsubselect.projections = [key]
  45466. subselect = Arel::SelectManager.new(select.engine)
  45467. subselect.project Arel.sql(key.name)
  45468. subselect.from subsubselect.as('__active_record_temp')
  45469. end
  45470. def add_index_length(option_strings, column_names, options = {})
  45471. if options.is_a?(Hash) && length = options[:length]
  45472. case length
  45473. when Hash
  45474. column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
  45475. when Fixnum
  45476. column_names.each {|name| option_strings[name] += "(#{length})"}
  45477. end
  45478. end
  45479. return option_strings
  45480. end
  45481. def quoted_columns_for_index(column_names, options = {})
  45482. option_strings = Hash[column_names.map {|name| [name, '']}]
  45483. # add index length
  45484. option_strings = add_index_length(option_strings, column_names, options)
  45485. # add index sort order
  45486. option_strings = add_index_sort_order(option_strings, column_names, options)
  45487. column_names.map {|name| quote_column_name(name) + option_strings[name]}
  45488. end
  45489. def translate_exception(exception, message)
  45490. case error_number(exception)
  45491. when 1062
  45492. RecordNotUnique.new(message, exception)
  45493. when 1452
  45494. InvalidForeignKey.new(message, exception)
  45495. else
  45496. super
  45497. end
  45498. end
  45499. def add_column_sql(table_name, column_name, type, options = {})
  45500. add_column_sql = "ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  45501. add_column_options!(add_column_sql, options)
  45502. add_column_position!(add_column_sql, options)
  45503. add_column_sql
  45504. end
  45505. def change_column_sql(table_name, column_name, type, options = {})
  45506. column = column_for(table_name, column_name)
  45507. unless options_include_default?(options)
  45508. options[:default] = column.default
  45509. end
  45510. unless options.has_key?(:null)
  45511. options[:null] = column.null
  45512. end
  45513. change_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  45514. add_column_options!(change_column_sql, options)
  45515. add_column_position!(change_column_sql, options)
  45516. change_column_sql
  45517. end
  45518. def rename_column_sql(table_name, column_name, new_column_name)
  45519. options = {}
  45520. if column = columns(table_name).find { |c| c.name == column_name.to_s }
  45521. options[:default] = column.default
  45522. options[:null] = column.null
  45523. else
  45524. raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
  45525. end
  45526. current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
  45527. rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
  45528. add_column_options!(rename_column_sql, options)
  45529. rename_column_sql
  45530. end
  45531. def remove_column_sql(table_name, column_name, type = nil, options = {})
  45532. "DROP #{quote_column_name(column_name)}"
  45533. end
  45534. def remove_columns_sql(table_name, *column_names)
  45535. column_names.map {|column_name| remove_column_sql(table_name, column_name) }
  45536. end
  45537. def add_index_sql(table_name, column_name, options = {})
  45538. index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
  45539. "ADD #{index_type} INDEX #{index_name} (#{index_columns})"
  45540. end
  45541. def remove_index_sql(table_name, options = {})
  45542. index_name = index_name_for_remove(table_name, options)
  45543. "DROP INDEX #{index_name}"
  45544. end
  45545. def add_timestamps_sql(table_name)
  45546. [add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
  45547. end
  45548. def remove_timestamps_sql(table_name)
  45549. [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
  45550. end
  45551. private
  45552. def supports_views?
  45553. version[0] >= 5
  45554. end
  45555. def column_for(table_name, column_name)
  45556. unless column = columns(table_name).find { |c| c.name == column_name.to_s }
  45557. raise "No such column: #{table_name}.#{column_name}"
  45558. end
  45559. column
  45560. end
  45561. def configure_connection
  45562. variables = @config[:variables] || {}
  45563. # By default, MySQL 'where id is null' selects the last inserted id.
  45564. # Turn this off. http://dev.rubyonrails.org/ticket/6778
  45565. variables[:sql_auto_is_null] = 0
  45566. # Increase timeout so the server doesn't disconnect us.
  45567. wait_timeout = @config[:wait_timeout]
  45568. wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
  45569. variables[:wait_timeout] = wait_timeout
  45570. # Make MySQL reject illegal values rather than truncating or blanking them, see
  45571. # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
  45572. # If the user has provided another value for sql_mode, don't replace it.
  45573. if strict_mode? && !variables.has_key?(:sql_mode)
  45574. variables[:sql_mode] = 'STRICT_ALL_TABLES'
  45575. end
  45576. # NAMES does not have an equals sign, see
  45577. # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
  45578. # (trailing comma because variable_assignments will always have content)
  45579. encoding = "NAMES #{@config[:encoding]}, " if @config[:encoding]
  45580. # Gather up all of the SET variables...
  45581. variable_assignments = variables.map do |k, v|
  45582. if v == ':default' || v == :default
  45583. "@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
  45584. elsif !v.nil?
  45585. "@@SESSION.#{k.to_s} = #{quote(v)}"
  45586. end
  45587. # or else nil; compact to clear nils out
  45588. end.compact.join(', ')
  45589. # ...and send them all in one query
  45590. execute("SET #{encoding} #{variable_assignments}", :skip_logging)
  45591. end
  45592. end
  45593. end
  45594. end
  45595. require 'set'
  45596. module ActiveRecord
  45597. # :stopdoc:
  45598. module ConnectionAdapters
  45599. # An abstract definition of a column in a table.
  45600. class Column
  45601. TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
  45602. FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
  45603. module Format
  45604. ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
  45605. ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
  45606. end
  45607. attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
  45608. attr_accessor :primary, :coder
  45609. alias :encoded? :coder
  45610. # Instantiates a new column in the table.
  45611. #
  45612. # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
  45613. # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
  45614. # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in
  45615. # <tt>company_name varchar(60)</tt>.
  45616. # It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
  45617. # +null+ determines if this column allows +NULL+ values.
  45618. def initialize(name, default, sql_type = nil, null = true)
  45619. @name = name
  45620. @sql_type = sql_type
  45621. @null = null
  45622. @limit = extract_limit(sql_type)
  45623. @precision = extract_precision(sql_type)
  45624. @scale = extract_scale(sql_type)
  45625. @type = simplified_type(sql_type)
  45626. @default = extract_default(default)
  45627. @primary = nil
  45628. @coder = nil
  45629. end
  45630. # Returns +true+ if the column is either of type string or text.
  45631. def text?
  45632. type == :string || type == :text
  45633. end
  45634. # Returns +true+ if the column is either of type integer, float or decimal.
  45635. def number?
  45636. type == :integer || type == :float || type == :decimal
  45637. end
  45638. def has_default?
  45639. !default.nil?
  45640. end
  45641. # Returns the Ruby class that corresponds to the abstract data type.
  45642. def klass
  45643. case type
  45644. when :integer then Fixnum
  45645. when :float then Float
  45646. when :decimal then BigDecimal
  45647. when :datetime, :timestamp, :time then Time
  45648. when :date then Date
  45649. when :text, :string, :binary then String
  45650. when :boolean then Object
  45651. end
  45652. end
  45653. def binary?
  45654. type == :binary
  45655. end
  45656. # Casts a Ruby value to something appropriate for writing to the database.
  45657. def type_cast_for_write(value)
  45658. return value unless number?
  45659. case value
  45660. when FalseClass
  45661. 0
  45662. when TrueClass
  45663. 1
  45664. when String
  45665. value.presence
  45666. else
  45667. value
  45668. end
  45669. end
  45670. # Casts value (which is a String) to an appropriate instance.
  45671. def type_cast(value)
  45672. return nil if value.nil?
  45673. return coder.load(value) if encoded?
  45674. klass = self.class
  45675. case type
  45676. when :string, :text then value
  45677. when :integer then klass.value_to_integer(value)
  45678. when :float then value.to_f
  45679. when :decimal then klass.value_to_decimal(value)
  45680. when :datetime, :timestamp then klass.string_to_time(value)
  45681. when :time then klass.string_to_dummy_time(value)
  45682. when :date then klass.value_to_date(value)
  45683. when :binary then klass.binary_to_string(value)
  45684. when :boolean then klass.value_to_boolean(value)
  45685. else value
  45686. end
  45687. end
  45688. def type_cast_code(var_name)
  45689. message = "Column#type_cast_code is deprecated in favor of using Column#type_cast only, " \
  45690. "and it is going to be removed in future Rails versions."
  45691. ActiveSupport::Deprecation.warn message
  45692. klass = self.class.name
  45693. case type
  45694. when :string, :text then var_name
  45695. when :integer then "#{klass}.value_to_integer(#{var_name})"
  45696. when :float then "#{var_name}.to_f"
  45697. when :decimal then "#{klass}.value_to_decimal(#{var_name})"
  45698. when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})"
  45699. when :time then "#{klass}.string_to_dummy_time(#{var_name})"
  45700. when :date then "#{klass}.value_to_date(#{var_name})"
  45701. when :binary then "#{klass}.binary_to_string(#{var_name})"
  45702. when :boolean then "#{klass}.value_to_boolean(#{var_name})"
  45703. when :hstore then "#{klass}.string_to_hstore(#{var_name})"
  45704. when :inet, :cidr then "#{klass}.string_to_cidr(#{var_name})"
  45705. when :json then "#{klass}.string_to_json(#{var_name})"
  45706. else var_name
  45707. end
  45708. end
  45709. # Returns the human name of the column name.
  45710. #
  45711. # ===== Examples
  45712. # Column.new('sales_stage', ...).human_name # => 'Sales stage'
  45713. def human_name
  45714. Base.human_attribute_name(@name)
  45715. end
  45716. def extract_default(default)
  45717. type_cast(default)
  45718. end
  45719. # Used to convert from Strings to BLOBs
  45720. def string_to_binary(value)
  45721. self.class.string_to_binary(value)
  45722. end
  45723. class << self
  45724. # Used to convert from Strings to BLOBs
  45725. def string_to_binary(value)
  45726. value
  45727. end
  45728. # Used to convert from BLOBs to Strings
  45729. def binary_to_string(value)
  45730. value
  45731. end
  45732. def value_to_date(value)
  45733. if value.is_a?(String)
  45734. return nil if value.blank?
  45735. fast_string_to_date(value) || fallback_string_to_date(value)
  45736. elsif value.respond_to?(:to_date)
  45737. value.to_date
  45738. else
  45739. value
  45740. end
  45741. end
  45742. def string_to_time(string)
  45743. return string unless string.is_a?(String)
  45744. return nil if string.blank?
  45745. fast_string_to_time(string) || fallback_string_to_time(string)
  45746. end
  45747. def string_to_dummy_time(string)
  45748. return string unless string.is_a?(String)
  45749. return nil if string.blank?
  45750. dummy_time_string = "2000-01-01 #{string}"
  45751. fast_string_to_time(dummy_time_string) || begin
  45752. time_hash = Date._parse(dummy_time_string)
  45753. return nil if time_hash[:hour].nil?
  45754. new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
  45755. end
  45756. end
  45757. # convert something to a boolean
  45758. def value_to_boolean(value)
  45759. if value.is_a?(String) && value.blank?
  45760. nil
  45761. else
  45762. TRUE_VALUES.include?(value)
  45763. end
  45764. end
  45765. # Used to convert values to integer.
  45766. # handle the case when an integer column is used to store boolean values
  45767. def value_to_integer(value)
  45768. case value
  45769. when TrueClass, FalseClass
  45770. value ? 1 : 0
  45771. else
  45772. value.to_i rescue nil
  45773. end
  45774. end
  45775. # convert something to a BigDecimal
  45776. def value_to_decimal(value)
  45777. # Using .class is faster than .is_a? and
  45778. # subclasses of BigDecimal will be handled
  45779. # in the else clause
  45780. if value.class == BigDecimal
  45781. value
  45782. elsif value.respond_to?(:to_d)
  45783. value.to_d
  45784. else
  45785. value.to_s.to_d
  45786. end
  45787. end
  45788. protected
  45789. # '0.123456' -> 123456
  45790. # '1.123456' -> 123456
  45791. def microseconds(time)
  45792. time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
  45793. end
  45794. def new_date(year, mon, mday)
  45795. if year && year != 0
  45796. Date.new(year, mon, mday) rescue nil
  45797. end
  45798. end
  45799. def new_time(year, mon, mday, hour, min, sec, microsec)
  45800. # Treat 0000-00-00 00:00:00 as nil.
  45801. return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
  45802. Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
  45803. end
  45804. def fast_string_to_date(string)
  45805. if string =~ Format::ISO_DATE
  45806. new_date $1.to_i, $2.to_i, $3.to_i
  45807. end
  45808. end
  45809. # Doesn't handle time zones.
  45810. def fast_string_to_time(string)
  45811. if string =~ Format::ISO_DATETIME
  45812. microsec = ($7.to_r * 1_000_000).to_i
  45813. new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
  45814. end
  45815. end
  45816. def fallback_string_to_date(string)
  45817. new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
  45818. end
  45819. def fallback_string_to_time(string)
  45820. time_hash = Date._parse(string)
  45821. time_hash[:sec_fraction] = microseconds(time_hash)
  45822. new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
  45823. end
  45824. end
  45825. private
  45826. def extract_limit(sql_type)
  45827. $1.to_i if sql_type =~ /\((.*)\)/
  45828. end
  45829. def extract_precision(sql_type)
  45830. $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
  45831. end
  45832. def extract_scale(sql_type)
  45833. case sql_type
  45834. when /^(numeric|decimal|number)\((\d+)\)/i then 0
  45835. when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
  45836. end
  45837. end
  45838. def simplified_type(field_type)
  45839. case field_type
  45840. when /int/i
  45841. :integer
  45842. when /float|double/i
  45843. :float
  45844. when /decimal|numeric|number/i
  45845. extract_scale(field_type) == 0 ? :integer : :decimal
  45846. when /datetime/i
  45847. :datetime
  45848. when /timestamp/i
  45849. :timestamp
  45850. when /time/i
  45851. :time
  45852. when /date/i
  45853. :date
  45854. when /clob/i, /text/i
  45855. :text
  45856. when /blob/i, /binary/i
  45857. :binary
  45858. when /char/i, /string/i
  45859. :string
  45860. when /boolean/i
  45861. :boolean
  45862. end
  45863. end
  45864. end
  45865. end
  45866. # :startdoc:
  45867. end
  45868. require 'uri'
  45869. module ActiveRecord
  45870. module ConnectionAdapters
  45871. class ConnectionSpecification #:nodoc:
  45872. attr_reader :config, :adapter_method
  45873. def initialize(config, adapter_method)
  45874. @config, @adapter_method = config, adapter_method
  45875. end
  45876. def initialize_dup(original)
  45877. @config = original.config.dup
  45878. end
  45879. ##
  45880. # Builds a ConnectionSpecification from user input
  45881. class Resolver # :nodoc:
  45882. attr_reader :config, :klass, :configurations
  45883. def initialize(config, configurations)
  45884. @config = config
  45885. @configurations = configurations
  45886. end
  45887. def spec
  45888. case config
  45889. when nil
  45890. raise AdapterNotSpecified unless defined?(Rails.env)
  45891. resolve_string_connection Rails.env
  45892. when Symbol, String
  45893. resolve_string_connection config.to_s
  45894. when Hash
  45895. resolve_hash_connection config
  45896. end
  45897. end
  45898. private
  45899. def resolve_string_connection(spec) # :nodoc:
  45900. hash = configurations.fetch(spec) do |k|
  45901. connection_url_to_hash(k)
  45902. end
  45903. raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
  45904. resolve_hash_connection hash
  45905. end
  45906. def resolve_hash_connection(spec) # :nodoc:
  45907. spec = spec.symbolize_keys
  45908. raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
  45909. begin
  45910. require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
  45911. rescue LoadError => e
  45912. raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace
  45913. end
  45914. adapter_method = "#{spec[:adapter]}_connection"
  45915. ConnectionSpecification.new(spec, adapter_method)
  45916. end
  45917. # For DATABASE_URL, accept a limited concept of ints and floats
  45918. SIMPLE_INT = /\A\d+\z/
  45919. SIMPLE_FLOAT = /\A\d+\.\d+\z/
  45920. def connection_url_to_hash(url) # :nodoc:
  45921. config = URI.parse url
  45922. adapter = config.scheme
  45923. adapter = "postgresql" if adapter == "postgres"
  45924. spec = { :adapter => adapter,
  45925. :username => config.user,
  45926. :password => config.password,
  45927. :port => config.port,
  45928. :database => config.path.sub(%r{^/},""),
  45929. :host => config.host }
  45930. spec.reject!{ |_,value| value.blank? }
  45931. uri_parser = URI::Parser.new
  45932. spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) }
  45933. if config.query
  45934. options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
  45935. options.each { |key, value| options[key] = type_cast_value(value) }
  45936. spec.merge!(options)
  45937. end
  45938. spec
  45939. end
  45940. def type_cast_value(value)
  45941. case value
  45942. when SIMPLE_INT
  45943. value.to_i
  45944. when SIMPLE_FLOAT
  45945. value.to_f
  45946. when 'true'
  45947. true
  45948. when 'false'
  45949. false
  45950. else
  45951. value
  45952. end
  45953. end
  45954. end
  45955. end
  45956. end
  45957. end
  45958. require 'active_record/connection_adapters/abstract_mysql_adapter'
  45959. gem 'mysql2', '~> 0.3.10'
  45960. require 'mysql2'
  45961. module ActiveRecord
  45962. module ConnectionHandling
  45963. # Establishes a connection to the database that's used by all Active Record objects.
  45964. def mysql2_connection(config)
  45965. config = config.symbolize_keys
  45966. config[:username] = 'root' if config[:username].nil?
  45967. if Mysql2::Client.const_defined? :FOUND_ROWS
  45968. config[:flags] = Mysql2::Client::FOUND_ROWS
  45969. end
  45970. client = Mysql2::Client.new(config)
  45971. options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
  45972. ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
  45973. end
  45974. end
  45975. module ConnectionAdapters
  45976. class Mysql2Adapter < AbstractMysqlAdapter
  45977. class Column < AbstractMysqlAdapter::Column # :nodoc:
  45978. def adapter
  45979. Mysql2Adapter
  45980. end
  45981. end
  45982. ADAPTER_NAME = 'Mysql2'
  45983. def initialize(connection, logger, connection_options, config)
  45984. super
  45985. @visitor = BindSubstitution.new self
  45986. configure_connection
  45987. end
  45988. def supports_explain?
  45989. true
  45990. end
  45991. # HELPER METHODS ===========================================
  45992. def each_hash(result) # :nodoc:
  45993. if block_given?
  45994. result.each(:as => :hash, :symbolize_keys => true) do |row|
  45995. yield row
  45996. end
  45997. else
  45998. to_enum(:each_hash, result)
  45999. end
  46000. end
  46001. def new_column(field, default, type, null, collation) # :nodoc:
  46002. Column.new(field, default, type, null, collation, strict_mode?)
  46003. end
  46004. def error_number(exception)
  46005. exception.error_number if exception.respond_to?(:error_number)
  46006. end
  46007. # QUOTING ==================================================
  46008. def quote_string(string)
  46009. @connection.escape(string)
  46010. end
  46011. # CONNECTION MANAGEMENT ====================================
  46012. def active?
  46013. return false unless @connection
  46014. @connection.ping
  46015. end
  46016. def reconnect!
  46017. super
  46018. disconnect!
  46019. connect
  46020. end
  46021. alias :reset! :reconnect!
  46022. # Disconnects from the database if already connected.
  46023. # Otherwise, this method does nothing.
  46024. def disconnect!
  46025. super
  46026. unless @connection.nil?
  46027. @connection.close
  46028. @connection = nil
  46029. end
  46030. end
  46031. # DATABASE STATEMENTS ======================================
  46032. def explain(arel, binds = [])
  46033. sql = "EXPLAIN #{to_sql(arel, binds.dup)}"
  46034. start = Time.now
  46035. result = exec_query(sql, 'EXPLAIN', binds)
  46036. elapsed = Time.now - start
  46037. ExplainPrettyPrinter.new.pp(result, elapsed)
  46038. end
  46039. class ExplainPrettyPrinter # :nodoc:
  46040. # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
  46041. # MySQL shell:
  46042. #
  46043. # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
  46044. # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  46045. # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
  46046. # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
  46047. # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
  46048. # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
  46049. # 2 rows in set (0.00 sec)
  46050. #
  46051. # This is an exercise in Ruby hyperrealism :).
  46052. def pp(result, elapsed)
  46053. widths = compute_column_widths(result)
  46054. separator = build_separator(widths)
  46055. pp = []
  46056. pp << separator
  46057. pp << build_cells(result.columns, widths)
  46058. pp << separator
  46059. result.rows.each do |row|
  46060. pp << build_cells(row, widths)
  46061. end
  46062. pp << separator
  46063. pp << build_footer(result.rows.length, elapsed)
  46064. pp.join("\n") + "\n"
  46065. end
  46066. private
  46067. def compute_column_widths(result)
  46068. [].tap do |widths|
  46069. result.columns.each_with_index do |column, i|
  46070. cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
  46071. widths << cells_in_column.map(&:length).max
  46072. end
  46073. end
  46074. end
  46075. def build_separator(widths)
  46076. padding = 1
  46077. '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
  46078. end
  46079. def build_cells(items, widths)
  46080. cells = []
  46081. items.each_with_index do |item, i|
  46082. item = 'NULL' if item.nil?
  46083. justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
  46084. cells << item.to_s.send(justifier, widths[i])
  46085. end
  46086. '| ' + cells.join(' | ') + ' |'
  46087. end
  46088. def build_footer(nrows, elapsed)
  46089. rows_label = nrows == 1 ? 'row' : 'rows'
  46090. "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
  46091. end
  46092. end
  46093. # FIXME: re-enable the following once a "better" query_cache solution is in core
  46094. #
  46095. # The overrides below perform much better than the originals in AbstractAdapter
  46096. # because we're able to take advantage of mysql2's lazy-loading capabilities
  46097. #
  46098. # # Returns a record hash with the column names as keys and column values
  46099. # # as values.
  46100. # def select_one(sql, name = nil)
  46101. # result = execute(sql, name)
  46102. # result.each(as: :hash) do |r|
  46103. # return r
  46104. # end
  46105. # end
  46106. #
  46107. # # Returns a single value from a record
  46108. # def select_value(sql, name = nil)
  46109. # result = execute(sql, name)
  46110. # if first = result.first
  46111. # first.first
  46112. # end
  46113. # end
  46114. #
  46115. # # Returns an array of the values of the first column in a select:
  46116. # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
  46117. # def select_values(sql, name = nil)
  46118. # execute(sql, name).map { |row| row.first }
  46119. # end
  46120. # Returns an array of arrays containing the field values.
  46121. # Order is the same as that returned by +columns+.
  46122. def select_rows(sql, name = nil)
  46123. execute(sql, name).to_a
  46124. end
  46125. # Executes the SQL statement in the context of this connection.
  46126. def execute(sql, name = nil)
  46127. # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
  46128. # made since we established the connection
  46129. @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
  46130. super
  46131. end
  46132. def exec_query(sql, name = 'SQL', binds = [])
  46133. result = execute(sql, name)
  46134. ActiveRecord::Result.new(result.fields, result.to_a)
  46135. end
  46136. alias exec_without_stmt exec_query
  46137. # Returns an array of record hashes with the column names as keys and
  46138. # column values as values.
  46139. def select(sql, name = nil, binds = [])
  46140. exec_query(sql, name)
  46141. end
  46142. def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
  46143. super
  46144. id_value || @connection.last_id
  46145. end
  46146. alias :create :insert_sql
  46147. def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
  46148. execute to_sql(sql, binds), name
  46149. end
  46150. def exec_delete(sql, name, binds)
  46151. execute to_sql(sql, binds), name
  46152. @connection.affected_rows
  46153. end
  46154. alias :exec_update :exec_delete
  46155. def last_inserted_id(result)
  46156. @connection.last_id
  46157. end
  46158. private
  46159. def connect
  46160. @connection = Mysql2::Client.new(@config)
  46161. configure_connection
  46162. end
  46163. def configure_connection
  46164. @connection.query_options.merge!(:as => :array)
  46165. super
  46166. end
  46167. def version
  46168. @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
  46169. end
  46170. end
  46171. end
  46172. end
  46173. require 'active_record/connection_adapters/abstract_mysql_adapter'
  46174. require 'active_record/connection_adapters/statement_pool'
  46175. require 'active_support/core_ext/hash/keys'
  46176. gem 'mysql', '~> 2.9'
  46177. require 'mysql'
  46178. class Mysql
  46179. class Time
  46180. def to_date
  46181. Date.new(year, month, day)
  46182. end
  46183. end
  46184. class Stmt; include Enumerable end
  46185. class Result; include Enumerable end
  46186. end
  46187. module ActiveRecord
  46188. module ConnectionHandling
  46189. # Establishes a connection to the database that's used by all Active Record objects.
  46190. def mysql_connection(config) # :nodoc:
  46191. config = config.symbolize_keys
  46192. host = config[:host]
  46193. port = config[:port]
  46194. socket = config[:socket]
  46195. username = config[:username] ? config[:username].to_s : 'root'
  46196. password = config[:password].to_s
  46197. database = config[:database]
  46198. mysql = Mysql.init
  46199. mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
  46200. default_flags = Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? Mysql::CLIENT_MULTI_RESULTS : 0
  46201. default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS)
  46202. options = [host, username, password, database, port, socket, default_flags]
  46203. ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
  46204. end
  46205. end
  46206. module ConnectionAdapters
  46207. # The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with
  46208. # the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
  46209. #
  46210. # Options:
  46211. #
  46212. # * <tt>:host</tt> - Defaults to "localhost".
  46213. # * <tt>:port</tt> - Defaults to 3306.
  46214. # * <tt>:socket</tt> - Defaults to "/tmp/mysql.sock".
  46215. # * <tt>:username</tt> - Defaults to "root"
  46216. # * <tt>:password</tt> - Defaults to nothing.
  46217. # * <tt>:database</tt> - The name of the database. No default, must be provided.
  46218. # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
  46219. # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
  46220. # * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html)
  46221. # * <tt>:variables</tt> - (Optional) A hash session variables to send as `SET @@SESSION.key = value` on each database connection. Use the value `:default` to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html).
  46222. # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
  46223. # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
  46224. # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
  46225. # * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
  46226. # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
  46227. #
  46228. class MysqlAdapter < AbstractMysqlAdapter
  46229. class Column < AbstractMysqlAdapter::Column #:nodoc:
  46230. def self.string_to_time(value)
  46231. return super unless Mysql::Time === value
  46232. new_time(
  46233. value.year,
  46234. value.month,
  46235. value.day,
  46236. value.hour,
  46237. value.minute,
  46238. value.second,
  46239. value.second_part)
  46240. end
  46241. def self.string_to_dummy_time(v)
  46242. return super unless Mysql::Time === v
  46243. new_time(2000, 01, 01, v.hour, v.minute, v.second, v.second_part)
  46244. end
  46245. def self.string_to_date(v)
  46246. return super unless Mysql::Time === v
  46247. new_date(v.year, v.month, v.day)
  46248. end
  46249. def adapter
  46250. MysqlAdapter
  46251. end
  46252. end
  46253. ADAPTER_NAME = 'MySQL'
  46254. class StatementPool < ConnectionAdapters::StatementPool
  46255. def initialize(connection, max = 1000)
  46256. super
  46257. @cache = Hash.new { |h,pid| h[pid] = {} }
  46258. end
  46259. def each(&block); cache.each(&block); end
  46260. def key?(key); cache.key?(key); end
  46261. def [](key); cache[key]; end
  46262. def length; cache.length; end
  46263. def delete(key); cache.delete(key); end
  46264. def []=(sql, key)
  46265. while @max <= cache.size
  46266. cache.shift.last[:stmt].close
  46267. end
  46268. cache[sql] = key
  46269. end
  46270. def clear
  46271. cache.values.each do |hash|
  46272. hash[:stmt].close
  46273. end
  46274. cache.clear
  46275. end
  46276. private
  46277. def cache
  46278. @cache[Process.pid]
  46279. end
  46280. end
  46281. def initialize(connection, logger, connection_options, config)
  46282. super
  46283. @statements = StatementPool.new(@connection,
  46284. config.fetch(:statement_limit) { 1000 })
  46285. @client_encoding = nil
  46286. connect
  46287. end
  46288. # Returns true, since this connection adapter supports prepared statement
  46289. # caching.
  46290. def supports_statement_cache?
  46291. true
  46292. end
  46293. # HELPER METHODS ===========================================
  46294. def each_hash(result) # :nodoc:
  46295. if block_given?
  46296. result.each_hash do |row|
  46297. row.symbolize_keys!
  46298. yield row
  46299. end
  46300. else
  46301. to_enum(:each_hash, result)
  46302. end
  46303. end
  46304. def new_column(field, default, type, null, collation) # :nodoc:
  46305. Column.new(field, default, type, null, collation, strict_mode?)
  46306. end
  46307. def error_number(exception) # :nodoc:
  46308. exception.errno if exception.respond_to?(:errno)
  46309. end
  46310. # QUOTING ==================================================
  46311. def type_cast(value, column)
  46312. return super unless value == true || value == false
  46313. value ? 1 : 0
  46314. end
  46315. def quote_string(string) #:nodoc:
  46316. @connection.quote(string)
  46317. end
  46318. # CONNECTION MANAGEMENT ====================================
  46319. def active?
  46320. if @connection.respond_to?(:stat)
  46321. @connection.stat
  46322. else
  46323. @connection.query 'select 1'
  46324. end
  46325. # mysql-ruby doesn't raise an exception when stat fails.
  46326. if @connection.respond_to?(:errno)
  46327. @connection.errno.zero?
  46328. else
  46329. true
  46330. end
  46331. rescue Mysql::Error
  46332. false
  46333. end
  46334. def reconnect!
  46335. super
  46336. disconnect!
  46337. connect
  46338. end
  46339. # Disconnects from the database if already connected. Otherwise, this
  46340. # method does nothing.
  46341. def disconnect!
  46342. super
  46343. @connection.close rescue nil
  46344. end
  46345. def reset!
  46346. if @connection.respond_to?(:change_user)
  46347. # See http://bugs.mysql.com/bug.php?id=33540 -- the workaround way to
  46348. # reset the connection is to change the user to the same user.
  46349. @connection.change_user(@config[:username], @config[:password], @config[:database])
  46350. configure_connection
  46351. end
  46352. end
  46353. # DATABASE STATEMENTS ======================================
  46354. def select_rows(sql, name = nil)
  46355. @connection.query_with_result = true
  46356. rows = exec_query(sql, name).rows
  46357. @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
  46358. rows
  46359. end
  46360. # Clears the prepared statements cache.
  46361. def clear_cache!
  46362. @statements.clear
  46363. end
  46364. # Taken from here:
  46365. # https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
  46366. # Author: TOMITA Masahiro <tommy@tmtm.org>
  46367. ENCODINGS = {
  46368. "armscii8" => nil,
  46369. "ascii" => Encoding::US_ASCII,
  46370. "big5" => Encoding::Big5,
  46371. "binary" => Encoding::ASCII_8BIT,
  46372. "cp1250" => Encoding::Windows_1250,
  46373. "cp1251" => Encoding::Windows_1251,
  46374. "cp1256" => Encoding::Windows_1256,
  46375. "cp1257" => Encoding::Windows_1257,
  46376. "cp850" => Encoding::CP850,
  46377. "cp852" => Encoding::CP852,
  46378. "cp866" => Encoding::IBM866,
  46379. "cp932" => Encoding::Windows_31J,
  46380. "dec8" => nil,
  46381. "eucjpms" => Encoding::EucJP_ms,
  46382. "euckr" => Encoding::EUC_KR,
  46383. "gb2312" => Encoding::EUC_CN,
  46384. "gbk" => Encoding::GBK,
  46385. "geostd8" => nil,
  46386. "greek" => Encoding::ISO_8859_7,
  46387. "hebrew" => Encoding::ISO_8859_8,
  46388. "hp8" => nil,
  46389. "keybcs2" => nil,
  46390. "koi8r" => Encoding::KOI8_R,
  46391. "koi8u" => Encoding::KOI8_U,
  46392. "latin1" => Encoding::ISO_8859_1,
  46393. "latin2" => Encoding::ISO_8859_2,
  46394. "latin5" => Encoding::ISO_8859_9,
  46395. "latin7" => Encoding::ISO_8859_13,
  46396. "macce" => Encoding::MacCentEuro,
  46397. "macroman" => Encoding::MacRoman,
  46398. "sjis" => Encoding::SHIFT_JIS,
  46399. "swe7" => nil,
  46400. "tis620" => Encoding::TIS_620,
  46401. "ucs2" => Encoding::UTF_16BE,
  46402. "ujis" => Encoding::EucJP_ms,
  46403. "utf8" => Encoding::UTF_8,
  46404. "utf8mb4" => Encoding::UTF_8,
  46405. }
  46406. # Get the client encoding for this database
  46407. def client_encoding
  46408. return @client_encoding if @client_encoding
  46409. result = exec_query(
  46410. "SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
  46411. 'SCHEMA')
  46412. @client_encoding = ENCODINGS[result.rows.last.last]
  46413. end
  46414. def exec_query(sql, name = 'SQL', binds = [])
  46415. # If the configuration sets prepared_statements:false, binds will
  46416. # always be empty, since the bind variables will have been already
  46417. # substituted and removed from binds by BindVisitor, so this will
  46418. # effectively disable prepared statement usage completely.
  46419. if binds.empty?
  46420. result_set, affected_rows = exec_without_stmt(sql, name)
  46421. else
  46422. result_set, affected_rows = exec_stmt(sql, name, binds)
  46423. end
  46424. yield affected_rows if block_given?
  46425. result_set
  46426. end
  46427. def last_inserted_id(result)
  46428. @connection.insert_id
  46429. end
  46430. module Fields
  46431. class Type
  46432. def type; end
  46433. def type_cast_for_write(value)
  46434. value
  46435. end
  46436. end
  46437. class Identity < Type
  46438. def type_cast(value); value; end
  46439. end
  46440. class Integer < Type
  46441. def type_cast(value)
  46442. return if value.nil?
  46443. value.to_i rescue value ? 1 : 0
  46444. end
  46445. end
  46446. class Date < Type
  46447. def type; :date; end
  46448. def type_cast(value)
  46449. return if value.nil?
  46450. # FIXME: probably we can improve this since we know it is mysql
  46451. # specific
  46452. ConnectionAdapters::Column.value_to_date value
  46453. end
  46454. end
  46455. class DateTime < Type
  46456. def type; :datetime; end
  46457. def type_cast(value)
  46458. return if value.nil?
  46459. # FIXME: probably we can improve this since we know it is mysql
  46460. # specific
  46461. ConnectionAdapters::Column.string_to_time value
  46462. end
  46463. end
  46464. class Time < Type
  46465. def type; :time; end
  46466. def type_cast(value)
  46467. return if value.nil?
  46468. # FIXME: probably we can improve this since we know it is mysql
  46469. # specific
  46470. ConnectionAdapters::Column.string_to_dummy_time value
  46471. end
  46472. end
  46473. class Float < Type
  46474. def type; :float; end
  46475. def type_cast(value)
  46476. return if value.nil?
  46477. value.to_f
  46478. end
  46479. end
  46480. class Decimal < Type
  46481. def type_cast(value)
  46482. return if value.nil?
  46483. ConnectionAdapters::Column.value_to_decimal value
  46484. end
  46485. end
  46486. class Boolean < Type
  46487. def type_cast(value)
  46488. return if value.nil?
  46489. ConnectionAdapters::Column.value_to_boolean value
  46490. end
  46491. end
  46492. TYPES = {}
  46493. # Register an MySQL +type_id+ with a typcasting object in
  46494. # +type+.
  46495. def self.register_type(type_id, type)
  46496. TYPES[type_id] = type
  46497. end
  46498. def self.alias_type(new, old)
  46499. TYPES[new] = TYPES[old]
  46500. end
  46501. register_type Mysql::Field::TYPE_TINY, Fields::Boolean.new
  46502. register_type Mysql::Field::TYPE_LONG, Fields::Integer.new
  46503. alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
  46504. alias_type Mysql::Field::TYPE_NEWDECIMAL, Mysql::Field::TYPE_LONG
  46505. register_type Mysql::Field::TYPE_VAR_STRING, Fields::Identity.new
  46506. register_type Mysql::Field::TYPE_BLOB, Fields::Identity.new
  46507. register_type Mysql::Field::TYPE_DATE, Fields::Date.new
  46508. register_type Mysql::Field::TYPE_DATETIME, Fields::DateTime.new
  46509. register_type Mysql::Field::TYPE_TIME, Fields::Time.new
  46510. register_type Mysql::Field::TYPE_FLOAT, Fields::Float.new
  46511. Mysql::Field.constants.grep(/TYPE/).map { |class_name|
  46512. Mysql::Field.const_get class_name
  46513. }.reject { |const| TYPES.key? const }.each do |const|
  46514. register_type const, Fields::Identity.new
  46515. end
  46516. end
  46517. def exec_without_stmt(sql, name = 'SQL') # :nodoc:
  46518. # Some queries, like SHOW CREATE TABLE don't work through the prepared
  46519. # statement API. For those queries, we need to use this method. :'(
  46520. log(sql, name) do
  46521. result = @connection.query(sql)
  46522. affected_rows = @connection.affected_rows
  46523. if result
  46524. types = {}
  46525. result.fetch_fields.each { |field|
  46526. if field.decimals > 0
  46527. types[field.name] = Fields::Decimal.new
  46528. else
  46529. types[field.name] = Fields::TYPES.fetch(field.type) {
  46530. Fields::Identity.new
  46531. }
  46532. end
  46533. }
  46534. result_set = ActiveRecord::Result.new(types.keys, result.to_a, types)
  46535. result.free
  46536. else
  46537. result_set = ActiveRecord::Result.new([], [])
  46538. end
  46539. [result_set, affected_rows]
  46540. end
  46541. end
  46542. def execute_and_free(sql, name = nil)
  46543. result = execute(sql, name)
  46544. ret = yield result
  46545. result.free
  46546. ret
  46547. end
  46548. def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
  46549. super sql, name
  46550. id_value || @connection.insert_id
  46551. end
  46552. alias :create :insert_sql
  46553. def exec_delete(sql, name, binds)
  46554. affected_rows = 0
  46555. exec_query(sql, name, binds) do |n|
  46556. affected_rows = n
  46557. end
  46558. affected_rows
  46559. end
  46560. alias :exec_update :exec_delete
  46561. def begin_db_transaction #:nodoc:
  46562. exec_query "BEGIN"
  46563. rescue Mysql::Error
  46564. # Transactions aren't supported
  46565. end
  46566. private
  46567. def exec_stmt(sql, name, binds)
  46568. cache = {}
  46569. log(sql, name, binds) do
  46570. if binds.empty?
  46571. stmt = @connection.prepare(sql)
  46572. else
  46573. cache = @statements[sql] ||= {
  46574. :stmt => @connection.prepare(sql)
  46575. }
  46576. stmt = cache[:stmt]
  46577. end
  46578. begin
  46579. stmt.execute(*binds.map { |col, val| type_cast(val, col) })
  46580. rescue Mysql::Error => e
  46581. # Older versions of MySQL leave the prepared statement in a bad
  46582. # place when an error occurs. To support older mysql versions, we
  46583. # need to close the statement and delete the statement from the
  46584. # cache.
  46585. stmt.close
  46586. @statements.delete sql
  46587. raise e
  46588. end
  46589. cols = nil
  46590. if metadata = stmt.result_metadata
  46591. cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
  46592. field.name
  46593. }
  46594. end
  46595. result_set = ActiveRecord::Result.new(cols, stmt.to_a) if cols
  46596. affected_rows = stmt.affected_rows
  46597. stmt.result_metadata.free if cols
  46598. stmt.free_result
  46599. stmt.close if binds.empty?
  46600. [result_set, affected_rows]
  46601. end
  46602. end
  46603. def connect
  46604. encoding = @config[:encoding]
  46605. if encoding
  46606. @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
  46607. end
  46608. if @config[:sslca] || @config[:sslkey]
  46609. @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
  46610. end
  46611. @connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
  46612. @connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
  46613. @connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
  46614. @connection.real_connect(*@connection_options)
  46615. # reconnect must be set after real_connect is called, because real_connect sets it to false internally
  46616. @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
  46617. configure_connection
  46618. end
  46619. # Many Rails applications monkey-patch a replacement of the configure_connection method
  46620. # and don't call 'super', so leave this here even though it looks superfluous.
  46621. def configure_connection
  46622. super
  46623. end
  46624. def select(sql, name = nil, binds = [])
  46625. @connection.query_with_result = true
  46626. rows = exec_query(sql, name, binds)
  46627. @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
  46628. rows
  46629. end
  46630. # Returns the version of the connected MySQL server.
  46631. def version
  46632. @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
  46633. end
  46634. end
  46635. end
  46636. end
  46637. module ActiveRecord
  46638. module ConnectionAdapters
  46639. class PostgreSQLColumn < Column
  46640. module ArrayParser
  46641. private
  46642. # Loads pg_array_parser if available. String parsing can be
  46643. # performed quicker by a native extension, which will not create
  46644. # a large amount of Ruby objects that will need to be garbage
  46645. # collected. pg_array_parser has a C and Java extension
  46646. begin
  46647. require 'pg_array_parser'
  46648. include PgArrayParser
  46649. rescue LoadError
  46650. def parse_pg_array(string)
  46651. parse_data(string, 0)
  46652. end
  46653. end
  46654. def parse_data(string, index)
  46655. local_index = index
  46656. array = []
  46657. while(local_index < string.length)
  46658. case string[local_index]
  46659. when '{'
  46660. local_index,array = parse_array_contents(array, string, local_index + 1)
  46661. when '}'
  46662. return array
  46663. end
  46664. local_index += 1
  46665. end
  46666. array
  46667. end
  46668. def parse_array_contents(array, string, index)
  46669. is_escaping = false
  46670. is_quoted = false
  46671. was_quoted = false
  46672. current_item = ''
  46673. local_index = index
  46674. while local_index
  46675. token = string[local_index]
  46676. if is_escaping
  46677. current_item << token
  46678. is_escaping = false
  46679. else
  46680. if is_quoted
  46681. case token
  46682. when '"'
  46683. is_quoted = false
  46684. was_quoted = true
  46685. when "\\"
  46686. is_escaping = true
  46687. else
  46688. current_item << token
  46689. end
  46690. else
  46691. case token
  46692. when "\\"
  46693. is_escaping = true
  46694. when ','
  46695. add_item_to_array(array, current_item, was_quoted)
  46696. current_item = ''
  46697. was_quoted = false
  46698. when '"'
  46699. is_quoted = true
  46700. when '{'
  46701. internal_items = []
  46702. local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
  46703. array.push(internal_items)
  46704. when '}'
  46705. add_item_to_array(array, current_item, was_quoted)
  46706. return local_index,array
  46707. else
  46708. current_item << token
  46709. end
  46710. end
  46711. end
  46712. local_index += 1
  46713. end
  46714. return local_index,array
  46715. end
  46716. def add_item_to_array(array, current_item, quoted)
  46717. if current_item.length == 0
  46718. elsif !quoted && current_item == 'NULL'
  46719. array.push nil
  46720. else
  46721. array.push current_item
  46722. end
  46723. end
  46724. end
  46725. end
  46726. end
  46727. end
  46728. module ActiveRecord
  46729. module ConnectionAdapters
  46730. class PostgreSQLColumn < Column
  46731. module Cast
  46732. def string_to_time(string)
  46733. return string unless String === string
  46734. case string
  46735. when 'infinity'; 1.0 / 0.0
  46736. when '-infinity'; -1.0 / 0.0
  46737. when / BC$/
  46738. super("-" + string.sub(/ BC$/, ""))
  46739. else
  46740. super
  46741. end
  46742. end
  46743. def hstore_to_string(object)
  46744. if Hash === object
  46745. object.map { |k,v|
  46746. "#{escape_hstore(k)}=>#{escape_hstore(v)}"
  46747. }.join ','
  46748. else
  46749. object
  46750. end
  46751. end
  46752. def string_to_hstore(string)
  46753. if string.nil?
  46754. nil
  46755. elsif String === string
  46756. Hash[string.scan(HstorePair).map { |k,v|
  46757. v = v.upcase == 'NULL' ? nil : v.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
  46758. k = k.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
  46759. [k,v]
  46760. }]
  46761. else
  46762. string
  46763. end
  46764. end
  46765. def json_to_string(object)
  46766. if Hash === object
  46767. ActiveSupport::JSON.encode(object)
  46768. else
  46769. object
  46770. end
  46771. end
  46772. def array_to_string(value, column, adapter, should_be_quoted = false)
  46773. casted_values = value.map do |val|
  46774. if String === val
  46775. if val == "NULL"
  46776. "\"#{val}\""
  46777. else
  46778. quote_and_escape(adapter.type_cast(val, column, true))
  46779. end
  46780. else
  46781. adapter.type_cast(val, column, true)
  46782. end
  46783. end
  46784. "{#{casted_values.join(',')}}"
  46785. end
  46786. def range_to_string(object)
  46787. from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
  46788. to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
  46789. "[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
  46790. end
  46791. def string_to_json(string)
  46792. if String === string
  46793. ActiveSupport::JSON.decode(string)
  46794. else
  46795. string
  46796. end
  46797. end
  46798. def string_to_cidr(string)
  46799. if string.nil?
  46800. nil
  46801. elsif String === string
  46802. IPAddr.new(string)
  46803. else
  46804. string
  46805. end
  46806. end
  46807. def cidr_to_string(object)
  46808. if IPAddr === object
  46809. "#{object.to_s}/#{object.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
  46810. else
  46811. object
  46812. end
  46813. end
  46814. def string_to_array(string, oid)
  46815. parse_pg_array(string).map{|val| oid.type_cast val}
  46816. end
  46817. private
  46818. HstorePair = begin
  46819. quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
  46820. unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
  46821. /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
  46822. end
  46823. def escape_hstore(value)
  46824. if value.nil?
  46825. 'NULL'
  46826. else
  46827. if value == ""
  46828. '""'
  46829. else
  46830. '"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
  46831. end
  46832. end
  46833. end
  46834. def quote_and_escape(value)
  46835. case value
  46836. when "NULL"
  46837. value
  46838. else
  46839. "\"#{value.gsub(/"/,"\\\"")}\""
  46840. end
  46841. end
  46842. end
  46843. end
  46844. end
  46845. end
  46846. module ActiveRecord
  46847. module ConnectionAdapters
  46848. class PostgreSQLAdapter < AbstractAdapter
  46849. module DatabaseStatements
  46850. def explain(arel, binds = [])
  46851. sql = "EXPLAIN #{to_sql(arel, binds)}"
  46852. ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
  46853. end
  46854. class ExplainPrettyPrinter # :nodoc:
  46855. # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
  46856. # PostgreSQL shell:
  46857. #
  46858. # QUERY PLAN
  46859. # ------------------------------------------------------------------------------
  46860. # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
  46861. # Join Filter: (posts.user_id = users.id)
  46862. # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
  46863. # Index Cond: (id = 1)
  46864. # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
  46865. # Filter: (posts.user_id = 1)
  46866. # (6 rows)
  46867. #
  46868. def pp(result)
  46869. header = result.columns.first
  46870. lines = result.rows.map(&:first)
  46871. # We add 2 because there's one char of padding at both sides, note
  46872. # the extra hyphens in the example above.
  46873. width = [header, *lines].map(&:length).max + 2
  46874. pp = []
  46875. pp << header.center(width).rstrip
  46876. pp << '-' * width
  46877. pp += lines.map {|line| " #{line}"}
  46878. nrows = result.rows.length
  46879. rows_label = nrows == 1 ? 'row' : 'rows'
  46880. pp << "(#{nrows} #{rows_label})"
  46881. pp.join("\n") + "\n"
  46882. end
  46883. end
  46884. # Executes a SELECT query and returns an array of rows. Each row is an
  46885. # array of field values.
  46886. def select_rows(sql, name = nil)
  46887. select_raw(sql, name).last
  46888. end
  46889. # Executes an INSERT query and returns the new record's ID
  46890. def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
  46891. unless pk
  46892. # Extract the table from the insert sql. Yuck.
  46893. table_ref = extract_table_ref_from_insert_sql(sql)
  46894. pk = primary_key(table_ref) if table_ref
  46895. end
  46896. if pk && use_insert_returning?
  46897. select_value("#{sql} RETURNING #{quote_column_name(pk)}")
  46898. elsif pk
  46899. super
  46900. last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
  46901. else
  46902. super
  46903. end
  46904. end
  46905. def create
  46906. super.insert
  46907. end
  46908. # create a 2D array representing the result set
  46909. def result_as_array(res) #:nodoc:
  46910. # check if we have any binary column and if they need escaping
  46911. ftypes = Array.new(res.nfields) do |i|
  46912. [i, res.ftype(i)]
  46913. end
  46914. rows = res.values
  46915. return rows unless ftypes.any? { |_, x|
  46916. x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
  46917. }
  46918. typehash = ftypes.group_by { |_, type| type }
  46919. binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
  46920. monies = typehash[MONEY_COLUMN_TYPE_OID] || []
  46921. rows.each do |row|
  46922. # unescape string passed BYTEA field (OID == 17)
  46923. binaries.each do |index, _|
  46924. row[index] = unescape_bytea(row[index])
  46925. end
  46926. # If this is a money type column and there are any currency symbols,
  46927. # then strip them off. Indeed it would be prettier to do this in
  46928. # PostgreSQLColumn.string_to_decimal but would break form input
  46929. # fields that call value_before_type_cast.
  46930. monies.each do |index, _|
  46931. data = row[index]
  46932. # Because money output is formatted according to the locale, there are two
  46933. # cases to consider (note the decimal separators):
  46934. # (1) $12,345,678.12
  46935. # (2) $12.345.678,12
  46936. case data
  46937. when /^-?\D+[\d,]+\.\d{2}$/ # (1)
  46938. data.gsub!(/[^-\d.]/, '')
  46939. when /^-?\D+[\d.]+,\d{2}$/ # (2)
  46940. data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
  46941. end
  46942. end
  46943. end
  46944. end
  46945. # Queries the database and returns the results in an Array-like object
  46946. def query(sql, name = nil) #:nodoc:
  46947. log(sql, name) do
  46948. result_as_array @connection.async_exec(sql)
  46949. end
  46950. end
  46951. # Executes an SQL statement, returning a PGresult object on success
  46952. # or raising a PGError exception otherwise.
  46953. def execute(sql, name = nil)
  46954. log(sql, name) do
  46955. @connection.async_exec(sql)
  46956. end
  46957. end
  46958. def substitute_at(column, index)
  46959. Arel::Nodes::BindParam.new "$#{index + 1}"
  46960. end
  46961. def exec_query(sql, name = 'SQL', binds = [])
  46962. log(sql, name, binds) do
  46963. result = binds.empty? ? exec_no_cache(sql, binds) :
  46964. exec_cache(sql, binds)
  46965. types = {}
  46966. result.fields.each_with_index do |fname, i|
  46967. ftype = result.ftype i
  46968. fmod = result.fmod i
  46969. types[fname] = OID::TYPE_MAP.fetch(ftype, fmod) { |oid, mod|
  46970. warn "unknown OID: #{fname}(#{oid}) (#{sql})"
  46971. OID::Identity.new
  46972. }
  46973. end
  46974. ret = ActiveRecord::Result.new(result.fields, result.values, types)
  46975. result.clear
  46976. return ret
  46977. end
  46978. end
  46979. def exec_delete(sql, name = 'SQL', binds = [])
  46980. log(sql, name, binds) do
  46981. result = binds.empty? ? exec_no_cache(sql, binds) :
  46982. exec_cache(sql, binds)
  46983. affected = result.cmd_tuples
  46984. result.clear
  46985. affected
  46986. end
  46987. end
  46988. alias :exec_update :exec_delete
  46989. def sql_for_insert(sql, pk, id_value, sequence_name, binds)
  46990. unless pk
  46991. # Extract the table from the insert sql. Yuck.
  46992. table_ref = extract_table_ref_from_insert_sql(sql)
  46993. pk = primary_key(table_ref) if table_ref
  46994. end
  46995. if pk && use_insert_returning?
  46996. sql = "#{sql} RETURNING #{quote_column_name(pk)}"
  46997. end
  46998. [sql, binds]
  46999. end
  47000. def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
  47001. val = exec_query(sql, name, binds)
  47002. if !use_insert_returning? && pk
  47003. unless sequence_name
  47004. table_ref = extract_table_ref_from_insert_sql(sql)
  47005. sequence_name = default_sequence_name(table_ref, pk)
  47006. return val unless sequence_name
  47007. end
  47008. last_insert_id_result(sequence_name)
  47009. else
  47010. val
  47011. end
  47012. end
  47013. # Executes an UPDATE query and returns the number of affected tuples.
  47014. def update_sql(sql, name = nil)
  47015. super.cmd_tuples
  47016. end
  47017. # Begins a transaction.
  47018. def begin_db_transaction
  47019. execute "BEGIN"
  47020. end
  47021. def begin_isolated_db_transaction(isolation)
  47022. begin_db_transaction
  47023. execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
  47024. end
  47025. # Commits a transaction.
  47026. def commit_db_transaction
  47027. execute "COMMIT"
  47028. end
  47029. # Aborts a transaction.
  47030. def rollback_db_transaction
  47031. execute "ROLLBACK"
  47032. end
  47033. def outside_transaction?
  47034. message = "#outside_transaction? is deprecated. This method was only really used " \
  47035. "internally, but you can use #transaction_open? instead."
  47036. ActiveSupport::Deprecation.warn message
  47037. @connection.transaction_status == PGconn::PQTRANS_IDLE
  47038. end
  47039. def create_savepoint
  47040. execute("SAVEPOINT #{current_savepoint_name}")
  47041. end
  47042. def rollback_to_savepoint
  47043. execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
  47044. end
  47045. def release_savepoint
  47046. execute("RELEASE SAVEPOINT #{current_savepoint_name}")
  47047. end
  47048. end
  47049. end
  47050. end
  47051. end
  47052. require 'active_record/connection_adapters/abstract_adapter'
  47053. module ActiveRecord
  47054. module ConnectionAdapters
  47055. class PostgreSQLAdapter < AbstractAdapter
  47056. module OID
  47057. class Type
  47058. def type; end
  47059. def type_cast_for_write(value)
  47060. value
  47061. end
  47062. end
  47063. class Identity < Type
  47064. def type_cast(value)
  47065. value
  47066. end
  47067. end
  47068. class Bytea < Type
  47069. def type_cast(value)
  47070. PGconn.unescape_bytea value
  47071. end
  47072. end
  47073. class Money < Type
  47074. def type_cast(value)
  47075. return if value.nil?
  47076. # Because money output is formatted according to the locale, there are two
  47077. # cases to consider (note the decimal separators):
  47078. # (1) $12,345,678.12
  47079. # (2) $12.345.678,12
  47080. case value
  47081. when /^-?\D+[\d,]+\.\d{2}$/ # (1)
  47082. value.gsub!(/[^-\d.]/, '')
  47083. when /^-?\D+[\d.]+,\d{2}$/ # (2)
  47084. value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
  47085. end
  47086. ConnectionAdapters::Column.value_to_decimal value
  47087. end
  47088. end
  47089. class Vector < Type
  47090. attr_reader :delim, :subtype
  47091. # +delim+ corresponds to the `typdelim` column in the pg_types
  47092. # table. +subtype+ is derived from the `typelem` column in the
  47093. # pg_types table.
  47094. def initialize(delim, subtype)
  47095. @delim = delim
  47096. @subtype = subtype
  47097. end
  47098. # FIXME: this should probably split on +delim+ and use +subtype+
  47099. # to cast the values. Unfortunately, the current Rails behavior
  47100. # is to just return the string.
  47101. def type_cast(value)
  47102. value
  47103. end
  47104. end
  47105. class Array < Type
  47106. attr_reader :subtype
  47107. def initialize(subtype)
  47108. @subtype = subtype
  47109. end
  47110. def type_cast(value)
  47111. if String === value
  47112. ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
  47113. else
  47114. value
  47115. end
  47116. end
  47117. end
  47118. class Range < Type
  47119. attr_reader :subtype
  47120. def initialize(subtype)
  47121. @subtype = subtype
  47122. end
  47123. def extract_bounds(value)
  47124. from, to = value[1..-2].split(',')
  47125. {
  47126. from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from,
  47127. to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
  47128. exclude_start: (value[0] == '('),
  47129. exclude_end: (value[-1] == ')')
  47130. }
  47131. end
  47132. def infinity(options = {})
  47133. ::Float::INFINITY * (options[:negative] ? -1 : 1)
  47134. end
  47135. def infinity?(value)
  47136. value.respond_to?(:infinite?) && value.infinite?
  47137. end
  47138. def to_integer(value)
  47139. infinity?(value) ? value : value.to_i
  47140. end
  47141. def type_cast(value)
  47142. return if value.nil? || value == 'empty'
  47143. return value if value.is_a?(::Range)
  47144. extracted = extract_bounds(value)
  47145. case @subtype
  47146. when :date
  47147. from = ConnectionAdapters::Column.value_to_date(extracted[:from])
  47148. from -= 1.day if extracted[:exclude_start]
  47149. to = ConnectionAdapters::Column.value_to_date(extracted[:to])
  47150. when :decimal
  47151. from = BigDecimal.new(extracted[:from].to_s)
  47152. # FIXME: add exclude start for ::Range, same for timestamp ranges
  47153. to = BigDecimal.new(extracted[:to].to_s)
  47154. when :time
  47155. from = ConnectionAdapters::Column.string_to_time(extracted[:from])
  47156. to = ConnectionAdapters::Column.string_to_time(extracted[:to])
  47157. when :integer
  47158. from = to_integer(extracted[:from]) rescue value ? 1 : 0
  47159. from -= 1 if extracted[:exclude_start]
  47160. to = to_integer(extracted[:to]) rescue value ? 1 : 0
  47161. else
  47162. return value
  47163. end
  47164. ::Range.new(from, to, extracted[:exclude_end])
  47165. end
  47166. end
  47167. class Integer < Type
  47168. def type_cast(value)
  47169. return if value.nil?
  47170. ConnectionAdapters::Column.value_to_integer value
  47171. end
  47172. end
  47173. class Boolean < Type
  47174. def type_cast(value)
  47175. return if value.nil?
  47176. ConnectionAdapters::Column.value_to_boolean value
  47177. end
  47178. end
  47179. class Timestamp < Type
  47180. def type; :timestamp; end
  47181. def type_cast(value)
  47182. return if value.nil?
  47183. # FIXME: probably we can improve this since we know it is PG
  47184. # specific
  47185. ConnectionAdapters::PostgreSQLColumn.string_to_time value
  47186. end
  47187. end
  47188. class Date < Type
  47189. def type; :datetime; end
  47190. def type_cast(value)
  47191. return if value.nil?
  47192. # FIXME: probably we can improve this since we know it is PG
  47193. # specific
  47194. ConnectionAdapters::Column.value_to_date value
  47195. end
  47196. end
  47197. class Time < Type
  47198. def type_cast(value)
  47199. return if value.nil?
  47200. # FIXME: probably we can improve this since we know it is PG
  47201. # specific
  47202. ConnectionAdapters::Column.string_to_dummy_time value
  47203. end
  47204. end
  47205. class Float < Type
  47206. def type_cast(value)
  47207. return if value.nil?
  47208. value.to_f
  47209. end
  47210. end
  47211. class Decimal < Type
  47212. def type_cast(value)
  47213. return if value.nil?
  47214. ConnectionAdapters::Column.value_to_decimal value
  47215. end
  47216. end
  47217. class Hstore < Type
  47218. def type_cast(value)
  47219. return if value.nil?
  47220. ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
  47221. end
  47222. end
  47223. class Cidr < Type
  47224. def type_cast(value)
  47225. return if value.nil?
  47226. ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
  47227. end
  47228. end
  47229. class Json < Type
  47230. def type_cast(value)
  47231. return if value.nil?
  47232. ConnectionAdapters::PostgreSQLColumn.string_to_json value
  47233. end
  47234. end
  47235. class TypeMap
  47236. def initialize
  47237. @mapping = {}
  47238. end
  47239. def []=(oid, type)
  47240. @mapping[oid] = type
  47241. end
  47242. def [](oid)
  47243. @mapping[oid]
  47244. end
  47245. def clear
  47246. @mapping.clear
  47247. end
  47248. def key?(oid)
  47249. @mapping.key? oid
  47250. end
  47251. def fetch(ftype, fmod)
  47252. # The type for the numeric depends on the width of the field,
  47253. # so we'll do something special here.
  47254. #
  47255. # When dealing with decimal columns:
  47256. #
  47257. # places after decimal = fmod - 4 & 0xffff
  47258. # places before decimal = (fmod - 4) >> 16 & 0xffff
  47259. if ftype == 1700 && (fmod - 4 & 0xffff).zero?
  47260. ftype = 23
  47261. end
  47262. @mapping.fetch(ftype) { |oid| yield oid, fmod }
  47263. end
  47264. end
  47265. TYPE_MAP = TypeMap.new # :nodoc:
  47266. # When the PG adapter connects, the pg_type table is queried. The
  47267. # key of this hash maps to the `typname` column from the table.
  47268. # TYPE_MAP is then dynamically built with oids as the key and type
  47269. # objects as values.
  47270. NAMES = Hash.new { |h,k| # :nodoc:
  47271. h[k] = OID::Identity.new
  47272. }
  47273. # Register an OID type named +name+ with a typcasting object in
  47274. # +type+. +name+ should correspond to the `typname` column in
  47275. # the `pg_type` table.
  47276. def self.register_type(name, type)
  47277. NAMES[name] = type
  47278. end
  47279. # Alias the +old+ type to the +new+ type.
  47280. def self.alias_type(new, old)
  47281. NAMES[new] = NAMES[old]
  47282. end
  47283. # Is +name+ a registered type?
  47284. def self.registered_type?(name)
  47285. NAMES.key? name
  47286. end
  47287. register_type 'int2', OID::Integer.new
  47288. alias_type 'int4', 'int2'
  47289. alias_type 'int8', 'int2'
  47290. alias_type 'oid', 'int2'
  47291. register_type 'daterange', OID::Range.new(:date)
  47292. register_type 'numrange', OID::Range.new(:decimal)
  47293. register_type 'tsrange', OID::Range.new(:time)
  47294. register_type 'int4range', OID::Range.new(:integer)
  47295. alias_type 'tstzrange', 'tsrange'
  47296. alias_type 'int8range', 'int4range'
  47297. register_type 'numeric', OID::Decimal.new
  47298. register_type 'text', OID::Identity.new
  47299. alias_type 'varchar', 'text'
  47300. alias_type 'char', 'text'
  47301. alias_type 'bpchar', 'text'
  47302. alias_type 'xml', 'text'
  47303. # FIXME: why are we keeping these types as strings?
  47304. alias_type 'tsvector', 'text'
  47305. alias_type 'interval', 'text'
  47306. alias_type 'bit', 'text'
  47307. alias_type 'varbit', 'text'
  47308. alias_type 'macaddr', 'text'
  47309. alias_type 'uuid', 'text'
  47310. # FIXME: I don't think this is correct. We should probably be returning a parsed date,
  47311. # but the tests pass with a string returned.
  47312. register_type 'timestamptz', OID::Identity.new
  47313. register_type 'money', OID::Money.new
  47314. register_type 'bytea', OID::Bytea.new
  47315. register_type 'bool', OID::Boolean.new
  47316. register_type 'float4', OID::Float.new
  47317. alias_type 'float8', 'float4'
  47318. register_type 'timestamp', OID::Timestamp.new
  47319. register_type 'date', OID::Date.new
  47320. register_type 'time', OID::Time.new
  47321. register_type 'path', OID::Identity.new
  47322. register_type 'polygon', OID::Identity.new
  47323. register_type 'circle', OID::Identity.new
  47324. register_type 'hstore', OID::Hstore.new
  47325. register_type 'json', OID::Json.new
  47326. register_type 'ltree', OID::Identity.new
  47327. register_type 'cidr', OID::Cidr.new
  47328. alias_type 'inet', 'cidr'
  47329. end
  47330. end
  47331. end
  47332. end
  47333. module ActiveRecord
  47334. module ConnectionAdapters
  47335. class PostgreSQLAdapter < AbstractAdapter
  47336. module Quoting
  47337. # Escapes binary strings for bytea input to the database.
  47338. def escape_bytea(value)
  47339. PGconn.escape_bytea(value) if value
  47340. end
  47341. # Unescapes bytea output from a database to the binary string it represents.
  47342. # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
  47343. # on escaped binary output from database drive.
  47344. def unescape_bytea(value)
  47345. PGconn.unescape_bytea(value) if value
  47346. end
  47347. # Quotes PostgreSQL-specific data types for SQL input.
  47348. def quote(value, column = nil) #:nodoc:
  47349. return super unless column
  47350. case value
  47351. when Range
  47352. if /range$/ =~ column.sql_type
  47353. "'#{PostgreSQLColumn.range_to_string(value)}'::#{column.sql_type}"
  47354. else
  47355. super
  47356. end
  47357. when Array
  47358. if column.array
  47359. "'#{PostgreSQLColumn.array_to_string(value, column, self)}'"
  47360. else
  47361. super
  47362. end
  47363. when Hash
  47364. case column.sql_type
  47365. when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
  47366. when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
  47367. else super
  47368. end
  47369. when IPAddr
  47370. case column.sql_type
  47371. when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
  47372. else super
  47373. end
  47374. when Float
  47375. if value.infinite? && column.type == :datetime
  47376. "'#{value.to_s.downcase}'"
  47377. elsif value.infinite? || value.nan?
  47378. "'#{value.to_s}'"
  47379. else
  47380. super
  47381. end
  47382. when Numeric
  47383. return super unless column.sql_type == 'money'
  47384. # Not truly string input, so doesn't require (or allow) escape string syntax.
  47385. "'#{value}'"
  47386. when String
  47387. case column.sql_type
  47388. when 'bytea' then "'#{escape_bytea(value)}'"
  47389. when 'xml' then "xml '#{quote_string(value)}'"
  47390. when /^bit/
  47391. case value
  47392. when /^[01]*$/ then "B'#{value}'" # Bit-string notation
  47393. when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
  47394. end
  47395. else
  47396. super
  47397. end
  47398. else
  47399. super
  47400. end
  47401. end
  47402. def type_cast(value, column, array_member = false)
  47403. return super(value, column) unless column
  47404. case value
  47405. when Range
  47406. return super(value, column) unless /range$/ =~ column.sql_type
  47407. PostgreSQLColumn.range_to_string(value)
  47408. when NilClass
  47409. if column.array && array_member
  47410. 'NULL'
  47411. elsif column.array
  47412. value
  47413. else
  47414. super(value, column)
  47415. end
  47416. when Array
  47417. return super(value, column) unless column.array
  47418. PostgreSQLColumn.array_to_string(value, column, self)
  47419. when String
  47420. return super(value, column) unless 'bytea' == column.sql_type
  47421. { :value => value, :format => 1 }
  47422. when Hash
  47423. case column.sql_type
  47424. when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
  47425. when 'json' then PostgreSQLColumn.json_to_string(value)
  47426. else super(value, column)
  47427. end
  47428. when IPAddr
  47429. return super(value, column) unless ['inet','cidr'].include? column.sql_type
  47430. PostgreSQLColumn.cidr_to_string(value)
  47431. else
  47432. super(value, column)
  47433. end
  47434. end
  47435. # Quotes strings for use in SQL input.
  47436. def quote_string(s) #:nodoc:
  47437. @connection.escape(s)
  47438. end
  47439. # Checks the following cases:
  47440. #
  47441. # - table_name
  47442. # - "table.name"
  47443. # - schema_name.table_name
  47444. # - schema_name."table.name"
  47445. # - "schema.name".table_name
  47446. # - "schema.name"."table.name"
  47447. def quote_table_name(name)
  47448. schema, name_part = extract_pg_identifier_from_name(name.to_s)
  47449. unless name_part
  47450. quote_column_name(schema)
  47451. else
  47452. table_name, name_part = extract_pg_identifier_from_name(name_part)
  47453. "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
  47454. end
  47455. end
  47456. def quote_table_name_for_assignment(table, attr)
  47457. quote_column_name(attr)
  47458. end
  47459. # Quotes column names for use in SQL queries.
  47460. def quote_column_name(name) #:nodoc:
  47461. PGconn.quote_ident(name.to_s)
  47462. end
  47463. # Quote date/time values for use in SQL input. Includes microseconds
  47464. # if the value is a Time responding to usec.
  47465. def quoted_date(value) #:nodoc:
  47466. result = super
  47467. if value.acts_like?(:time) && value.respond_to?(:usec)
  47468. result = "#{result}.#{sprintf("%06d", value.usec)}"
  47469. end
  47470. if value.year < 0
  47471. result = result.sub(/^-/, "") + " BC"
  47472. end
  47473. result
  47474. end
  47475. end
  47476. end
  47477. end
  47478. end
  47479. module ActiveRecord
  47480. module ConnectionAdapters
  47481. class PostgreSQLAdapter < AbstractAdapter
  47482. module ReferentialIntegrity
  47483. def supports_disable_referential_integrity? #:nodoc:
  47484. true
  47485. end
  47486. def disable_referential_integrity #:nodoc:
  47487. if supports_disable_referential_integrity?
  47488. begin
  47489. execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
  47490. rescue
  47491. execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
  47492. end
  47493. end
  47494. yield
  47495. ensure
  47496. if supports_disable_referential_integrity?
  47497. begin
  47498. execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
  47499. rescue
  47500. execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
  47501. end
  47502. end
  47503. end
  47504. end
  47505. end
  47506. end
  47507. end
  47508. module ActiveRecord
  47509. module ConnectionAdapters
  47510. class PostgreSQLAdapter < AbstractAdapter
  47511. module SchemaStatements
  47512. # Drops the database specified on the +name+ attribute
  47513. # and creates it again using the provided +options+.
  47514. def recreate_database(name, options = {}) #:nodoc:
  47515. drop_database(name)
  47516. create_database(name, options)
  47517. end
  47518. # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
  47519. # <tt>:encoding</tt>, <tt>:collation</tt>, <tt>:ctype</tt>,
  47520. # <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
  47521. # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
  47522. #
  47523. # Example:
  47524. # create_database config[:database], config
  47525. # create_database 'foo_development', encoding: 'unicode'
  47526. def create_database(name, options = {})
  47527. options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
  47528. option_string = options.sum do |key, value|
  47529. case key
  47530. when :owner
  47531. " OWNER = \"#{value}\""
  47532. when :template
  47533. " TEMPLATE = \"#{value}\""
  47534. when :encoding
  47535. " ENCODING = '#{value}'"
  47536. when :collation
  47537. " LC_COLLATE = '#{value}'"
  47538. when :ctype
  47539. " LC_CTYPE = '#{value}'"
  47540. when :tablespace
  47541. " TABLESPACE = \"#{value}\""
  47542. when :connection_limit
  47543. " CONNECTION LIMIT = #{value}"
  47544. else
  47545. ""
  47546. end
  47547. end
  47548. execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
  47549. end
  47550. # Drops a PostgreSQL database.
  47551. #
  47552. # Example:
  47553. # drop_database 'matt_development'
  47554. def drop_database(name) #:nodoc:
  47555. execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
  47556. end
  47557. # Returns the list of all tables in the schema search path or a specified schema.
  47558. def tables(name = nil)
  47559. query(<<-SQL, 'SCHEMA').map { |row| row[0] }
  47560. SELECT tablename
  47561. FROM pg_tables
  47562. WHERE schemaname = ANY (current_schemas(false))
  47563. SQL
  47564. end
  47565. # Returns true if table exists.
  47566. # If the schema is not specified as part of +name+ then it will only find tables within
  47567. # the current schema search path (regardless of permissions to access tables in other schemas)
  47568. def table_exists?(name)
  47569. schema, table = Utils.extract_schema_and_table(name.to_s)
  47570. return false unless table
  47571. binds = [[nil, table]]
  47572. binds << [nil, schema] if schema
  47573. exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
  47574. SELECT COUNT(*)
  47575. FROM pg_class c
  47576. LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
  47577. WHERE c.relkind in ('v','r')
  47578. AND c.relname = '#{table.gsub(/(^"|"$)/,'')}'
  47579. AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
  47580. SQL
  47581. end
  47582. # Returns true if schema exists.
  47583. def schema_exists?(name)
  47584. exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
  47585. SELECT COUNT(*)
  47586. FROM pg_namespace
  47587. WHERE nspname = '#{name}'
  47588. SQL
  47589. end
  47590. # Returns an array of indexes for the given table.
  47591. def indexes(table_name, name = nil)
  47592. result = query(<<-SQL, 'SCHEMA')
  47593. SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
  47594. FROM pg_class t
  47595. INNER JOIN pg_index d ON t.oid = d.indrelid
  47596. INNER JOIN pg_class i ON d.indexrelid = i.oid
  47597. WHERE i.relkind = 'i'
  47598. AND d.indisprimary = 'f'
  47599. AND t.relname = '#{table_name}'
  47600. AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
  47601. ORDER BY i.relname
  47602. SQL
  47603. result.map do |row|
  47604. index_name = row[0]
  47605. unique = row[1] == 't'
  47606. indkey = row[2].split(" ")
  47607. inddef = row[3]
  47608. oid = row[4]
  47609. columns = Hash[query(<<-SQL, "SCHEMA")]
  47610. SELECT a.attnum, a.attname
  47611. FROM pg_attribute a
  47612. WHERE a.attrelid = #{oid}
  47613. AND a.attnum IN (#{indkey.join(",")})
  47614. SQL
  47615. column_names = columns.values_at(*indkey).compact
  47616. # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
  47617. desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
  47618. orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
  47619. where = inddef.scan(/WHERE (.+)$/).flatten[0]
  47620. column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where)
  47621. end.compact
  47622. end
  47623. # Returns the list of all column definitions for a table.
  47624. def columns(table_name)
  47625. # Limit, precision, and scale are all handled by the superclass.
  47626. column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
  47627. oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) {
  47628. OID::Identity.new
  47629. }
  47630. PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
  47631. end
  47632. end
  47633. # Returns the current database name.
  47634. def current_database
  47635. query('select current_database()', 'SCHEMA')[0][0]
  47636. end
  47637. # Returns the current schema name.
  47638. def current_schema
  47639. query('SELECT current_schema', 'SCHEMA')[0][0]
  47640. end
  47641. # Returns the current database encoding format.
  47642. def encoding
  47643. query(<<-end_sql, 'SCHEMA')[0][0]
  47644. SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
  47645. WHERE pg_database.datname LIKE '#{current_database}'
  47646. end_sql
  47647. end
  47648. # Returns the current database collation.
  47649. def collation
  47650. query(<<-end_sql, 'SCHEMA')[0][0]
  47651. SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
  47652. end_sql
  47653. end
  47654. # Returns the current database ctype.
  47655. def ctype
  47656. query(<<-end_sql, 'SCHEMA')[0][0]
  47657. SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
  47658. end_sql
  47659. end
  47660. # Returns an array of schema names.
  47661. def schema_names
  47662. query(<<-SQL, 'SCHEMA').flatten
  47663. SELECT nspname
  47664. FROM pg_namespace
  47665. WHERE nspname !~ '^pg_.*'
  47666. AND nspname NOT IN ('information_schema')
  47667. ORDER by nspname;
  47668. SQL
  47669. end
  47670. # Creates a schema for the given schema name.
  47671. def create_schema schema_name
  47672. execute "CREATE SCHEMA #{schema_name}"
  47673. end
  47674. # Drops the schema for the given schema name.
  47675. def drop_schema schema_name
  47676. execute "DROP SCHEMA #{schema_name} CASCADE"
  47677. end
  47678. # Sets the schema search path to a string of comma-separated schema names.
  47679. # Names beginning with $ have to be quoted (e.g. $user => '$user').
  47680. # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
  47681. #
  47682. # This should be not be called manually but set in database.yml.
  47683. def schema_search_path=(schema_csv)
  47684. if schema_csv
  47685. execute("SET search_path TO #{schema_csv}", 'SCHEMA')
  47686. @schema_search_path = schema_csv
  47687. end
  47688. end
  47689. # Returns the active schema search path.
  47690. def schema_search_path
  47691. @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
  47692. end
  47693. # Returns the current client message level.
  47694. def client_min_messages
  47695. query('SHOW client_min_messages', 'SCHEMA')[0][0]
  47696. end
  47697. # Set the client message level.
  47698. def client_min_messages=(level)
  47699. execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
  47700. end
  47701. # Returns the sequence name for a table's primary key or some other specified key.
  47702. def default_sequence_name(table_name, pk = nil) #:nodoc:
  47703. result = serial_sequence(table_name, pk || 'id')
  47704. return nil unless result
  47705. result.split('.').last
  47706. rescue ActiveRecord::StatementInvalid
  47707. "#{table_name}_#{pk || 'id'}_seq"
  47708. end
  47709. def serial_sequence(table, column)
  47710. result = exec_query(<<-eosql, 'SCHEMA')
  47711. SELECT pg_get_serial_sequence('#{table}', '#{column}')
  47712. eosql
  47713. result.rows.first.first
  47714. end
  47715. # Resets the sequence of a table's primary key to the maximum value.
  47716. def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
  47717. unless pk and sequence
  47718. default_pk, default_sequence = pk_and_sequence_for(table)
  47719. pk ||= default_pk
  47720. sequence ||= default_sequence
  47721. end
  47722. if @logger && pk && !sequence
  47723. @logger.warn "#{table} has primary key #{pk} with no default sequence"
  47724. end
  47725. if pk && sequence
  47726. quoted_sequence = quote_table_name(sequence)
  47727. select_value <<-end_sql, 'SCHEMA'
  47728. SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
  47729. end_sql
  47730. end
  47731. end
  47732. # Returns a table's primary key and belonging sequence.
  47733. def pk_and_sequence_for(table) #:nodoc:
  47734. # First try looking for a sequence with a dependency on the
  47735. # given table's primary key.
  47736. result = query(<<-end_sql, 'SCHEMA')[0]
  47737. SELECT attr.attname, seq.relname
  47738. FROM pg_class seq,
  47739. pg_attribute attr,
  47740. pg_depend dep,
  47741. pg_constraint cons
  47742. WHERE seq.oid = dep.objid
  47743. AND seq.relkind = 'S'
  47744. AND attr.attrelid = dep.refobjid
  47745. AND attr.attnum = dep.refobjsubid
  47746. AND attr.attrelid = cons.conrelid
  47747. AND attr.attnum = cons.conkey[1]
  47748. AND cons.contype = 'p'
  47749. AND dep.refobjid = '#{quote_table_name(table)}'::regclass
  47750. end_sql
  47751. if result.nil? or result.empty?
  47752. result = query(<<-end_sql, 'SCHEMA')[0]
  47753. SELECT attr.attname,
  47754. CASE
  47755. WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
  47756. substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
  47757. strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
  47758. ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
  47759. END
  47760. FROM pg_class t
  47761. JOIN pg_attribute attr ON (t.oid = attrelid)
  47762. JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
  47763. JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
  47764. WHERE t.oid = '#{quote_table_name(table)}'::regclass
  47765. AND cons.contype = 'p'
  47766. AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval'
  47767. end_sql
  47768. end
  47769. [result.first, result.last]
  47770. rescue
  47771. nil
  47772. end
  47773. # Returns just a table's primary key
  47774. def primary_key(table)
  47775. row = exec_query(<<-end_sql, 'SCHEMA').rows.first
  47776. SELECT attr.attname
  47777. FROM pg_attribute attr
  47778. INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
  47779. WHERE cons.contype = 'p'
  47780. AND cons.conrelid = '#{quote_table_name(table)}'::regclass
  47781. end_sql
  47782. row && row.first
  47783. end
  47784. # Renames a table.
  47785. # Also renames a table's primary key sequence if the sequence name matches the
  47786. # Active Record default.
  47787. #
  47788. # Example:
  47789. # rename_table('octopuses', 'octopi')
  47790. def rename_table(name, new_name)
  47791. clear_cache!
  47792. execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
  47793. pk, seq = pk_and_sequence_for(new_name)
  47794. if seq == "#{name}_#{pk}_seq"
  47795. new_seq = "#{new_name}_#{pk}_seq"
  47796. execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
  47797. end
  47798. end
  47799. # Adds a new column to the named table.
  47800. # See TableDefinition#column for details of the options you can use.
  47801. def add_column(table_name, column_name, type, options = {})
  47802. clear_cache!
  47803. add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  47804. add_column_options!(add_column_sql, options)
  47805. execute add_column_sql
  47806. end
  47807. # Changes the column of a table.
  47808. def change_column(table_name, column_name, type, options = {})
  47809. clear_cache!
  47810. quoted_table_name = quote_table_name(table_name)
  47811. execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  47812. change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
  47813. change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
  47814. end
  47815. # Changes the default value of a table column.
  47816. def change_column_default(table_name, column_name, default)
  47817. clear_cache!
  47818. execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
  47819. end
  47820. def change_column_null(table_name, column_name, null, default = nil)
  47821. clear_cache!
  47822. unless null || default.nil?
  47823. execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  47824. end
  47825. execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
  47826. end
  47827. # Renames a column in a table.
  47828. def rename_column(table_name, column_name, new_column_name)
  47829. clear_cache!
  47830. execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
  47831. end
  47832. def remove_index!(table_name, index_name) #:nodoc:
  47833. execute "DROP INDEX #{quote_table_name(index_name)}"
  47834. end
  47835. def rename_index(table_name, old_name, new_name)
  47836. execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
  47837. end
  47838. def index_name_length
  47839. 63
  47840. end
  47841. # Maps logical Rails types to PostgreSQL-specific data types.
  47842. def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  47843. case type.to_s
  47844. when 'binary'
  47845. # PostgreSQL doesn't support limits on binary (bytea) columns.
  47846. # The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
  47847. case limit
  47848. when nil, 0..0x3fffffff; super(type)
  47849. else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
  47850. end
  47851. when 'text'
  47852. # PostgreSQL doesn't support limits on text columns.
  47853. # The hard limit is 1Gb, according to section 8.3 in the manual.
  47854. case limit
  47855. when nil, 0..0x3fffffff; super(type)
  47856. else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
  47857. end
  47858. when 'integer'
  47859. return 'integer' unless limit
  47860. case limit
  47861. when 1, 2; 'smallint'
  47862. when 3, 4; 'integer'
  47863. when 5..8; 'bigint'
  47864. else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
  47865. end
  47866. when 'datetime'
  47867. return super unless precision
  47868. case precision
  47869. when 0..6; "timestamp(#{precision})"
  47870. else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
  47871. end
  47872. else
  47873. super
  47874. end
  47875. end
  47876. # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
  47877. #
  47878. # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
  47879. # requires that the ORDER BY include the distinct column.
  47880. #
  47881. # distinct("posts.id", ["posts.created_at desc"])
  47882. # # => "DISTINCT posts.id, posts.created_at AS alias_0"
  47883. def distinct(columns, orders) #:nodoc:
  47884. order_columns = orders.map{ |s|
  47885. # Convert Arel node to string
  47886. s = s.to_sql unless s.is_a?(String)
  47887. # Remove any ASC/DESC modifiers
  47888. s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '')
  47889. }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
  47890. [super].concat(order_columns).join(', ')
  47891. end
  47892. end
  47893. end
  47894. end
  47895. end
  47896. require 'active_record/connection_adapters/abstract_adapter'
  47897. require 'active_record/connection_adapters/statement_pool'
  47898. require 'active_record/connection_adapters/postgresql/oid'
  47899. require 'active_record/connection_adapters/postgresql/cast'
  47900. require 'active_record/connection_adapters/postgresql/array_parser'
  47901. require 'active_record/connection_adapters/postgresql/quoting'
  47902. require 'active_record/connection_adapters/postgresql/schema_statements'
  47903. require 'active_record/connection_adapters/postgresql/database_statements'
  47904. require 'active_record/connection_adapters/postgresql/referential_integrity'
  47905. require 'arel/visitors/bind_visitor'
  47906. # Make sure we're using pg high enough for PGResult#values
  47907. gem 'pg', '~> 0.11'
  47908. require 'pg'
  47909. require 'ipaddr'
  47910. module ActiveRecord
  47911. module ConnectionHandling
  47912. VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
  47913. :client_encoding, :options, :application_name, :fallback_application_name,
  47914. :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
  47915. :tty, :sslmode, :requiressl, :sslcert, :sslkey, :sslrootcert, :sslcrl,
  47916. :requirepeer, :krbsrvname, :gsslib, :service]
  47917. # Establishes a connection to the database that's used by all Active Record objects
  47918. def postgresql_connection(config) # :nodoc:
  47919. conn_params = config.symbolize_keys
  47920. conn_params.delete_if { |_, v| v.nil? }
  47921. # Map ActiveRecords param names to PGs.
  47922. conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
  47923. conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
  47924. # Forward only valid config params to PGconn.connect.
  47925. conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
  47926. # The postgres drivers don't allow the creation of an unconnected PGconn object,
  47927. # so just pass a nil connection object for the time being.
  47928. ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
  47929. end
  47930. end
  47931. module ConnectionAdapters
  47932. # PostgreSQL-specific extensions to column definitions in a table.
  47933. class PostgreSQLColumn < Column #:nodoc:
  47934. attr_accessor :array
  47935. # Instantiates a new PostgreSQL column definition in a table.
  47936. def initialize(name, default, oid_type, sql_type = nil, null = true)
  47937. @oid_type = oid_type
  47938. if sql_type =~ /\[\]$/
  47939. @array = true
  47940. super(name, self.class.extract_value_from_default(default), sql_type[0..sql_type.length - 3], null)
  47941. else
  47942. @array = false
  47943. super(name, self.class.extract_value_from_default(default), sql_type, null)
  47944. end
  47945. end
  47946. # :stopdoc:
  47947. class << self
  47948. include ConnectionAdapters::PostgreSQLColumn::Cast
  47949. include ConnectionAdapters::PostgreSQLColumn::ArrayParser
  47950. attr_accessor :money_precision
  47951. end
  47952. # :startdoc:
  47953. # Extracts the value from a PostgreSQL column default definition.
  47954. def self.extract_value_from_default(default)
  47955. # This is a performance optimization for Ruby 1.9.2 in development.
  47956. # If the value is nil, we return nil straight away without checking
  47957. # the regular expressions. If we check each regular expression,
  47958. # Regexp#=== will call NilClass#to_str, which will trigger
  47959. # method_missing (defined by whiny nil in ActiveSupport) which
  47960. # makes this method very very slow.
  47961. return default unless default
  47962. case default
  47963. when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
  47964. $1
  47965. # Numeric types
  47966. when /\A\(?(-?\d+(\.\d*)?\)?)\z/
  47967. $1
  47968. # Character types
  47969. when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
  47970. $1
  47971. # Binary data types
  47972. when /\A'(.*)'::bytea\z/m
  47973. $1
  47974. # Date/time types
  47975. when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
  47976. $1
  47977. when /\A'(.*)'::interval\z/
  47978. $1
  47979. # Boolean type
  47980. when 'true'
  47981. true
  47982. when 'false'
  47983. false
  47984. # Geometric types
  47985. when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
  47986. $1
  47987. # Network address types
  47988. when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
  47989. $1
  47990. # Bit string types
  47991. when /\AB'(.*)'::"?bit(?: varying)?"?\z/
  47992. $1
  47993. # XML type
  47994. when /\A'(.*)'::xml\z/m
  47995. $1
  47996. # Arrays
  47997. when /\A'(.*)'::"?\D+"?\[\]\z/
  47998. $1
  47999. # Hstore
  48000. when /\A'(.*)'::hstore\z/
  48001. $1
  48002. # JSON
  48003. when /\A'(.*)'::json\z/
  48004. $1
  48005. # Object identifier types
  48006. when /\A-?\d+\z/
  48007. $1
  48008. else
  48009. # Anything else is blank, some user type, or some function
  48010. # and we can't know the value of that, so return nil.
  48011. nil
  48012. end
  48013. end
  48014. def type_cast(value)
  48015. return if value.nil?
  48016. return super if encoded?
  48017. @oid_type.type_cast value
  48018. end
  48019. private
  48020. def extract_limit(sql_type)
  48021. case sql_type
  48022. when /^bigint/i; 8
  48023. when /^smallint/i; 2
  48024. when /^timestamp/i; nil
  48025. else super
  48026. end
  48027. end
  48028. # Extracts the scale from PostgreSQL-specific data types.
  48029. def extract_scale(sql_type)
  48030. # Money type has a fixed scale of 2.
  48031. sql_type =~ /^money/ ? 2 : super
  48032. end
  48033. # Extracts the precision from PostgreSQL-specific data types.
  48034. def extract_precision(sql_type)
  48035. if sql_type == 'money'
  48036. self.class.money_precision
  48037. elsif sql_type =~ /timestamp/i
  48038. $1.to_i if sql_type =~ /\((\d+)\)/
  48039. else
  48040. super
  48041. end
  48042. end
  48043. # Maps PostgreSQL-specific data types to logical Rails types.
  48044. def simplified_type(field_type)
  48045. case field_type
  48046. # Numeric and monetary types
  48047. when /^(?:real|double precision)$/
  48048. :float
  48049. # Monetary types
  48050. when 'money'
  48051. :decimal
  48052. when 'hstore'
  48053. :hstore
  48054. when 'ltree'
  48055. :ltree
  48056. # Network address types
  48057. when 'inet'
  48058. :inet
  48059. when 'cidr'
  48060. :cidr
  48061. when 'macaddr'
  48062. :macaddr
  48063. # Character types
  48064. when /^(?:character varying|bpchar)(?:\(\d+\))?$/
  48065. :string
  48066. # Binary data types
  48067. when 'bytea'
  48068. :binary
  48069. # Date/time types
  48070. when /^timestamp with(?:out)? time zone$/
  48071. :datetime
  48072. when /^interval(?:|\(\d+\))$/
  48073. :string
  48074. # Geometric types
  48075. when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
  48076. :string
  48077. # Bit strings
  48078. when /^bit(?: varying)?(?:\(\d+\))?$/
  48079. :string
  48080. # XML type
  48081. when 'xml'
  48082. :xml
  48083. # tsvector type
  48084. when 'tsvector'
  48085. :tsvector
  48086. # Arrays
  48087. when /^\D+\[\]$/
  48088. :string
  48089. # Object identifier types
  48090. when 'oid'
  48091. :integer
  48092. # UUID type
  48093. when 'uuid'
  48094. :uuid
  48095. # JSON type
  48096. when 'json'
  48097. :json
  48098. # Small and big integer types
  48099. when /^(?:small|big)int$/
  48100. :integer
  48101. when /(num|date|tstz|ts|int4|int8)range$/
  48102. field_type.to_sym
  48103. # Pass through all types that are not specific to PostgreSQL.
  48104. else
  48105. super
  48106. end
  48107. end
  48108. end
  48109. # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
  48110. #
  48111. # Options:
  48112. #
  48113. # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
  48114. # the default is to connect to localhost.
  48115. # * <tt>:port</tt> - Defaults to 5432.
  48116. # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
  48117. # * <tt>:password</tt> - Password to be used if the server demands password authentication.
  48118. # * <tt>:database</tt> - Defaults to be the same as the user name.
  48119. # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
  48120. # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
  48121. # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
  48122. # <encoding></tt> call on the connection.
  48123. # * <tt>:min_messages</tt> - An optional client min messages that is used in a
  48124. # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
  48125. # * <tt>:variables</tt> - An optional hash of additional parameters that
  48126. # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
  48127. # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
  48128. # defaults to true.
  48129. #
  48130. # Any further options are used as connection parameters to libpq. See
  48131. # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
  48132. # list of parameters.
  48133. #
  48134. # In addition, default connection parameters of libpq can be set per environment variables.
  48135. # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
  48136. class PostgreSQLAdapter < AbstractAdapter
  48137. class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
  48138. attr_accessor :array
  48139. end
  48140. class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
  48141. def xml(*args)
  48142. options = args.extract_options!
  48143. column(args[0], 'xml', options)
  48144. end
  48145. def tsvector(*args)
  48146. options = args.extract_options!
  48147. column(args[0], 'tsvector', options)
  48148. end
  48149. def int4range(name, options = {})
  48150. column(name, 'int4range', options)
  48151. end
  48152. def int8range(name, options = {})
  48153. column(name, 'int8range', options)
  48154. end
  48155. def tsrange(name, options = {})
  48156. column(name, 'tsrange', options)
  48157. end
  48158. def tstzrange(name, options = {})
  48159. column(name, 'tstzrange', options)
  48160. end
  48161. def numrange(name, options = {})
  48162. column(name, 'numrange', options)
  48163. end
  48164. def daterange(name, options = {})
  48165. column(name, 'daterange', options)
  48166. end
  48167. def hstore(name, options = {})
  48168. column(name, 'hstore', options)
  48169. end
  48170. def ltree(name, options = {})
  48171. column(name, 'ltree', options)
  48172. end
  48173. def inet(name, options = {})
  48174. column(name, 'inet', options)
  48175. end
  48176. def cidr(name, options = {})
  48177. column(name, 'cidr', options)
  48178. end
  48179. def macaddr(name, options = {})
  48180. column(name, 'macaddr', options)
  48181. end
  48182. def uuid(name, options = {})
  48183. column(name, 'uuid', options)
  48184. end
  48185. def json(name, options = {})
  48186. column(name, 'json', options)
  48187. end
  48188. def column(name, type = nil, options = {})
  48189. super
  48190. column = self[name]
  48191. column.array = options[:array]
  48192. self
  48193. end
  48194. private
  48195. def new_column_definition(base, name, type)
  48196. definition = ColumnDefinition.new base, name, type
  48197. @columns << definition
  48198. @columns_hash[name] = definition
  48199. definition
  48200. end
  48201. end
  48202. ADAPTER_NAME = 'PostgreSQL'
  48203. NATIVE_DATABASE_TYPES = {
  48204. primary_key: "serial primary key",
  48205. string: { name: "character varying", limit: 255 },
  48206. text: { name: "text" },
  48207. integer: { name: "integer" },
  48208. float: { name: "float" },
  48209. decimal: { name: "decimal" },
  48210. datetime: { name: "timestamp" },
  48211. timestamp: { name: "timestamp" },
  48212. time: { name: "time" },
  48213. date: { name: "date" },
  48214. daterange: { name: "daterange" },
  48215. numrange: { name: "numrange" },
  48216. tsrange: { name: "tsrange" },
  48217. tstzrange: { name: "tstzrange" },
  48218. int4range: { name: "int4range" },
  48219. int8range: { name: "int8range" },
  48220. binary: { name: "bytea" },
  48221. boolean: { name: "boolean" },
  48222. xml: { name: "xml" },
  48223. tsvector: { name: "tsvector" },
  48224. hstore: { name: "hstore" },
  48225. inet: { name: "inet" },
  48226. cidr: { name: "cidr" },
  48227. macaddr: { name: "macaddr" },
  48228. uuid: { name: "uuid" },
  48229. json: { name: "json" },
  48230. ltree: { name: "ltree" }
  48231. }
  48232. include Quoting
  48233. include ReferentialIntegrity
  48234. include SchemaStatements
  48235. include DatabaseStatements
  48236. # Returns 'PostgreSQL' as adapter name for identification purposes.
  48237. def adapter_name
  48238. ADAPTER_NAME
  48239. end
  48240. # Adds `:array` option to the default set provided by the
  48241. # AbstractAdapter
  48242. def prepare_column_options(column, types)
  48243. spec = super
  48244. spec[:array] = 'true' if column.respond_to?(:array) && column.array
  48245. spec
  48246. end
  48247. # Adds `:array` as a valid migration key
  48248. def migration_keys
  48249. super + [:array]
  48250. end
  48251. # Returns +true+, since this connection adapter supports prepared statement
  48252. # caching.
  48253. def supports_statement_cache?
  48254. true
  48255. end
  48256. def supports_index_sort_order?
  48257. true
  48258. end
  48259. def supports_partial_index?
  48260. true
  48261. end
  48262. def supports_transaction_isolation?
  48263. true
  48264. end
  48265. class StatementPool < ConnectionAdapters::StatementPool
  48266. def initialize(connection, max)
  48267. super
  48268. @counter = 0
  48269. @cache = Hash.new { |h,pid| h[pid] = {} }
  48270. end
  48271. def each(&block); cache.each(&block); end
  48272. def key?(key); cache.key?(key); end
  48273. def [](key); cache[key]; end
  48274. def length; cache.length; end
  48275. def next_key
  48276. "a#{@counter + 1}"
  48277. end
  48278. def []=(sql, key)
  48279. while @max <= cache.size
  48280. dealloc(cache.shift.last)
  48281. end
  48282. @counter += 1
  48283. cache[sql] = key
  48284. end
  48285. def clear
  48286. cache.each_value do |stmt_key|
  48287. dealloc stmt_key
  48288. end
  48289. cache.clear
  48290. end
  48291. def delete(sql_key)
  48292. dealloc cache[sql_key]
  48293. cache.delete sql_key
  48294. end
  48295. private
  48296. def cache
  48297. @cache[Process.pid]
  48298. end
  48299. def dealloc(key)
  48300. @connection.query "DEALLOCATE #{key}" if connection_active?
  48301. end
  48302. def connection_active?
  48303. @connection.status == PGconn::CONNECTION_OK
  48304. rescue PGError
  48305. false
  48306. end
  48307. end
  48308. class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
  48309. include Arel::Visitors::BindVisitor
  48310. end
  48311. # Initializes and connects a PostgreSQL adapter.
  48312. def initialize(connection, logger, connection_parameters, config)
  48313. super(connection, logger)
  48314. if config.fetch(:prepared_statements) { true }
  48315. @visitor = Arel::Visitors::PostgreSQL.new self
  48316. else
  48317. @visitor = BindSubstitution.new self
  48318. end
  48319. @connection_parameters, @config = connection_parameters, config
  48320. # @local_tz is initialized as nil to avoid warnings when connect tries to use it
  48321. @local_tz = nil
  48322. @table_alias_length = nil
  48323. connect
  48324. @statements = StatementPool.new @connection,
  48325. config.fetch(:statement_limit) { 1000 }
  48326. if postgresql_version < 80200
  48327. raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
  48328. end
  48329. initialize_type_map
  48330. @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
  48331. @use_insert_returning = @config.key?(:insert_returning) ? @config[:insert_returning] : true
  48332. end
  48333. # Clears the prepared statements cache.
  48334. def clear_cache!
  48335. @statements.clear
  48336. end
  48337. # Is this connection alive and ready for queries?
  48338. def active?
  48339. @connection.query 'SELECT 1'
  48340. true
  48341. rescue PGError
  48342. false
  48343. end
  48344. # Close then reopen the connection.
  48345. def reconnect!
  48346. super
  48347. @connection.reset
  48348. configure_connection
  48349. end
  48350. def reset!
  48351. clear_cache!
  48352. super
  48353. end
  48354. # Disconnects from the database if already connected. Otherwise, this
  48355. # method does nothing.
  48356. def disconnect!
  48357. super
  48358. @connection.close rescue nil
  48359. end
  48360. def native_database_types #:nodoc:
  48361. NATIVE_DATABASE_TYPES
  48362. end
  48363. # Returns true, since this connection adapter supports migrations.
  48364. def supports_migrations?
  48365. true
  48366. end
  48367. # Does PostgreSQL support finding primary key on non-Active Record tables?
  48368. def supports_primary_key? #:nodoc:
  48369. true
  48370. end
  48371. # Enable standard-conforming strings if available.
  48372. def set_standard_conforming_strings
  48373. old, self.client_min_messages = client_min_messages, 'panic'
  48374. execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
  48375. ensure
  48376. self.client_min_messages = old
  48377. end
  48378. def supports_insert_with_returning?
  48379. true
  48380. end
  48381. def supports_ddl_transactions?
  48382. true
  48383. end
  48384. # Returns true, since this connection adapter supports savepoints.
  48385. def supports_savepoints?
  48386. true
  48387. end
  48388. # Returns true.
  48389. def supports_explain?
  48390. true
  48391. end
  48392. # Returns true if pg > 9.2
  48393. def supports_extensions?
  48394. postgresql_version >= 90200
  48395. end
  48396. # Range datatypes weren't introduced until PostgreSQL 9.2
  48397. def supports_ranges?
  48398. postgresql_version >= 90200
  48399. end
  48400. def enable_extension(name)
  48401. exec_query("CREATE EXTENSION IF NOT EXISTS #{name}").tap {
  48402. reload_type_map
  48403. }
  48404. end
  48405. def disable_extension(name)
  48406. exec_query("DROP EXTENSION IF EXISTS #{name} CASCADE").tap {
  48407. reload_type_map
  48408. }
  48409. end
  48410. def extension_enabled?(name)
  48411. if supports_extensions?
  48412. res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)",
  48413. 'SCHEMA'
  48414. res.column_types['exists'].type_cast res.rows.first.first
  48415. end
  48416. end
  48417. def extensions
  48418. if supports_extensions?
  48419. res = exec_query "SELECT extname from pg_extension", "SCHEMA"
  48420. res.rows.map { |r| res.column_types['extname'].type_cast r.first }
  48421. else
  48422. super
  48423. end
  48424. end
  48425. # Returns the configured supported identifier length supported by PostgreSQL
  48426. def table_alias_length
  48427. @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
  48428. end
  48429. def add_column_options!(sql, options)
  48430. if options[:array] || options[:column].try(:array)
  48431. sql << '[]'
  48432. end
  48433. super
  48434. end
  48435. # Set the authorized user for this session
  48436. def session_auth=(user)
  48437. clear_cache!
  48438. exec_query "SET SESSION AUTHORIZATION #{user}"
  48439. end
  48440. module Utils
  48441. extend self
  48442. # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
  48443. # +schema_name+ is nil if not specified in +name+.
  48444. # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
  48445. # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
  48446. #
  48447. # * <tt>table_name</tt>
  48448. # * <tt>"table.name"</tt>
  48449. # * <tt>schema_name.table_name</tt>
  48450. # * <tt>schema_name."table.name"</tt>
  48451. # * <tt>"schema.name"."table name"</tt>
  48452. def extract_schema_and_table(name)
  48453. table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
  48454. [schema, table]
  48455. end
  48456. end
  48457. def use_insert_returning?
  48458. @use_insert_returning
  48459. end
  48460. protected
  48461. # Returns the version of the connected PostgreSQL server.
  48462. def postgresql_version
  48463. @connection.server_version
  48464. end
  48465. # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
  48466. FOREIGN_KEY_VIOLATION = "23503"
  48467. UNIQUE_VIOLATION = "23505"
  48468. def translate_exception(exception, message)
  48469. case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
  48470. when UNIQUE_VIOLATION
  48471. RecordNotUnique.new(message, exception)
  48472. when FOREIGN_KEY_VIOLATION
  48473. InvalidForeignKey.new(message, exception)
  48474. else
  48475. super
  48476. end
  48477. end
  48478. private
  48479. def reload_type_map
  48480. OID::TYPE_MAP.clear
  48481. initialize_type_map
  48482. end
  48483. def initialize_type_map
  48484. result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
  48485. leaves, nodes = result.partition { |row| row['typelem'] == '0' }
  48486. # populate the leaf nodes
  48487. leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
  48488. OID::TYPE_MAP[row['oid'].to_i] = OID::NAMES[row['typname']]
  48489. end
  48490. arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
  48491. # populate composite types
  48492. nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
  48493. vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
  48494. OID::TYPE_MAP[row['oid'].to_i] = vector
  48495. end
  48496. # populate array types
  48497. arrays.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
  48498. array = OID::Array.new OID::TYPE_MAP[row['typelem'].to_i]
  48499. OID::TYPE_MAP[row['oid'].to_i] = array
  48500. end
  48501. end
  48502. FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
  48503. def exec_no_cache(sql, binds)
  48504. @connection.async_exec(sql)
  48505. end
  48506. def exec_cache(sql, binds)
  48507. stmt_key = prepare_statement sql
  48508. # Clear the queue
  48509. @connection.get_last_result
  48510. @connection.send_query_prepared(stmt_key, binds.map { |col, val|
  48511. type_cast(val, col)
  48512. })
  48513. @connection.block
  48514. @connection.get_last_result
  48515. rescue PGError => e
  48516. # Get the PG code for the failure. Annoyingly, the code for
  48517. # prepared statements whose return value may have changed is
  48518. # FEATURE_NOT_SUPPORTED. Check here for more details:
  48519. # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
  48520. begin
  48521. code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
  48522. rescue
  48523. raise e
  48524. end
  48525. if FEATURE_NOT_SUPPORTED == code
  48526. @statements.delete sql_key(sql)
  48527. retry
  48528. else
  48529. raise e
  48530. end
  48531. end
  48532. # Returns the statement identifier for the client side cache
  48533. # of statements
  48534. def sql_key(sql)
  48535. "#{schema_search_path}-#{sql}"
  48536. end
  48537. # Prepare the statement if it hasn't been prepared, return
  48538. # the statement key.
  48539. def prepare_statement(sql)
  48540. sql_key = sql_key(sql)
  48541. unless @statements.key? sql_key
  48542. nextkey = @statements.next_key
  48543. @connection.prepare nextkey, sql
  48544. @statements[sql_key] = nextkey
  48545. end
  48546. @statements[sql_key]
  48547. end
  48548. # The internal PostgreSQL identifier of the money data type.
  48549. MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
  48550. # The internal PostgreSQL identifier of the BYTEA data type.
  48551. BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
  48552. # Connects to a PostgreSQL server and sets up the adapter depending on the
  48553. # connected server's characteristics.
  48554. def connect
  48555. @connection = PGconn.connect(@connection_parameters)
  48556. # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
  48557. # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
  48558. # should know about this but can't detect it there, so deal with it here.
  48559. PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
  48560. configure_connection
  48561. end
  48562. # Configures the encoding, verbosity, schema search path, and time zone of the connection.
  48563. # This is called by #connect and should not be called manually.
  48564. def configure_connection
  48565. if @config[:encoding]
  48566. @connection.set_client_encoding(@config[:encoding])
  48567. end
  48568. self.client_min_messages = @config[:min_messages] || 'warning'
  48569. self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
  48570. # Use standard-conforming strings if available so we don't have to do the E'...' dance.
  48571. set_standard_conforming_strings
  48572. # If using Active Record's time zone support configure the connection to return
  48573. # TIMESTAMP WITH ZONE types in UTC.
  48574. # (SET TIME ZONE does not use an equals sign like other SET variables)
  48575. if ActiveRecord::Base.default_timezone == :utc
  48576. execute("SET time zone 'UTC'", 'SCHEMA')
  48577. elsif @local_tz
  48578. execute("SET time zone '#{@local_tz}'", 'SCHEMA')
  48579. end
  48580. # SET statements from :variables config hash
  48581. # http://www.postgresql.org/docs/8.3/static/sql-set.html
  48582. variables = @config[:variables] || {}
  48583. variables.map do |k, v|
  48584. if v == ':default' || v == :default
  48585. # Sets the value to the global or compile default
  48586. execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA')
  48587. elsif !v.nil?
  48588. execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
  48589. end
  48590. end
  48591. end
  48592. # Returns the current ID of a table's sequence.
  48593. def last_insert_id(sequence_name) #:nodoc:
  48594. Integer(last_insert_id_value(sequence_name))
  48595. end
  48596. def last_insert_id_value(sequence_name)
  48597. last_insert_id_result(sequence_name).rows.first.first
  48598. end
  48599. def last_insert_id_result(sequence_name) #:nodoc:
  48600. exec_query("SELECT currval('#{sequence_name}')", 'SQL')
  48601. end
  48602. # Executes a SELECT query and returns the results, performing any data type
  48603. # conversions that are required to be performed here instead of in PostgreSQLColumn.
  48604. def select(sql, name = nil, binds = [])
  48605. exec_query(sql, name, binds)
  48606. end
  48607. def select_raw(sql, name = nil)
  48608. res = execute(sql, name)
  48609. results = result_as_array(res)
  48610. fields = res.fields
  48611. res.clear
  48612. return fields, results
  48613. end
  48614. # Returns the list of a table's column names, data types, and default values.
  48615. #
  48616. # The underlying query is roughly:
  48617. # SELECT column.name, column.type, default.value
  48618. # FROM column LEFT JOIN default
  48619. # ON column.table_id = default.table_id
  48620. # AND column.num = default.column_num
  48621. # WHERE column.table_id = get_table_id('table_name')
  48622. # AND column.num > 0
  48623. # AND NOT column.is_dropped
  48624. # ORDER BY column.num
  48625. #
  48626. # If the table name is not prefixed with a schema, the database will
  48627. # take the first match from the schema search path.
  48628. #
  48629. # Query implementation notes:
  48630. # - format_type includes the column size constraint, e.g. varchar(50)
  48631. # - ::regclass is a function that gives the id for a table name
  48632. def column_definitions(table_name) #:nodoc:
  48633. exec_query(<<-end_sql, 'SCHEMA').rows
  48634. SELECT a.attname, format_type(a.atttypid, a.atttypmod),
  48635. pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
  48636. FROM pg_attribute a LEFT JOIN pg_attrdef d
  48637. ON a.attrelid = d.adrelid AND a.attnum = d.adnum
  48638. WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
  48639. AND a.attnum > 0 AND NOT a.attisdropped
  48640. ORDER BY a.attnum
  48641. end_sql
  48642. end
  48643. def extract_pg_identifier_from_name(name)
  48644. match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
  48645. if match_data
  48646. rest = name[match_data[0].length, name.length]
  48647. rest = rest[1, rest.length] if rest.start_with? "."
  48648. [match_data[1], (rest.length > 0 ? rest : nil)]
  48649. end
  48650. end
  48651. def extract_table_ref_from_insert_sql(sql)
  48652. sql[/into\s+([^\(]*).*values\s*\(/i]
  48653. $1.strip if $1
  48654. end
  48655. def table_definition
  48656. TableDefinition.new(self)
  48657. end
  48658. end
  48659. end
  48660. end
  48661. module ActiveRecord
  48662. module ConnectionAdapters
  48663. class SchemaCache
  48664. attr_reader :primary_keys, :tables, :version
  48665. attr_accessor :connection
  48666. def initialize(conn)
  48667. @connection = conn
  48668. @columns = {}
  48669. @columns_hash = {}
  48670. @primary_keys = {}
  48671. @tables = {}
  48672. prepare_default_proc
  48673. end
  48674. # A cached lookup for table existence.
  48675. def table_exists?(name)
  48676. return @tables[name] if @tables.key? name
  48677. @tables[name] = connection.table_exists?(name)
  48678. end
  48679. # Add internal cache for table with +table_name+.
  48680. def add(table_name)
  48681. if table_exists?(table_name)
  48682. @primary_keys[table_name]
  48683. @columns[table_name]
  48684. @columns_hash[table_name]
  48685. end
  48686. end
  48687. # Get the columns for a table
  48688. def columns(table = nil)
  48689. if table
  48690. @columns[table]
  48691. else
  48692. @columns
  48693. end
  48694. end
  48695. # Get the columns for a table as a hash, key is the column name
  48696. # value is the column object.
  48697. def columns_hash(table = nil)
  48698. if table
  48699. @columns_hash[table]
  48700. else
  48701. @columns_hash
  48702. end
  48703. end
  48704. # Clears out internal caches
  48705. def clear!
  48706. @columns.clear
  48707. @columns_hash.clear
  48708. @primary_keys.clear
  48709. @tables.clear
  48710. @version = nil
  48711. end
  48712. # Clear out internal caches for table with +table_name+.
  48713. def clear_table_cache!(table_name)
  48714. @columns.delete table_name
  48715. @columns_hash.delete table_name
  48716. @primary_keys.delete table_name
  48717. @tables.delete table_name
  48718. end
  48719. def marshal_dump
  48720. # if we get current version during initialization, it happens stack over flow.
  48721. @version = ActiveRecord::Migrator.current_version
  48722. [@version] + [:@columns, :@columns_hash, :@primary_keys, :@tables].map do |val|
  48723. self.instance_variable_get(val).inject({}) { |h, v| h[v[0]] = v[1]; h }
  48724. end
  48725. end
  48726. def marshal_load(array)
  48727. @version, @columns, @columns_hash, @primary_keys, @tables = array
  48728. prepare_default_proc
  48729. end
  48730. private
  48731. def prepare_default_proc
  48732. @columns.default_proc = Proc.new do |h, table_name|
  48733. h[table_name] = connection.columns(table_name)
  48734. end
  48735. @columns_hash.default_proc = Proc.new do |h, table_name|
  48736. h[table_name] = Hash[columns[table_name].map { |col|
  48737. [col.name, col]
  48738. }]
  48739. end
  48740. @primary_keys.default_proc = Proc.new do |h, table_name|
  48741. h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil
  48742. end
  48743. end
  48744. end
  48745. end
  48746. end
  48747. require 'active_record/connection_adapters/abstract_adapter'
  48748. require 'active_record/connection_adapters/statement_pool'
  48749. require 'arel/visitors/bind_visitor'
  48750. gem 'sqlite3', '~> 1.3.6'
  48751. require 'sqlite3'
  48752. module ActiveRecord
  48753. module ConnectionHandling
  48754. # sqlite3 adapter reuses sqlite_connection.
  48755. def sqlite3_connection(config) # :nodoc:
  48756. # Require database.
  48757. unless config[:database]
  48758. raise ArgumentError, "No database file specified. Missing argument: database"
  48759. end
  48760. # Allow database path relative to Rails.root, but only if
  48761. # the database path is not the special path that tells
  48762. # Sqlite to build a database only in memory.
  48763. if defined?(Rails.root) && ':memory:' != config[:database]
  48764. config[:database] = File.expand_path(config[:database], Rails.root)
  48765. end
  48766. db = SQLite3::Database.new(
  48767. config[:database],
  48768. :results_as_hash => true
  48769. )
  48770. db.busy_timeout(config[:timeout]) if config[:timeout]
  48771. ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
  48772. end
  48773. end
  48774. module ConnectionAdapters #:nodoc:
  48775. class SQLite3Column < Column #:nodoc:
  48776. class << self
  48777. def binary_to_string(value)
  48778. if value.encoding != Encoding::ASCII_8BIT
  48779. value = value.force_encoding(Encoding::ASCII_8BIT)
  48780. end
  48781. value
  48782. end
  48783. end
  48784. end
  48785. # The SQLite3 adapter works SQLite 3.6.16 or newer
  48786. # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
  48787. #
  48788. # Options:
  48789. #
  48790. # * <tt>:database</tt> - Path to the database file.
  48791. class SQLite3Adapter < AbstractAdapter
  48792. class Version
  48793. include Comparable
  48794. def initialize(version_string)
  48795. @version = version_string.split('.').map { |v| v.to_i }
  48796. end
  48797. def <=>(version_string)
  48798. @version <=> version_string.split('.').map { |v| v.to_i }
  48799. end
  48800. end
  48801. class StatementPool < ConnectionAdapters::StatementPool
  48802. def initialize(connection, max)
  48803. super
  48804. @cache = Hash.new { |h,pid| h[pid] = {} }
  48805. end
  48806. def each(&block); cache.each(&block); end
  48807. def key?(key); cache.key?(key); end
  48808. def [](key); cache[key]; end
  48809. def length; cache.length; end
  48810. def []=(sql, key)
  48811. while @max <= cache.size
  48812. dealloc(cache.shift.last[:stmt])
  48813. end
  48814. cache[sql] = key
  48815. end
  48816. def clear
  48817. cache.values.each do |hash|
  48818. dealloc hash[:stmt]
  48819. end
  48820. cache.clear
  48821. end
  48822. private
  48823. def cache
  48824. @cache[$$]
  48825. end
  48826. def dealloc(stmt)
  48827. stmt.close unless stmt.closed?
  48828. end
  48829. end
  48830. class BindSubstitution < Arel::Visitors::SQLite # :nodoc:
  48831. include Arel::Visitors::BindVisitor
  48832. end
  48833. def initialize(connection, logger, config)
  48834. super(connection, logger)
  48835. @active = nil
  48836. @statements = StatementPool.new(@connection,
  48837. config.fetch(:statement_limit) { 1000 })
  48838. @config = config
  48839. if config.fetch(:prepared_statements) { true }
  48840. @visitor = Arel::Visitors::SQLite.new self
  48841. else
  48842. @visitor = BindSubstitution.new self
  48843. end
  48844. end
  48845. def adapter_name #:nodoc:
  48846. 'SQLite'
  48847. end
  48848. # Returns true
  48849. def supports_ddl_transactions?
  48850. true
  48851. end
  48852. # Returns true if SQLite version is '3.6.8' or greater, false otherwise.
  48853. def supports_savepoints?
  48854. sqlite_version >= '3.6.8'
  48855. end
  48856. # Returns true, since this connection adapter supports prepared statement
  48857. # caching.
  48858. def supports_statement_cache?
  48859. true
  48860. end
  48861. # Returns true, since this connection adapter supports migrations.
  48862. def supports_migrations? #:nodoc:
  48863. true
  48864. end
  48865. # Returns true.
  48866. def supports_primary_key? #:nodoc:
  48867. true
  48868. end
  48869. def requires_reloading?
  48870. true
  48871. end
  48872. # Returns true
  48873. def supports_add_column?
  48874. true
  48875. end
  48876. def active?
  48877. @active != false
  48878. end
  48879. # Disconnects from the database if already connected. Otherwise, this
  48880. # method does nothing.
  48881. def disconnect!
  48882. super
  48883. @active = false
  48884. @connection.close rescue nil
  48885. end
  48886. # Clears the prepared statements cache.
  48887. def clear_cache!
  48888. @statements.clear
  48889. end
  48890. # Returns true
  48891. def supports_count_distinct? #:nodoc:
  48892. true
  48893. end
  48894. # Returns true
  48895. def supports_autoincrement? #:nodoc:
  48896. true
  48897. end
  48898. def supports_index_sort_order?
  48899. true
  48900. end
  48901. def native_database_types #:nodoc:
  48902. {
  48903. :primary_key => default_primary_key_type,
  48904. :string => { :name => "varchar", :limit => 255 },
  48905. :text => { :name => "text" },
  48906. :integer => { :name => "integer" },
  48907. :float => { :name => "float" },
  48908. :decimal => { :name => "decimal" },
  48909. :datetime => { :name => "datetime" },
  48910. :timestamp => { :name => "datetime" },
  48911. :time => { :name => "time" },
  48912. :date => { :name => "date" },
  48913. :binary => { :name => "blob" },
  48914. :boolean => { :name => "boolean" }
  48915. }
  48916. end
  48917. # Returns the current database encoding format as a string, eg: 'UTF-8'
  48918. def encoding
  48919. @connection.encoding.to_s
  48920. end
  48921. # Returns true.
  48922. def supports_explain?
  48923. true
  48924. end
  48925. # QUOTING ==================================================
  48926. def quote(value, column = nil)
  48927. if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
  48928. s = column.class.string_to_binary(value).unpack("H*")[0]
  48929. "x'#{s}'"
  48930. else
  48931. super
  48932. end
  48933. end
  48934. def quote_string(s) #:nodoc:
  48935. @connection.class.quote(s)
  48936. end
  48937. def quote_table_name_for_assignment(table, attr)
  48938. quote_column_name(attr)
  48939. end
  48940. def quote_column_name(name) #:nodoc:
  48941. %Q("#{name.to_s.gsub('"', '""')}")
  48942. end
  48943. # Quote date/time values for use in SQL input. Includes microseconds
  48944. # if the value is a Time responding to usec.
  48945. def quoted_date(value) #:nodoc:
  48946. if value.respond_to?(:usec)
  48947. "#{super}.#{sprintf("%06d", value.usec)}"
  48948. else
  48949. super
  48950. end
  48951. end
  48952. def type_cast(value, column) # :nodoc:
  48953. return value.to_f if BigDecimal === value
  48954. return super unless String === value
  48955. return super unless column && value
  48956. value = super
  48957. if column.type == :string && value.encoding == Encoding::ASCII_8BIT
  48958. logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
  48959. value = value.encode Encoding::UTF_8
  48960. end
  48961. value
  48962. end
  48963. # DATABASE STATEMENTS ======================================
  48964. def explain(arel, binds = [])
  48965. sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
  48966. ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
  48967. end
  48968. class ExplainPrettyPrinter
  48969. # Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
  48970. # the output of the SQLite shell:
  48971. #
  48972. # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
  48973. # 0|1|1|SCAN TABLE posts (~100000 rows)
  48974. #
  48975. def pp(result) # :nodoc:
  48976. result.rows.map do |row|
  48977. row.join('|')
  48978. end.join("\n") + "\n"
  48979. end
  48980. end
  48981. def exec_query(sql, name = nil, binds = [])
  48982. log(sql, name, binds) do
  48983. # Don't cache statements without bind values
  48984. if binds.empty?
  48985. stmt = @connection.prepare(sql)
  48986. cols = stmt.columns
  48987. records = stmt.to_a
  48988. stmt.close
  48989. stmt = records
  48990. else
  48991. cache = @statements[sql] ||= {
  48992. :stmt => @connection.prepare(sql)
  48993. }
  48994. stmt = cache[:stmt]
  48995. cols = cache[:cols] ||= stmt.columns
  48996. stmt.reset!
  48997. stmt.bind_params binds.map { |col, val|
  48998. type_cast(val, col)
  48999. }
  49000. end
  49001. ActiveRecord::Result.new(cols, stmt.to_a)
  49002. end
  49003. end
  49004. def exec_delete(sql, name = 'SQL', binds = [])
  49005. exec_query(sql, name, binds)
  49006. @connection.changes
  49007. end
  49008. alias :exec_update :exec_delete
  49009. def last_inserted_id(result)
  49010. @connection.last_insert_row_id
  49011. end
  49012. def execute(sql, name = nil) #:nodoc:
  49013. log(sql, name) { @connection.execute(sql) }
  49014. end
  49015. def update_sql(sql, name = nil) #:nodoc:
  49016. super
  49017. @connection.changes
  49018. end
  49019. def delete_sql(sql, name = nil) #:nodoc:
  49020. sql += " WHERE 1=1" unless sql =~ /WHERE/i
  49021. super sql, name
  49022. end
  49023. def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
  49024. super
  49025. id_value || @connection.last_insert_row_id
  49026. end
  49027. alias :create :insert_sql
  49028. def select_rows(sql, name = nil)
  49029. exec_query(sql, name).rows
  49030. end
  49031. def create_savepoint
  49032. execute("SAVEPOINT #{current_savepoint_name}")
  49033. end
  49034. def rollback_to_savepoint
  49035. execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
  49036. end
  49037. def release_savepoint
  49038. execute("RELEASE SAVEPOINT #{current_savepoint_name}")
  49039. end
  49040. def begin_db_transaction #:nodoc:
  49041. log('begin transaction',nil) { @connection.transaction }
  49042. end
  49043. def commit_db_transaction #:nodoc:
  49044. log('commit transaction',nil) { @connection.commit }
  49045. end
  49046. def rollback_db_transaction #:nodoc:
  49047. log('rollback transaction',nil) { @connection.rollback }
  49048. end
  49049. # SCHEMA STATEMENTS ========================================
  49050. def tables(name = nil, table_name = nil) #:nodoc:
  49051. sql = <<-SQL
  49052. SELECT name
  49053. FROM sqlite_master
  49054. WHERE type = 'table' AND NOT name = 'sqlite_sequence'
  49055. SQL
  49056. sql << " AND name = #{quote_table_name(table_name)}" if table_name
  49057. exec_query(sql, 'SCHEMA').map do |row|
  49058. row['name']
  49059. end
  49060. end
  49061. def table_exists?(table_name)
  49062. table_name && tables(nil, table_name).any?
  49063. end
  49064. # Returns an array of +SQLite3Column+ objects for the table specified by +table_name+.
  49065. def columns(table_name) #:nodoc:
  49066. table_structure(table_name).map do |field|
  49067. case field["dflt_value"]
  49068. when /^null$/i
  49069. field["dflt_value"] = nil
  49070. when /^'(.*)'$/m
  49071. field["dflt_value"] = $1.gsub("''", "'")
  49072. when /^"(.*)"$/m
  49073. field["dflt_value"] = $1.gsub('""', '"')
  49074. end
  49075. SQLite3Column.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
  49076. end
  49077. end
  49078. # Returns an array of indexes for the given table.
  49079. def indexes(table_name, name = nil) #:nodoc:
  49080. exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
  49081. IndexDefinition.new(
  49082. table_name,
  49083. row['name'],
  49084. row['unique'] != 0,
  49085. exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
  49086. col['name']
  49087. })
  49088. end
  49089. end
  49090. def primary_key(table_name) #:nodoc:
  49091. column = table_structure(table_name).find { |field|
  49092. field['pk'] == 1
  49093. }
  49094. column && column['name']
  49095. end
  49096. def remove_index!(table_name, index_name) #:nodoc:
  49097. exec_query "DROP INDEX #{quote_column_name(index_name)}"
  49098. end
  49099. # Renames a table.
  49100. #
  49101. # Example:
  49102. # rename_table('octopuses', 'octopi')
  49103. def rename_table(name, new_name)
  49104. exec_query "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
  49105. end
  49106. # See: http://www.sqlite.org/lang_altertable.html
  49107. # SQLite has an additional restriction on the ALTER TABLE statement
  49108. def valid_alter_table_options( type, options)
  49109. type.to_sym != :primary_key
  49110. end
  49111. def add_column(table_name, column_name, type, options = {}) #:nodoc:
  49112. if supports_add_column? && valid_alter_table_options( type, options )
  49113. super(table_name, column_name, type, options)
  49114. else
  49115. alter_table(table_name) do |definition|
  49116. definition.column(column_name, type, options)
  49117. end
  49118. end
  49119. end
  49120. def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
  49121. alter_table(table_name) do |definition|
  49122. definition.columns.delete(definition[column_name])
  49123. end
  49124. end
  49125. def change_column_default(table_name, column_name, default) #:nodoc:
  49126. alter_table(table_name) do |definition|
  49127. definition[column_name].default = default
  49128. end
  49129. end
  49130. def change_column_null(table_name, column_name, null, default = nil)
  49131. unless null || default.nil?
  49132. exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  49133. end
  49134. alter_table(table_name) do |definition|
  49135. definition[column_name].null = null
  49136. end
  49137. end
  49138. def change_column(table_name, column_name, type, options = {}) #:nodoc:
  49139. alter_table(table_name) do |definition|
  49140. include_default = options_include_default?(options)
  49141. definition[column_name].instance_eval do
  49142. self.type = type
  49143. self.limit = options[:limit] if options.include?(:limit)
  49144. self.default = options[:default] if include_default
  49145. self.null = options[:null] if options.include?(:null)
  49146. self.precision = options[:precision] if options.include?(:precision)
  49147. self.scale = options[:scale] if options.include?(:scale)
  49148. end
  49149. end
  49150. end
  49151. def rename_column(table_name, column_name, new_column_name) #:nodoc:
  49152. unless columns(table_name).detect{|c| c.name == column_name.to_s }
  49153. raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
  49154. end
  49155. alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
  49156. end
  49157. protected
  49158. def select(sql, name = nil, binds = []) #:nodoc:
  49159. exec_query(sql, name, binds)
  49160. end
  49161. def table_structure(table_name)
  49162. structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
  49163. raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
  49164. structure
  49165. end
  49166. def alter_table(table_name, options = {}) #:nodoc:
  49167. altered_table_name = "altered_#{table_name}"
  49168. caller = lambda {|definition| yield definition if block_given?}
  49169. transaction do
  49170. move_table(table_name, altered_table_name,
  49171. options.merge(:temporary => true))
  49172. move_table(altered_table_name, table_name, &caller)
  49173. end
  49174. end
  49175. def move_table(from, to, options = {}, &block) #:nodoc:
  49176. copy_table(from, to, options, &block)
  49177. drop_table(from)
  49178. end
  49179. def copy_table(from, to, options = {}) #:nodoc:
  49180. from_primary_key = primary_key(from)
  49181. options[:id] = false
  49182. create_table(to, options) do |definition|
  49183. @definition = definition
  49184. @definition.primary_key(from_primary_key) if from_primary_key.present?
  49185. columns(from).each do |column|
  49186. column_name = options[:rename] ?
  49187. (options[:rename][column.name] ||
  49188. options[:rename][column.name.to_sym] ||
  49189. column.name) : column.name
  49190. next if column_name == from_primary_key
  49191. @definition.column(column_name, column.type,
  49192. :limit => column.limit, :default => column.default,
  49193. :precision => column.precision, :scale => column.scale,
  49194. :null => column.null)
  49195. end
  49196. yield @definition if block_given?
  49197. end
  49198. copy_table_indexes(from, to, options[:rename] || {})
  49199. copy_table_contents(from, to,
  49200. @definition.columns.map {|column| column.name},
  49201. options[:rename] || {})
  49202. end
  49203. def copy_table_indexes(from, to, rename = {}) #:nodoc:
  49204. indexes(from).each do |index|
  49205. name = index.name
  49206. if to == "altered_#{from}"
  49207. name = "temp_#{name}"
  49208. elsif from == "altered_#{to}"
  49209. name = name[5..-1]
  49210. end
  49211. to_column_names = columns(to).map { |c| c.name }
  49212. columns = index.columns.map {|c| rename[c] || c }.select do |column|
  49213. to_column_names.include?(column)
  49214. end
  49215. unless columns.empty?
  49216. # index name can't be the same
  49217. opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_") }
  49218. opts[:unique] = true if index.unique
  49219. add_index(to, columns, opts)
  49220. end
  49221. end
  49222. end
  49223. def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
  49224. column_mappings = Hash[columns.map {|name| [name, name]}]
  49225. rename.each { |a| column_mappings[a.last] = a.first }
  49226. from_columns = columns(from).collect {|col| col.name}
  49227. columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
  49228. quoted_columns = columns.map { |col| quote_column_name(col) } * ','
  49229. quoted_to = quote_table_name(to)
  49230. exec_query("SELECT * FROM #{quote_table_name(from)}").each do |row|
  49231. sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
  49232. sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
  49233. sql << ')'
  49234. exec_query sql
  49235. end
  49236. end
  49237. def sqlite_version
  49238. @sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
  49239. end
  49240. def default_primary_key_type
  49241. if supports_autoincrement?
  49242. 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'
  49243. else
  49244. 'INTEGER PRIMARY KEY NOT NULL'
  49245. end
  49246. end
  49247. def translate_exception(exception, message)
  49248. case exception.message
  49249. when /column(s)? .* (is|are) not unique/
  49250. RecordNotUnique.new(message, exception)
  49251. else
  49252. super
  49253. end
  49254. end
  49255. end
  49256. end
  49257. end
  49258. module ActiveRecord
  49259. module ConnectionAdapters
  49260. class StatementPool
  49261. include Enumerable
  49262. def initialize(connection, max = 1000)
  49263. @connection = connection
  49264. @max = max
  49265. end
  49266. def each
  49267. raise NotImplementedError
  49268. end
  49269. def key?(key)
  49270. raise NotImplementedError
  49271. end
  49272. def [](key)
  49273. raise NotImplementedError
  49274. end
  49275. def length
  49276. raise NotImplementedError
  49277. end
  49278. def []=(sql, key)
  49279. raise NotImplementedError
  49280. end
  49281. def clear
  49282. raise NotImplementedError
  49283. end
  49284. def delete(key)
  49285. raise NotImplementedError
  49286. end
  49287. end
  49288. end
  49289. end
  49290. module ActiveRecord
  49291. module ConnectionHandling
  49292. # Establishes the connection to the database. Accepts a hash as input where
  49293. # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
  49294. # example for regular databases (MySQL, Postgresql, etc):
  49295. #
  49296. # ActiveRecord::Base.establish_connection(
  49297. # adapter: "mysql",
  49298. # host: "localhost",
  49299. # username: "myuser",
  49300. # password: "mypass",
  49301. # database: "somedatabase"
  49302. # )
  49303. #
  49304. # Example for SQLite database:
  49305. #
  49306. # ActiveRecord::Base.establish_connection(
  49307. # adapter: "sqlite",
  49308. # database: "path/to/dbfile"
  49309. # )
  49310. #
  49311. # Also accepts keys as strings (for parsing from YAML for example):
  49312. #
  49313. # ActiveRecord::Base.establish_connection(
  49314. # "adapter" => "sqlite",
  49315. # "database" => "path/to/dbfile"
  49316. # )
  49317. #
  49318. # Or a URL:
  49319. #
  49320. # ActiveRecord::Base.establish_connection(
  49321. # "postgres://myuser:mypass@localhost/somedatabase"
  49322. # )
  49323. #
  49324. # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
  49325. # may be returned on an error.
  49326. def establish_connection(spec = ENV["DATABASE_URL"])
  49327. resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new spec, configurations
  49328. spec = resolver.spec
  49329. unless respond_to?(spec.adapter_method)
  49330. raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
  49331. end
  49332. remove_connection
  49333. connection_handler.establish_connection self, spec
  49334. end
  49335. # Returns the connection currently associated with the class. This can
  49336. # also be used to "borrow" the connection to do database work unrelated
  49337. # to any of the specific Active Records.
  49338. def connection
  49339. retrieve_connection
  49340. end
  49341. def connection_id
  49342. Thread.current['ActiveRecord::Base.connection_id']
  49343. end
  49344. def connection_id=(connection_id)
  49345. Thread.current['ActiveRecord::Base.connection_id'] = connection_id
  49346. end
  49347. # Returns the configuration of the associated connection as a hash:
  49348. #
  49349. # ActiveRecord::Base.connection_config
  49350. # # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}
  49351. #
  49352. # Please use only for reading.
  49353. def connection_config
  49354. connection_pool.spec.config
  49355. end
  49356. def connection_pool
  49357. connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
  49358. end
  49359. def retrieve_connection
  49360. connection_handler.retrieve_connection(self)
  49361. end
  49362. # Returns true if Active Record is connected.
  49363. def connected?
  49364. connection_handler.connected?(self)
  49365. end
  49366. def remove_connection(klass = self)
  49367. connection_handler.remove_connection(klass)
  49368. end
  49369. def clear_cache! # :nodoc:
  49370. connection.schema_cache.clear!
  49371. end
  49372. delegate :clear_active_connections!, :clear_reloadable_connections!,
  49373. :clear_all_connections!, :to => :connection_handler
  49374. end
  49375. end
  49376. require 'active_support/core_ext/hash/indifferent_access'
  49377. require 'active_support/core_ext/object/duplicable'
  49378. require 'thread'
  49379. module ActiveRecord
  49380. module Core
  49381. extend ActiveSupport::Concern
  49382. included do
  49383. ##
  49384. # :singleton-method:
  49385. #
  49386. # Accepts a logger conforming to the interface of Log4r which is then
  49387. # passed on to any new database connections made and which can be
  49388. # retrieved on both a class and instance level by calling +logger+.
  49389. mattr_accessor :logger, instance_writer: false
  49390. ##
  49391. # :singleton-method:
  49392. # Contains the database configuration - as is typically stored in config/database.yml -
  49393. # as a Hash.
  49394. #
  49395. # For example, the following database.yml...
  49396. #
  49397. # development:
  49398. # adapter: sqlite3
  49399. # database: db/development.sqlite3
  49400. #
  49401. # production:
  49402. # adapter: sqlite3
  49403. # database: db/production.sqlite3
  49404. #
  49405. # ...would result in ActiveRecord::Base.configurations to look like this:
  49406. #
  49407. # {
  49408. # 'development' => {
  49409. # 'adapter' => 'sqlite3',
  49410. # 'database' => 'db/development.sqlite3'
  49411. # },
  49412. # 'production' => {
  49413. # 'adapter' => 'sqlite3',
  49414. # 'database' => 'db/production.sqlite3'
  49415. # }
  49416. # }
  49417. mattr_accessor :configurations, instance_writer: false
  49418. self.configurations = {}
  49419. ##
  49420. # :singleton-method:
  49421. # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
  49422. # dates and times from the database. This is set to :utc by default.
  49423. mattr_accessor :default_timezone, instance_writer: false
  49424. self.default_timezone = :utc
  49425. ##
  49426. # :singleton-method:
  49427. # Specifies the format to use when dumping the database schema with Rails'
  49428. # Rakefile. If :sql, the schema is dumped as (potentially database-
  49429. # specific) SQL statements. If :ruby, the schema is dumped as an
  49430. # ActiveRecord::Schema file which can be loaded into any database that
  49431. # supports migrations. Use :ruby if you want to have different database
  49432. # adapters for, e.g., your development and test environments.
  49433. mattr_accessor :schema_format, instance_writer: false
  49434. self.schema_format = :ruby
  49435. ##
  49436. # :singleton-method:
  49437. # Specify whether or not to use timestamps for migration versions
  49438. mattr_accessor :timestamped_migrations, instance_writer: false
  49439. self.timestamped_migrations = true
  49440. class_attribute :connection_handler, instance_writer: false
  49441. self.connection_handler = ConnectionAdapters::ConnectionHandler.new
  49442. end
  49443. module ClassMethods
  49444. def inherited(child_class) #:nodoc:
  49445. child_class.initialize_generated_modules
  49446. super
  49447. end
  49448. def initialize_generated_modules
  49449. @attribute_methods_mutex = Mutex.new
  49450. # force attribute methods to be higher in inheritance hierarchy than other generated methods
  49451. generated_attribute_methods.const_set(:AttrNames, Module.new {
  49452. def self.const_missing(name)
  49453. const_set(name, [name.to_s.sub(/ATTR_/, '')].pack('h*').freeze)
  49454. end
  49455. })
  49456. generated_feature_methods
  49457. end
  49458. def generated_feature_methods
  49459. @generated_feature_methods ||= begin
  49460. mod = const_set(:GeneratedFeatureMethods, Module.new)
  49461. include mod
  49462. mod
  49463. end
  49464. end
  49465. # Returns a string like 'Post(id:integer, title:string, body:text)'
  49466. def inspect
  49467. if self == Base
  49468. super
  49469. elsif abstract_class?
  49470. "#{super}(abstract)"
  49471. elsif table_exists?
  49472. attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
  49473. "#{super}(#{attr_list})"
  49474. else
  49475. "#{super}(Table doesn't exist)"
  49476. end
  49477. end
  49478. # Overwrite the default class equality method to provide support for association proxies.
  49479. def ===(object)
  49480. object.is_a?(self)
  49481. end
  49482. # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
  49483. #
  49484. # class Post < ActiveRecord::Base
  49485. # scope :published_and_commented, published.and(self.arel_table[:comments_count].gt(0))
  49486. # end
  49487. def arel_table
  49488. @arel_table ||= Arel::Table.new(table_name, arel_engine)
  49489. end
  49490. # Returns the Arel engine.
  49491. def arel_engine
  49492. @arel_engine ||= begin
  49493. if Base == self || connection_handler.retrieve_connection_pool(self)
  49494. self
  49495. else
  49496. superclass.arel_engine
  49497. end
  49498. end
  49499. end
  49500. private
  49501. def relation #:nodoc:
  49502. relation = Relation.new(self, arel_table)
  49503. if finder_needs_type_condition?
  49504. relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
  49505. else
  49506. relation
  49507. end
  49508. end
  49509. end
  49510. # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
  49511. # attributes but not yet saved (pass a hash with key names matching the associated table column names).
  49512. # In both instances, valid attribute keys are determined by the column names of the associated table --
  49513. # hence you can't have attributes that aren't part of the table columns.
  49514. #
  49515. # ==== Example:
  49516. # # Instantiates a single new object
  49517. # User.new(first_name: 'Jamie')
  49518. def initialize(attributes = nil)
  49519. defaults = self.class.column_defaults.dup
  49520. defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
  49521. @attributes = self.class.initialize_attributes(defaults)
  49522. @columns_hash = self.class.column_types.dup
  49523. init_internals
  49524. ensure_proper_type
  49525. populate_with_current_scope_attributes
  49526. assign_attributes(attributes) if attributes
  49527. yield self if block_given?
  49528. run_callbacks :initialize unless _initialize_callbacks.empty?
  49529. end
  49530. # Initialize an empty model object from +coder+. +coder+ must contain
  49531. # the attributes necessary for initializing an empty model object. For
  49532. # example:
  49533. #
  49534. # class Post < ActiveRecord::Base
  49535. # end
  49536. #
  49537. # post = Post.allocate
  49538. # post.init_with('attributes' => { 'title' => 'hello world' })
  49539. # post.title # => 'hello world'
  49540. def init_with(coder)
  49541. @attributes = self.class.initialize_attributes(coder['attributes'])
  49542. @columns_hash = self.class.column_types.merge(coder['column_types'] || {})
  49543. init_internals
  49544. @new_record = false
  49545. run_callbacks :find
  49546. run_callbacks :initialize
  49547. self
  49548. end
  49549. ##
  49550. # :method: clone
  49551. # Identical to Ruby's clone method. This is a "shallow" copy. Be warned that your attributes are not copied.
  49552. # That means that modifying attributes of the clone will modify the original, since they will both point to the
  49553. # same attributes hash. If you need a copy of your attributes hash, please use the #dup method.
  49554. #
  49555. # user = User.first
  49556. # new_user = user.clone
  49557. # user.name # => "Bob"
  49558. # new_user.name = "Joe"
  49559. # user.name # => "Joe"
  49560. #
  49561. # user.object_id == new_user.object_id # => false
  49562. # user.name.object_id == new_user.name.object_id # => true
  49563. #
  49564. # user.name.object_id == user.dup.name.object_id # => false
  49565. ##
  49566. # :method: dup
  49567. # Duped objects have no id assigned and are treated as new records. Note
  49568. # that this is a "shallow" copy as it copies the object's attributes
  49569. # only, not its associations. The extent of a "deep" copy is application
  49570. # specific and is therefore left to the application to implement according
  49571. # to its need.
  49572. # The dup method does not preserve the timestamps (created|updated)_(at|on).
  49573. ##
  49574. def initialize_dup(other) # :nodoc:
  49575. cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
  49576. self.class.initialize_attributes(cloned_attributes, :serialized => false)
  49577. @attributes = cloned_attributes
  49578. @attributes[self.class.primary_key] = nil
  49579. run_callbacks(:initialize) unless _initialize_callbacks.empty?
  49580. @changed_attributes = {}
  49581. self.class.column_defaults.each do |attr, orig_value|
  49582. @changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
  49583. end
  49584. @aggregation_cache = {}
  49585. @association_cache = {}
  49586. @attributes_cache = {}
  49587. @new_record = true
  49588. ensure_proper_type
  49589. populate_with_current_scope_attributes
  49590. super
  49591. end
  49592. # Populate +coder+ with attributes about this record that should be
  49593. # serialized. The structure of +coder+ defined in this method is
  49594. # guaranteed to match the structure of +coder+ passed to the +init_with+
  49595. # method.
  49596. #
  49597. # Example:
  49598. #
  49599. # class Post < ActiveRecord::Base
  49600. # end
  49601. # coder = {}
  49602. # Post.new.encode_with(coder)
  49603. # coder # => {"attributes" => {"id" => nil, ... }}
  49604. def encode_with(coder)
  49605. coder['attributes'] = attributes
  49606. end
  49607. # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
  49608. # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
  49609. #
  49610. # Note that new records are different from any other record by definition, unless the
  49611. # other record is the receiver itself. Besides, if you fetch existing records with
  49612. # +select+ and leave the ID out, you're on your own, this predicate will return false.
  49613. #
  49614. # Note also that destroying a record preserves its ID in the model instance, so deleted
  49615. # models are still comparable.
  49616. def ==(comparison_object)
  49617. super ||
  49618. comparison_object.instance_of?(self.class) &&
  49619. id.present? &&
  49620. comparison_object.id == id
  49621. end
  49622. alias :eql? :==
  49623. # Delegates to id in order to allow two records of the same type and id to work with something like:
  49624. # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
  49625. def hash
  49626. id.hash
  49627. end
  49628. # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
  49629. def freeze
  49630. @attributes.freeze
  49631. self
  49632. end
  49633. # Returns +true+ if the attributes hash has been frozen.
  49634. def frozen?
  49635. @attributes.frozen?
  49636. end
  49637. # Allows sort on objects
  49638. def <=>(other_object)
  49639. if other_object.is_a?(self.class)
  49640. self.to_key <=> other_object.to_key
  49641. end
  49642. end
  49643. # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
  49644. # attributes will be marked as read only since they cannot be saved.
  49645. def readonly?
  49646. @readonly
  49647. end
  49648. # Marks this record as read only.
  49649. def readonly!
  49650. @readonly = true
  49651. end
  49652. # Returns the connection currently associated with the class. This can
  49653. # also be used to "borrow" the connection to do database work that isn't
  49654. # easily done without going straight to SQL.
  49655. def connection
  49656. self.class.connection
  49657. end
  49658. # Returns the contents of the record as a nicely formatted string.
  49659. def inspect
  49660. inspection = if @attributes
  49661. self.class.column_names.collect { |name|
  49662. if has_attribute?(name)
  49663. "#{name}: #{attribute_for_inspect(name)}"
  49664. end
  49665. }.compact.join(", ")
  49666. else
  49667. "not initialized"
  49668. end
  49669. "#<#{self.class} #{inspection}>"
  49670. end
  49671. # Returns a hash of the given methods with their names as keys and returned values as values.
  49672. def slice(*methods)
  49673. Hash[methods.map { |method| [method, public_send(method)] }].with_indifferent_access
  49674. end
  49675. private
  49676. # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
  49677. # of the array, and then rescues from the possible NoMethodError. If those elements are
  49678. # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
  49679. # which significantly impacts upon performance.
  49680. #
  49681. # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
  49682. #
  49683. # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
  49684. def to_ary # :nodoc:
  49685. nil
  49686. end
  49687. def init_internals
  49688. pk = self.class.primary_key
  49689. @attributes[pk] = nil unless @attributes.key?(pk)
  49690. @aggregation_cache = {}
  49691. @association_cache = {}
  49692. @attributes_cache = {}
  49693. @previously_changed = {}
  49694. @changed_attributes = {}
  49695. @readonly = false
  49696. @destroyed = false
  49697. @marked_for_destruction = false
  49698. @new_record = true
  49699. @txn = nil
  49700. @_start_transaction_state = {}
  49701. @transaction = nil
  49702. end
  49703. end
  49704. end
  49705. module ActiveRecord
  49706. # = Active Record Counter Cache
  49707. module CounterCache
  49708. extend ActiveSupport::Concern
  49709. module ClassMethods
  49710. # Resets one or more counter caches to their correct value using an SQL
  49711. # count query. This is useful when adding new counter caches, or if the
  49712. # counter has been corrupted or modified directly by SQL.
  49713. #
  49714. # ==== Parameters
  49715. #
  49716. # * +id+ - The id of the object you wish to reset a counter on.
  49717. # * +counters+ - One or more counter names to reset
  49718. #
  49719. # ==== Examples
  49720. #
  49721. # # For Post with id #1 records reset the comments_count
  49722. # Post.reset_counters(1, :comments)
  49723. def reset_counters(id, *counters)
  49724. object = find(id)
  49725. counters.each do |association|
  49726. has_many_association = reflect_on_association(association.to_sym)
  49727. if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
  49728. has_many_association = has_many_association.through_reflection
  49729. end
  49730. foreign_key = has_many_association.foreign_key.to_s
  49731. child_class = has_many_association.klass
  49732. belongs_to = child_class.reflect_on_all_associations(:belongs_to)
  49733. reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
  49734. counter_name = reflection.counter_cache_column
  49735. stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
  49736. arel_table[counter_name] => object.send(association).count
  49737. })
  49738. connection.update stmt
  49739. end
  49740. return true
  49741. end
  49742. # A generic "counter updater" implementation, intended primarily to be
  49743. # used by increment_counter and decrement_counter, but which may also
  49744. # be useful on its own. It simply does a direct SQL update for the record
  49745. # with the given ID, altering the given hash of counters by the amount
  49746. # given by the corresponding value:
  49747. #
  49748. # ==== Parameters
  49749. #
  49750. # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
  49751. # * +counters+ - An Array of Hashes containing the names of the fields
  49752. # to update as keys and the amount to update the field by as values.
  49753. #
  49754. # ==== Examples
  49755. #
  49756. # # For the Post with id of 5, decrement the comment_count by 1, and
  49757. # # increment the action_count by 1
  49758. # Post.update_counters 5, comment_count: -1, action_count: 1
  49759. # # Executes the following SQL:
  49760. # # UPDATE posts
  49761. # # SET comment_count = COALESCE(comment_count, 0) - 1,
  49762. # # action_count = COALESCE(action_count, 0) + 1
  49763. # # WHERE id = 5
  49764. #
  49765. # # For the Posts with id of 10 and 15, increment the comment_count by 1
  49766. # Post.update_counters [10, 15], comment_count: 1
  49767. # # Executes the following SQL:
  49768. # # UPDATE posts
  49769. # # SET comment_count = COALESCE(comment_count, 0) + 1
  49770. # # WHERE id IN (10, 15)
  49771. def update_counters(id, counters)
  49772. updates = counters.map do |counter_name, value|
  49773. operator = value < 0 ? '-' : '+'
  49774. quoted_column = connection.quote_column_name(counter_name)
  49775. "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
  49776. end
  49777. where(primary_key => id).update_all updates.join(', ')
  49778. end
  49779. # Increment a numeric field by one, via a direct SQL update.
  49780. #
  49781. # This method is used primarily for maintaining counter_cache columns used to
  49782. # store aggregate values. For example, a DiscussionBoard may cache posts_count
  49783. # and comments_count to avoid running an SQL query to calculate the number of
  49784. # posts and comments there are each time it is displayed.
  49785. #
  49786. # ==== Parameters
  49787. #
  49788. # * +counter_name+ - The name of the field that should be incremented.
  49789. # * +id+ - The id of the object that should be incremented or an Array of ids.
  49790. #
  49791. # ==== Examples
  49792. #
  49793. # # Increment the post_count column for the record with an id of 5
  49794. # DiscussionBoard.increment_counter(:post_count, 5)
  49795. def increment_counter(counter_name, id)
  49796. update_counters(id, counter_name => 1)
  49797. end
  49798. # Decrement a numeric field by one, via a direct SQL update.
  49799. #
  49800. # This works the same as increment_counter but reduces the column value by
  49801. # 1 instead of increasing it.
  49802. #
  49803. # ==== Parameters
  49804. #
  49805. # * +counter_name+ - The name of the field that should be decremented.
  49806. # * +id+ - The id of the object that should be decremented or an Array of ids.
  49807. #
  49808. # ==== Examples
  49809. #
  49810. # # Decrement the post_count column for the record with an id of 5
  49811. # DiscussionBoard.decrement_counter(:post_count, 5)
  49812. def decrement_counter(counter_name, id)
  49813. update_counters(id, counter_name => -1)
  49814. end
  49815. end
  49816. end
  49817. end
  49818. module ActiveRecord
  49819. module DynamicMatchers #:nodoc:
  49820. # This code in this file seems to have a lot of indirection, but the indirection
  49821. # is there to provide extension points for the activerecord-deprecated_finders
  49822. # gem. When we stop supporting activerecord-deprecated_finders (from Rails 5),
  49823. # then we can remove the indirection.
  49824. def respond_to?(name, include_private = false)
  49825. match = Method.match(self, name)
  49826. match && match.valid? || super
  49827. end
  49828. private
  49829. def method_missing(name, *arguments, &block)
  49830. match = Method.match(self, name)
  49831. if match && match.valid?
  49832. match.define
  49833. send(name, *arguments, &block)
  49834. else
  49835. super
  49836. end
  49837. end
  49838. class Method
  49839. @matchers = []
  49840. class << self
  49841. attr_reader :matchers
  49842. def match(model, name)
  49843. klass = matchers.find { |k| name =~ k.pattern }
  49844. klass.new(model, name) if klass
  49845. end
  49846. def pattern
  49847. /^#{prefix}_([_a-zA-Z]\w*)#{suffix}$/
  49848. end
  49849. def prefix
  49850. raise NotImplementedError
  49851. end
  49852. def suffix
  49853. ''
  49854. end
  49855. end
  49856. attr_reader :model, :name, :attribute_names
  49857. def initialize(model, name)
  49858. @model = model
  49859. @name = name.to_s
  49860. @attribute_names = @name.match(self.class.pattern)[1].split('_and_')
  49861. @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
  49862. end
  49863. def valid?
  49864. attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
  49865. end
  49866. def define
  49867. model.class_eval <<-CODE, __FILE__, __LINE__ + 1
  49868. def self.#{name}(#{signature})
  49869. #{body}
  49870. end
  49871. CODE
  49872. end
  49873. def body
  49874. raise NotImplementedError
  49875. end
  49876. end
  49877. module Finder
  49878. # Extended in activerecord-deprecated_finders
  49879. def body
  49880. result
  49881. end
  49882. # Extended in activerecord-deprecated_finders
  49883. def result
  49884. "#{finder}(#{attributes_hash})"
  49885. end
  49886. # Extended in activerecord-deprecated_finders
  49887. def signature
  49888. attribute_names.join(', ')
  49889. end
  49890. def attributes_hash
  49891. "{" + attribute_names.map { |name| ":#{name} => #{name}" }.join(',') + "}"
  49892. end
  49893. def finder
  49894. raise NotImplementedError
  49895. end
  49896. end
  49897. class FindBy < Method
  49898. Method.matchers << self
  49899. include Finder
  49900. def self.prefix
  49901. "find_by"
  49902. end
  49903. def finder
  49904. "find_by"
  49905. end
  49906. end
  49907. class FindByBang < Method
  49908. Method.matchers << self
  49909. include Finder
  49910. def self.prefix
  49911. "find_by"
  49912. end
  49913. def self.suffix
  49914. "!"
  49915. end
  49916. def finder
  49917. "find_by!"
  49918. end
  49919. end
  49920. end
  49921. end
  49922. module ActiveRecord
  49923. # = Active Record Errors
  49924. #
  49925. # Generic Active Record exception class.
  49926. class ActiveRecordError < StandardError
  49927. end
  49928. # Raised when the single-table inheritance mechanism fails to locate the subclass
  49929. # (for example due to improper usage of column that +inheritance_column+ points to).
  49930. class SubclassNotFound < ActiveRecordError #:nodoc:
  49931. end
  49932. # Raised when an object assigned to an association has an incorrect type.
  49933. #
  49934. # class Ticket < ActiveRecord::Base
  49935. # has_many :patches
  49936. # end
  49937. #
  49938. # class Patch < ActiveRecord::Base
  49939. # belongs_to :ticket
  49940. # end
  49941. #
  49942. # # Comments are not patches, this assignment raises AssociationTypeMismatch.
  49943. # @ticket.patches << Comment.new(content: "Please attach tests to your patch.")
  49944. class AssociationTypeMismatch < ActiveRecordError
  49945. end
  49946. # Raised when unserialized object's type mismatches one specified for serializable field.
  49947. class SerializationTypeMismatch < ActiveRecordError
  49948. end
  49949. # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
  49950. # misses adapter field).
  49951. class AdapterNotSpecified < ActiveRecordError
  49952. end
  49953. # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
  49954. class AdapterNotFound < ActiveRecordError
  49955. end
  49956. # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
  49957. # is given a nil object).
  49958. class ConnectionNotEstablished < ActiveRecordError
  49959. end
  49960. # Raised when Active Record cannot find record by given id or set of ids.
  49961. class RecordNotFound < ActiveRecordError
  49962. end
  49963. # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
  49964. # saved because record is invalid.
  49965. class RecordNotSaved < ActiveRecordError
  49966. end
  49967. # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
  49968. class RecordNotDestroyed < ActiveRecordError
  49969. end
  49970. # Raised when SQL statement cannot be executed by the database (for example, it's often the case for
  49971. # MySQL when Ruby driver used is too old).
  49972. class StatementInvalid < ActiveRecordError
  49973. end
  49974. # Raised when SQL statement is invalid and the application gets a blank result.
  49975. class ThrowResult < ActiveRecordError
  49976. end
  49977. # Parent class for all specific exceptions which wrap database driver exceptions
  49978. # provides access to the original exception also.
  49979. class WrappedDatabaseException < StatementInvalid
  49980. attr_reader :original_exception
  49981. def initialize(message, original_exception)
  49982. super(message)
  49983. @original_exception = original_exception
  49984. end
  49985. end
  49986. # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
  49987. class RecordNotUnique < WrappedDatabaseException
  49988. end
  49989. # Raised when a record cannot be inserted or updated because it references a non-existent record.
  49990. class InvalidForeignKey < WrappedDatabaseException
  49991. end
  49992. # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
  49993. # when using +find+ method)
  49994. # does not match number of expected variables.
  49995. #
  49996. # For example, in
  49997. #
  49998. # Location.where("lat = ? AND lng = ?", 53.7362)
  49999. #
  50000. # two placeholders are given but only one variable to fill them.
  50001. class PreparedStatementInvalid < ActiveRecordError
  50002. end
  50003. # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
  50004. # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
  50005. # the page before the other.
  50006. #
  50007. # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
  50008. class StaleObjectError < ActiveRecordError
  50009. attr_reader :record, :attempted_action
  50010. def initialize(record, attempted_action)
  50011. super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
  50012. @record = record
  50013. @attempted_action = attempted_action
  50014. end
  50015. end
  50016. # Raised when association is being configured improperly or
  50017. # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
  50018. class ConfigurationError < ActiveRecordError
  50019. end
  50020. # Raised on attempt to update record that is instantiated as read only.
  50021. class ReadOnlyRecord < ActiveRecordError
  50022. end
  50023. # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
  50024. # to distinguish a deliberate rollback from other exceptional situations.
  50025. # Normally, raising an exception will cause the +transaction+ method to rollback
  50026. # the database transaction *and* pass on the exception. But if you raise an
  50027. # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
  50028. # without passing on the exception.
  50029. #
  50030. # For example, you could do this in your controller to rollback a transaction:
  50031. #
  50032. # class BooksController < ActionController::Base
  50033. # def create
  50034. # Book.transaction do
  50035. # book = Book.new(params[:book])
  50036. # book.save!
  50037. # if today_is_friday?
  50038. # # The system must fail on Friday so that our support department
  50039. # # won't be out of job. We silently rollback this transaction
  50040. # # without telling the user.
  50041. # raise ActiveRecord::Rollback, "Call tech support!"
  50042. # end
  50043. # end
  50044. # # ActiveRecord::Rollback is the only exception that won't be passed on
  50045. # # by ActiveRecord::Base.transaction, so this line will still be reached
  50046. # # even on Friday.
  50047. # redirect_to root_url
  50048. # end
  50049. # end
  50050. class Rollback < ActiveRecordError
  50051. end
  50052. # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
  50053. class DangerousAttributeError < ActiveRecordError
  50054. end
  50055. # Raised when unknown attributes are supplied via mass assignment.
  50056. class UnknownAttributeError < NoMethodError
  50057. end
  50058. # Raised when an error occurred while doing a mass assignment to an attribute through the
  50059. # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
  50060. # offending attribute.
  50061. class AttributeAssignmentError < ActiveRecordError
  50062. attr_reader :exception, :attribute
  50063. def initialize(message, exception, attribute)
  50064. super(message)
  50065. @exception = exception
  50066. @attribute = attribute
  50067. end
  50068. end
  50069. # Raised when there are multiple errors while doing a mass assignment through the +attributes+
  50070. # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
  50071. # objects, each corresponding to the error while assigning to an attribute.
  50072. class MultiparameterAssignmentErrors < ActiveRecordError
  50073. attr_reader :errors
  50074. def initialize(errors)
  50075. @errors = errors
  50076. end
  50077. end
  50078. # Raised when a primary key is needed, but there is not one specified in the schema or model.
  50079. class UnknownPrimaryKey < ActiveRecordError
  50080. attr_reader :model
  50081. def initialize(model)
  50082. super("Unknown primary key for table #{model.table_name} in model #{model}.")
  50083. @model = model
  50084. end
  50085. end
  50086. # Raised when a relation cannot be mutated because it's already loaded.
  50087. #
  50088. # class Task < ActiveRecord::Base
  50089. # end
  50090. #
  50091. # relation = Task.all
  50092. # relation.loaded? # => true
  50093. #
  50094. # # Methods which try to mutate a loaded relation fail.
  50095. # relation.where!(title: 'TODO') # => ActiveRecord::ImmutableRelation
  50096. # relation.limit!(5) # => ActiveRecord::ImmutableRelation
  50097. class ImmutableRelation < ActiveRecordError
  50098. end
  50099. class TransactionIsolationError < ActiveRecordError
  50100. end
  50101. end
  50102. require 'active_support/lazy_load_hooks'
  50103. module ActiveRecord
  50104. module Explain
  50105. def self.extended(base)
  50106. base.mattr_accessor :auto_explain_threshold_in_seconds, instance_accessor: false
  50107. end
  50108. # If the database adapter supports explain and auto explain is enabled,
  50109. # this method triggers EXPLAIN logging for the queries triggered by the
  50110. # block if it takes more than the threshold as a whole. That is, the
  50111. # threshold is not checked against each individual query, but against the
  50112. # duration of the entire block. This approach is convenient for relations.
  50113. #
  50114. # The available_queries_for_explain thread variable collects the queries
  50115. # to be explained. If the value is nil, it means queries are not being
  50116. # currently collected. A false value indicates collecting is turned
  50117. # off. Otherwise it is an array of queries.
  50118. def logging_query_plan # :nodoc:
  50119. return yield unless logger
  50120. threshold = auto_explain_threshold_in_seconds
  50121. current = Thread.current
  50122. if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil?
  50123. begin
  50124. queries = current[:available_queries_for_explain] = []
  50125. start = Time.now
  50126. result = yield
  50127. logger.warn(exec_explain(queries)) if Time.now - start > threshold
  50128. result
  50129. ensure
  50130. current[:available_queries_for_explain] = nil
  50131. end
  50132. else
  50133. yield
  50134. end
  50135. end
  50136. # Relation#explain needs to be able to collect the queries regardless of
  50137. # whether auto explain is enabled. This method serves that purpose.
  50138. def collecting_queries_for_explain # :nodoc:
  50139. current = Thread.current
  50140. original, current[:available_queries_for_explain] = current[:available_queries_for_explain], []
  50141. return yield, current[:available_queries_for_explain]
  50142. ensure
  50143. # Note that the return value above does not depend on this assigment.
  50144. current[:available_queries_for_explain] = original
  50145. end
  50146. # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
  50147. # Returns a formatted string ready to be logged.
  50148. def exec_explain(queries) # :nodoc:
  50149. str = queries && queries.map do |sql, bind|
  50150. [].tap do |msg|
  50151. msg << "EXPLAIN for: #{sql}"
  50152. unless bind.empty?
  50153. bind_msg = bind.map {|col, val| [col.name, val]}.inspect
  50154. msg.last << " #{bind_msg}"
  50155. end
  50156. msg << connection.explain(sql, bind)
  50157. end.join("\n")
  50158. end.join("\n")
  50159. # Overriding inspect to be more human readable, specially in the console.
  50160. def str.inspect
  50161. self
  50162. end
  50163. str
  50164. end
  50165. # Silences automatic EXPLAIN logging for the duration of the block.
  50166. #
  50167. # This has high priority, no EXPLAINs will be run even if downwards
  50168. # the threshold is set to 0.
  50169. #
  50170. # As the name of the method suggests this only applies to automatic
  50171. # EXPLAINs, manual calls to <tt>ActiveRecord::Relation#explain</tt> run.
  50172. def silence_auto_explain
  50173. current = Thread.current
  50174. original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
  50175. yield
  50176. ensure
  50177. current[:available_queries_for_explain] = original
  50178. end
  50179. end
  50180. end
  50181. require 'active_support/notifications'
  50182. module ActiveRecord
  50183. class ExplainSubscriber # :nodoc:
  50184. def start(name, id, payload)
  50185. # unused
  50186. end
  50187. def finish(name, id, payload)
  50188. if queries = Thread.current[:available_queries_for_explain]
  50189. queries << payload.values_at(:sql, :binds) unless ignore_payload?(payload)
  50190. end
  50191. end
  50192. # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
  50193. # our own EXPLAINs now matter how loopingly beautiful that would be.
  50194. #
  50195. # On the other hand, we want to monitor the performance of our real database
  50196. # queries, not the performance of the access to the query cache.
  50197. IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
  50198. EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)/i
  50199. def ignore_payload?(payload)
  50200. payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
  50201. end
  50202. ActiveSupport::Notifications.subscribe("sql.active_record", new)
  50203. end
  50204. end
  50205. require 'erb'
  50206. require 'yaml'
  50207. module ActiveRecord
  50208. class FixtureSet
  50209. class File # :nodoc:
  50210. include Enumerable
  50211. ##
  50212. # Open a fixture file named +file+. When called with a block, the block
  50213. # is called with the filehandle and the filehandle is automatically closed
  50214. # when the block finishes.
  50215. def self.open(file)
  50216. x = new file
  50217. block_given? ? yield(x) : x
  50218. end
  50219. def initialize(file)
  50220. @file = file
  50221. @rows = nil
  50222. end
  50223. def each(&block)
  50224. rows.each(&block)
  50225. end
  50226. RESCUE_ERRORS = [ ArgumentError, Psych::SyntaxError ] # :nodoc:
  50227. private
  50228. def rows
  50229. return @rows if @rows
  50230. begin
  50231. data = YAML.load(render(IO.read(@file)))
  50232. rescue *RESCUE_ERRORS => error
  50233. raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
  50234. end
  50235. @rows = data ? validate(data).to_a : []
  50236. end
  50237. def render(content)
  50238. ERB.new(content).result
  50239. end
  50240. # Validate our unmarshalled data.
  50241. def validate(data)
  50242. unless Hash === data || YAML::Omap === data
  50243. raise Fixture::FormatError, 'fixture is not a hash'
  50244. end
  50245. raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
  50246. data
  50247. end
  50248. end
  50249. end
  50250. end
  50251. require 'erb'
  50252. require 'yaml'
  50253. require 'zlib'
  50254. require 'active_support/dependencies'
  50255. require 'active_record/fixture_set/file'
  50256. require 'active_record/errors'
  50257. module ActiveRecord
  50258. class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
  50259. end
  50260. # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
  50261. #
  50262. # They are stored in YAML files, one file per model, which are placed in the directory
  50263. # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
  50264. # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
  50265. # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
  50266. # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
  50267. # like this:
  50268. #
  50269. # rubyonrails:
  50270. # id: 1
  50271. # name: Ruby on Rails
  50272. # url: http://www.rubyonrails.org
  50273. #
  50274. # google:
  50275. # id: 2
  50276. # name: Google
  50277. # url: http://www.google.com
  50278. #
  50279. # This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
  50280. # is followed by an indented list of key/value pairs in the "key: value" format. Records are
  50281. # separated by a blank line for your viewing pleasure.
  50282. #
  50283. # Note that fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
  50284. # See http://yaml.org/type/omap.html
  50285. # for the specification. You will need ordered fixtures when you have foreign key constraints
  50286. # on keys in the same table. This is commonly needed for tree structures. Example:
  50287. #
  50288. # --- !omap
  50289. # - parent:
  50290. # id: 1
  50291. # parent_id: NULL
  50292. # title: Parent
  50293. # - child:
  50294. # id: 2
  50295. # parent_id: 1
  50296. # title: Child
  50297. #
  50298. # = Using Fixtures in Test Cases
  50299. #
  50300. # Since fixtures are a testing construct, we use them in our unit and functional tests. There
  50301. # are two ways to use the fixtures, but first let's take a look at a sample unit test:
  50302. #
  50303. # require 'test_helper'
  50304. #
  50305. # class WebSiteTest < ActiveSupport::TestCase
  50306. # test "web_site_count" do
  50307. # assert_equal 2, WebSite.count
  50308. # end
  50309. # end
  50310. #
  50311. # By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
  50312. # so this test will succeed.
  50313. #
  50314. # The testing environment will automatically load the all fixtures into the database before each
  50315. # test. To ensure consistent data, the environment deletes the fixtures before running the load.
  50316. #
  50317. # In addition to being available in the database, the fixture's data may also be accessed by
  50318. # using a special dynamic method, which has the same name as the model, and accepts the
  50319. # name of the fixture to instantiate:
  50320. #
  50321. # test "find" do
  50322. # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
  50323. # end
  50324. #
  50325. # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
  50326. # following tests:
  50327. #
  50328. # test "find_alt_method_1" do
  50329. # assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
  50330. # end
  50331. #
  50332. # test "find_alt_method_2" do
  50333. # assert_equal "Ruby on Rails", @rubyonrails.name
  50334. # end
  50335. #
  50336. # In order to use these methods to access fixtured data within your testcases, you must specify one of the
  50337. # following in your <tt>ActiveSupport::TestCase</tt>-derived class:
  50338. #
  50339. # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
  50340. # self.use_instantiated_fixtures = true
  50341. #
  50342. # - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
  50343. # self.use_instantiated_fixtures = :no_instances
  50344. #
  50345. # Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
  50346. # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
  50347. # large sets of fixtured data.
  50348. #
  50349. # = Dynamic fixtures with ERB
  50350. #
  50351. # Some times you don't care about the content of the fixtures as much as you care about the volume.
  50352. # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
  50353. # testing, like:
  50354. #
  50355. # <% 1.upto(1000) do |i| %>
  50356. # fix_<%= i %>:
  50357. # id: <%= i %>
  50358. # name: guy_<%= 1 %>
  50359. # <% end %>
  50360. #
  50361. # This will create 1000 very simple fixtures.
  50362. #
  50363. # Using ERB, you can also inject dynamic values into your fixtures with inserts like
  50364. # <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
  50365. # This is however a feature to be used with some caution. The point of fixtures are that they're
  50366. # stable units of predictable sample data. If you feel that you need to inject dynamic values, then
  50367. # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
  50368. # in fixtures are to be considered a code smell.
  50369. #
  50370. # = Transactional Fixtures
  50371. #
  50372. # Test cases can use begin+rollback to isolate their changes to the database instead of having to
  50373. # delete+insert for every test case.
  50374. #
  50375. # class FooTest < ActiveSupport::TestCase
  50376. # self.use_transactional_fixtures = true
  50377. #
  50378. # test "godzilla" do
  50379. # assert !Foo.all.empty?
  50380. # Foo.destroy_all
  50381. # assert Foo.all.empty?
  50382. # end
  50383. #
  50384. # test "godzilla aftermath" do
  50385. # assert !Foo.all.empty?
  50386. # end
  50387. # end
  50388. #
  50389. # If you preload your test database with all fixture data (probably in the rake task) and use
  50390. # transactional fixtures, then you may omit all fixtures declarations in your test cases since
  50391. # all the data's already there and every case rolls back its changes.
  50392. #
  50393. # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
  50394. # true. This will provide access to fixture data for every table that has been loaded through
  50395. # fixtures (depending on the value of +use_instantiated_fixtures+).
  50396. #
  50397. # When *not* to use transactional fixtures:
  50398. #
  50399. # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
  50400. # all parent transactions commit, particularly, the fixtures transaction which is begun in setup
  50401. # and rolled back in teardown. Thus, you won't be able to verify
  50402. # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
  50403. # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
  50404. # Use InnoDB, MaxDB, or NDB instead.
  50405. #
  50406. # = Advanced Fixtures
  50407. #
  50408. # Fixtures that don't specify an ID get some extra features:
  50409. #
  50410. # * Stable, autogenerated IDs
  50411. # * Label references for associations (belongs_to, has_one, has_many)
  50412. # * HABTM associations as inline lists
  50413. # * Autofilled timestamp columns
  50414. # * Fixture label interpolation
  50415. # * Support for YAML defaults
  50416. #
  50417. # == Stable, Autogenerated IDs
  50418. #
  50419. # Here, have a monkey fixture:
  50420. #
  50421. # george:
  50422. # id: 1
  50423. # name: George the Monkey
  50424. #
  50425. # reginald:
  50426. # id: 2
  50427. # name: Reginald the Pirate
  50428. #
  50429. # Each of these fixtures has two unique identifiers: one for the database
  50430. # and one for the humans. Why don't we generate the primary key instead?
  50431. # Hashing each fixture's label yields a consistent ID:
  50432. #
  50433. # george: # generated id: 503576764
  50434. # name: George the Monkey
  50435. #
  50436. # reginald: # generated id: 324201669
  50437. # name: Reginald the Pirate
  50438. #
  50439. # Active Record looks at the fixture's model class, discovers the correct
  50440. # primary key, and generates it right before inserting the fixture
  50441. # into the database.
  50442. #
  50443. # The generated ID for a given label is constant, so we can discover
  50444. # any fixture's ID without loading anything, as long as we know the label.
  50445. #
  50446. # == Label references for associations (belongs_to, has_one, has_many)
  50447. #
  50448. # Specifying foreign keys in fixtures can be very fragile, not to
  50449. # mention difficult to read. Since Active Record can figure out the ID of
  50450. # any fixture from its label, you can specify FK's by label instead of ID.
  50451. #
  50452. # === belongs_to
  50453. #
  50454. # Let's break out some more monkeys and pirates.
  50455. #
  50456. # ### in pirates.yml
  50457. #
  50458. # reginald:
  50459. # id: 1
  50460. # name: Reginald the Pirate
  50461. # monkey_id: 1
  50462. #
  50463. # ### in monkeys.yml
  50464. #
  50465. # george:
  50466. # id: 1
  50467. # name: George the Monkey
  50468. # pirate_id: 1
  50469. #
  50470. # Add a few more monkeys and pirates and break this into multiple files,
  50471. # and it gets pretty hard to keep track of what's going on. Let's
  50472. # use labels instead of IDs:
  50473. #
  50474. # ### in pirates.yml
  50475. #
  50476. # reginald:
  50477. # name: Reginald the Pirate
  50478. # monkey: george
  50479. #
  50480. # ### in monkeys.yml
  50481. #
  50482. # george:
  50483. # name: George the Monkey
  50484. # pirate: reginald
  50485. #
  50486. # Pow! All is made clear. Active Record reflects on the fixture's model class,
  50487. # finds all the +belongs_to+ associations, and allows you to specify
  50488. # a target *label* for the *association* (monkey: george) rather than
  50489. # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
  50490. #
  50491. # ==== Polymorphic belongs_to
  50492. #
  50493. # Supporting polymorphic relationships is a little bit more complicated, since
  50494. # Active Record needs to know what type your association is pointing at. Something
  50495. # like this should look familiar:
  50496. #
  50497. # ### in fruit.rb
  50498. #
  50499. # belongs_to :eater, polymorphic: true
  50500. #
  50501. # ### in fruits.yml
  50502. #
  50503. # apple:
  50504. # id: 1
  50505. # name: apple
  50506. # eater_id: 1
  50507. # eater_type: Monkey
  50508. #
  50509. # Can we do better? You bet!
  50510. #
  50511. # apple:
  50512. # eater: george (Monkey)
  50513. #
  50514. # Just provide the polymorphic target type and Active Record will take care of the rest.
  50515. #
  50516. # === has_and_belongs_to_many
  50517. #
  50518. # Time to give our monkey some fruit.
  50519. #
  50520. # ### in monkeys.yml
  50521. #
  50522. # george:
  50523. # id: 1
  50524. # name: George the Monkey
  50525. #
  50526. # ### in fruits.yml
  50527. #
  50528. # apple:
  50529. # id: 1
  50530. # name: apple
  50531. #
  50532. # orange:
  50533. # id: 2
  50534. # name: orange
  50535. #
  50536. # grape:
  50537. # id: 3
  50538. # name: grape
  50539. #
  50540. # ### in fruits_monkeys.yml
  50541. #
  50542. # apple_george:
  50543. # fruit_id: 1
  50544. # monkey_id: 1
  50545. #
  50546. # orange_george:
  50547. # fruit_id: 2
  50548. # monkey_id: 1
  50549. #
  50550. # grape_george:
  50551. # fruit_id: 3
  50552. # monkey_id: 1
  50553. #
  50554. # Let's make the HABTM fixture go away.
  50555. #
  50556. # ### in monkeys.yml
  50557. #
  50558. # george:
  50559. # id: 1
  50560. # name: George the Monkey
  50561. # fruits: apple, orange, grape
  50562. #
  50563. # ### in fruits.yml
  50564. #
  50565. # apple:
  50566. # name: apple
  50567. #
  50568. # orange:
  50569. # name: orange
  50570. #
  50571. # grape:
  50572. # name: grape
  50573. #
  50574. # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
  50575. # on George's fixture, but we could've just as easily specified a list
  50576. # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
  50577. # the fixture's model class and discovers the +has_and_belongs_to_many+
  50578. # associations.
  50579. #
  50580. # == Autofilled Timestamp Columns
  50581. #
  50582. # If your table/model specifies any of Active Record's
  50583. # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
  50584. # they will automatically be set to <tt>Time.now</tt>.
  50585. #
  50586. # If you've set specific values, they'll be left alone.
  50587. #
  50588. # == Fixture label interpolation
  50589. #
  50590. # The label of the current fixture is always available as a column value:
  50591. #
  50592. # geeksomnia:
  50593. # name: Geeksomnia's Account
  50594. # subdomain: $LABEL
  50595. #
  50596. # Also, sometimes (like when porting older join table fixtures) you'll need
  50597. # to be able to get a hold of the identifier for a given label. ERB
  50598. # to the rescue:
  50599. #
  50600. # george_reginald:
  50601. # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
  50602. # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
  50603. #
  50604. # == Support for YAML defaults
  50605. #
  50606. # You probably already know how to use YAML to set and reuse defaults in
  50607. # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
  50608. #
  50609. # DEFAULTS: &DEFAULTS
  50610. # created_on: <%= 3.weeks.ago.to_s(:db) %>
  50611. #
  50612. # first:
  50613. # name: Smurf
  50614. # <<: *DEFAULTS
  50615. #
  50616. # second:
  50617. # name: Fraggle
  50618. # <<: *DEFAULTS
  50619. #
  50620. # Any fixture labeled "DEFAULTS" is safely ignored.
  50621. class FixtureSet
  50622. #--
  50623. # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
  50624. #++
  50625. MAX_ID = 2 ** 30 - 1
  50626. @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
  50627. def self.find_table_name(fixture_set_name) # :nodoc:
  50628. ActiveSupport::Deprecation.warn(
  50629. "ActiveRecord::Fixtures.find_table_name is deprecated and shall be removed from future releases. Use ActiveRecord::Fixtures.default_fixture_model_name instead.")
  50630. default_fixture_model_name(fixture_set_name)
  50631. end
  50632. def self.default_fixture_model_name(fixture_set_name) # :nodoc:
  50633. ActiveRecord::Base.pluralize_table_names ?
  50634. fixture_set_name.singularize.camelize :
  50635. fixture_set_name.camelize
  50636. end
  50637. def self.default_fixture_table_name(fixture_set_name) # :nodoc:
  50638. "#{ ActiveRecord::Base.table_name_prefix }"\
  50639. "#{ fixture_set_name.tr('/', '_') }"\
  50640. "#{ ActiveRecord::Base.table_name_suffix }".to_sym
  50641. end
  50642. def self.reset_cache
  50643. @@all_cached_fixtures.clear
  50644. end
  50645. def self.cache_for_connection(connection)
  50646. @@all_cached_fixtures[connection]
  50647. end
  50648. def self.fixture_is_cached?(connection, table_name)
  50649. cache_for_connection(connection)[table_name]
  50650. end
  50651. def self.cached_fixtures(connection, keys_to_fetch = nil)
  50652. if keys_to_fetch
  50653. cache_for_connection(connection).values_at(*keys_to_fetch)
  50654. else
  50655. cache_for_connection(connection).values
  50656. end
  50657. end
  50658. def self.cache_fixtures(connection, fixtures_map)
  50659. cache_for_connection(connection).update(fixtures_map)
  50660. end
  50661. def self.instantiate_fixtures(object, fixture_set, load_instances = true)
  50662. if load_instances
  50663. fixture_set.each do |fixture_name, fixture|
  50664. begin
  50665. object.instance_variable_set "@#{fixture_name}", fixture.find
  50666. rescue FixtureClassNotFound
  50667. nil
  50668. end
  50669. end
  50670. end
  50671. end
  50672. def self.instantiate_all_loaded_fixtures(object, load_instances = true)
  50673. all_loaded_fixtures.each_value do |fixture_set|
  50674. instantiate_fixtures(object, fixture_set, load_instances)
  50675. end
  50676. end
  50677. cattr_accessor :all_loaded_fixtures
  50678. self.all_loaded_fixtures = {}
  50679. def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {})
  50680. fixture_set_names = Array(fixture_set_names).map(&:to_s)
  50681. class_names = class_names.stringify_keys
  50682. # FIXME: Apparently JK uses this.
  50683. connection = block_given? ? yield : ActiveRecord::Base.connection
  50684. files_to_read = fixture_set_names.reject { |fs_name|
  50685. fixture_is_cached?(connection, fs_name)
  50686. }
  50687. unless files_to_read.empty?
  50688. connection.disable_referential_integrity do
  50689. fixtures_map = {}
  50690. fixture_sets = files_to_read.map do |fs_name|
  50691. fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
  50692. connection,
  50693. fs_name,
  50694. class_names[fs_name] || default_fixture_model_name(fs_name),
  50695. ::File.join(fixtures_directory, fs_name))
  50696. end
  50697. all_loaded_fixtures.update(fixtures_map)
  50698. connection.transaction(:requires_new => true) do
  50699. fixture_sets.each do |fs|
  50700. conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
  50701. table_rows = fs.table_rows
  50702. table_rows.keys.each do |table|
  50703. conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
  50704. end
  50705. table_rows.each do |fixture_set_name, rows|
  50706. rows.each do |row|
  50707. conn.insert_fixture(row, fixture_set_name)
  50708. end
  50709. end
  50710. end
  50711. # Cap primary key sequences to max(pk).
  50712. if connection.respond_to?(:reset_pk_sequence!)
  50713. fixture_sets.each do |fs|
  50714. connection.reset_pk_sequence!(fs.table_name)
  50715. end
  50716. end
  50717. end
  50718. cache_fixtures(connection, fixtures_map)
  50719. end
  50720. end
  50721. cached_fixtures(connection, fixture_set_names)
  50722. end
  50723. # Returns a consistent, platform-independent identifier for +label+.
  50724. # Identifiers are positive integers less than 2^32.
  50725. def self.identify(label)
  50726. Zlib.crc32(label.to_s) % MAX_ID
  50727. end
  50728. attr_reader :table_name, :name, :fixtures, :model_class
  50729. def initialize(connection, name, class_name, path)
  50730. @fixtures = {} # Ordered hash
  50731. @name = name
  50732. @path = path
  50733. if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
  50734. @model_class = class_name
  50735. else
  50736. @model_class = class_name.constantize rescue nil
  50737. end
  50738. @connection = ( model_class.respond_to?(:connection) ?
  50739. model_class.connection : connection )
  50740. @table_name = ( model_class.respond_to?(:table_name) ?
  50741. model_class.table_name :
  50742. self.class.default_fixture_table_name(name) )
  50743. read_fixture_files
  50744. end
  50745. def [](x)
  50746. fixtures[x]
  50747. end
  50748. def []=(k,v)
  50749. fixtures[k] = v
  50750. end
  50751. def each(&block)
  50752. fixtures.each(&block)
  50753. end
  50754. def size
  50755. fixtures.size
  50756. end
  50757. # Return a hash of rows to be inserted. The key is the table, the value is
  50758. # a list of rows to insert to that table.
  50759. def table_rows
  50760. now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
  50761. now = now.to_s(:db)
  50762. # allow a standard key to be used for doing defaults in YAML
  50763. fixtures.delete('DEFAULTS')
  50764. # track any join tables we need to insert later
  50765. rows = Hash.new { |h,table| h[table] = [] }
  50766. rows[table_name] = fixtures.map do |label, fixture|
  50767. row = fixture.to_hash
  50768. if model_class && model_class < ActiveRecord::Base
  50769. # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
  50770. if model_class.record_timestamps
  50771. timestamp_column_names.each do |c_name|
  50772. row[c_name] = now unless row.key?(c_name)
  50773. end
  50774. end
  50775. # interpolate the fixture label
  50776. row.each do |key, value|
  50777. row[key] = label if value == "$LABEL"
  50778. end
  50779. # generate a primary key if necessary
  50780. if has_primary_key_column? && !row.include?(primary_key_name)
  50781. row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
  50782. end
  50783. # If STI is used, find the correct subclass for association reflection
  50784. reflection_class =
  50785. if row.include?(inheritance_column_name)
  50786. row[inheritance_column_name].constantize rescue model_class
  50787. else
  50788. model_class
  50789. end
  50790. reflection_class.reflect_on_all_associations.each do |association|
  50791. case association.macro
  50792. when :belongs_to
  50793. # Do not replace association name with association foreign key if they are named the same
  50794. fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
  50795. if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
  50796. if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
  50797. # support polymorphic belongs_to as "label (Type)"
  50798. row[association.foreign_type] = $1
  50799. end
  50800. row[fk_name] = ActiveRecord::FixtureSet.identify(value)
  50801. end
  50802. when :has_and_belongs_to_many
  50803. if (targets = row.delete(association.name.to_s))
  50804. targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
  50805. table_name = association.join_table
  50806. rows[table_name].concat targets.map { |target|
  50807. { association.foreign_key => row[primary_key_name],
  50808. association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
  50809. }
  50810. end
  50811. end
  50812. end
  50813. end
  50814. row
  50815. end
  50816. rows
  50817. end
  50818. private
  50819. def primary_key_name
  50820. @primary_key_name ||= model_class && model_class.primary_key
  50821. end
  50822. def has_primary_key_column?
  50823. @has_primary_key_column ||= primary_key_name &&
  50824. model_class.columns.any? { |c| c.name == primary_key_name }
  50825. end
  50826. def timestamp_column_names
  50827. @timestamp_column_names ||=
  50828. %w(created_at created_on updated_at updated_on) & column_names
  50829. end
  50830. def inheritance_column_name
  50831. @inheritance_column_name ||= model_class && model_class.inheritance_column
  50832. end
  50833. def column_names
  50834. @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
  50835. end
  50836. def read_fixture_files
  50837. yaml_files = Dir["#{@path}/**/*.yml"].select { |f|
  50838. ::File.file?(f)
  50839. } + [yaml_file_path]
  50840. yaml_files.each do |file|
  50841. FixtureSet::File.open(file) do |fh|
  50842. fh.each do |fixture_name, row|
  50843. fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
  50844. end
  50845. end
  50846. end
  50847. end
  50848. def yaml_file_path
  50849. "#{@path}.yml"
  50850. end
  50851. end
  50852. #--
  50853. # Deprecate 'Fixtures' in favor of 'FixtureSet'.
  50854. #++
  50855. # :nodoc:
  50856. Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', 'ActiveRecord::FixtureSet')
  50857. class Fixture #:nodoc:
  50858. include Enumerable
  50859. class FixtureError < StandardError #:nodoc:
  50860. end
  50861. class FormatError < FixtureError #:nodoc:
  50862. end
  50863. attr_reader :model_class, :fixture
  50864. def initialize(fixture, model_class)
  50865. @fixture = fixture
  50866. @model_class = model_class
  50867. end
  50868. def class_name
  50869. model_class.name if model_class
  50870. end
  50871. def each
  50872. fixture.each { |item| yield item }
  50873. end
  50874. def [](key)
  50875. fixture[key]
  50876. end
  50877. alias :to_hash :fixture
  50878. def find
  50879. if model_class
  50880. model_class.find(fixture[model_class.primary_key])
  50881. else
  50882. raise FixtureClassNotFound, "No class attached to find."
  50883. end
  50884. end
  50885. end
  50886. end
  50887. module ActiveRecord
  50888. module TestFixtures
  50889. extend ActiveSupport::Concern
  50890. included do
  50891. setup :setup_fixtures
  50892. teardown :teardown_fixtures
  50893. class_attribute :fixture_path
  50894. class_attribute :fixture_table_names
  50895. class_attribute :fixture_class_names
  50896. class_attribute :use_transactional_fixtures
  50897. class_attribute :use_instantiated_fixtures # true, false, or :no_instances
  50898. class_attribute :pre_loaded_fixtures
  50899. self.fixture_table_names = []
  50900. self.use_transactional_fixtures = true
  50901. self.use_instantiated_fixtures = false
  50902. self.pre_loaded_fixtures = false
  50903. self.fixture_class_names = Hash.new do |h, fixture_set_name|
  50904. h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name)
  50905. end
  50906. end
  50907. module ClassMethods
  50908. # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
  50909. #
  50910. # Examples:
  50911. #
  50912. # set_fixture_class some_fixture: SomeModel,
  50913. # 'namespaced/fixture' => Another::Model
  50914. #
  50915. # The keys must be the fixture names, that coincide with the short paths to the fixture files.
  50916. #--
  50917. # It is also possible to pass the class name instead of the class:
  50918. # set_fixture_class 'some_fixture' => 'SomeModel'
  50919. # I think this option is redundant, i propose to deprecate it.
  50920. # Isn't it easier to always pass the class itself?
  50921. # (2011-12-20 alexeymuranov)
  50922. #++
  50923. def set_fixture_class(class_names = {})
  50924. self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
  50925. end
  50926. def fixtures(*fixture_set_names)
  50927. if fixture_set_names.first == :all
  50928. fixture_set_names = Dir["#{fixture_path}/**/*.{yml}"]
  50929. fixture_set_names.map! { |f| f[(fixture_path.size + 1)..-5] }
  50930. else
  50931. fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s }
  50932. end
  50933. self.fixture_table_names |= fixture_set_names
  50934. require_fixture_classes(fixture_set_names)
  50935. setup_fixture_accessors(fixture_set_names)
  50936. end
  50937. def try_to_load_dependency(file_name)
  50938. require_dependency file_name
  50939. rescue LoadError => e
  50940. # Let's hope the developer has included it himself
  50941. # Let's warn in case this is a subdependency, otherwise
  50942. # subdependency error messages are totally cryptic
  50943. if ActiveRecord::Base.logger
  50944. ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
  50945. end
  50946. end
  50947. def require_fixture_classes(fixture_set_names = nil)
  50948. if fixture_set_names
  50949. fixture_set_names = fixture_set_names.map { |n| n.to_s }
  50950. else
  50951. fixture_set_names = fixture_table_names
  50952. end
  50953. fixture_set_names.each do |file_name|
  50954. file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
  50955. try_to_load_dependency(file_name)
  50956. end
  50957. end
  50958. def setup_fixture_accessors(fixture_set_names = nil)
  50959. fixture_set_names = Array(fixture_set_names || fixture_table_names)
  50960. methods = Module.new do
  50961. fixture_set_names.each do |fs_name|
  50962. fs_name = fs_name.to_s
  50963. accessor_name = fs_name.tr('/', '_').to_sym
  50964. define_method(accessor_name) do |*fixture_names|
  50965. force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
  50966. @fixture_cache[fs_name] ||= {}
  50967. instances = fixture_names.map do |f_name|
  50968. f_name = f_name.to_s
  50969. @fixture_cache[fs_name].delete(f_name) if force_reload
  50970. if @loaded_fixtures[fs_name][f_name]
  50971. @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
  50972. else
  50973. raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
  50974. end
  50975. end
  50976. instances.size == 1 ? instances.first : instances
  50977. end
  50978. private accessor_name
  50979. end
  50980. end
  50981. include methods
  50982. end
  50983. def uses_transaction(*methods)
  50984. @uses_transaction = [] unless defined?(@uses_transaction)
  50985. @uses_transaction.concat methods.map { |m| m.to_s }
  50986. end
  50987. def uses_transaction?(method)
  50988. @uses_transaction = [] unless defined?(@uses_transaction)
  50989. @uses_transaction.include?(method.to_s)
  50990. end
  50991. end
  50992. def run_in_transaction?
  50993. use_transactional_fixtures &&
  50994. !self.class.uses_transaction?(method_name)
  50995. end
  50996. def setup_fixtures
  50997. return if ActiveRecord::Base.configurations.blank?
  50998. if pre_loaded_fixtures && !use_transactional_fixtures
  50999. raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
  51000. end
  51001. @fixture_cache = {}
  51002. @fixture_connections = []
  51003. @@already_loaded_fixtures ||= {}
  51004. # Load fixtures once and begin transaction.
  51005. if run_in_transaction?
  51006. if @@already_loaded_fixtures[self.class]
  51007. @loaded_fixtures = @@already_loaded_fixtures[self.class]
  51008. else
  51009. @loaded_fixtures = load_fixtures
  51010. @@already_loaded_fixtures[self.class] = @loaded_fixtures
  51011. end
  51012. @fixture_connections = enlist_fixture_connections
  51013. @fixture_connections.each do |connection|
  51014. connection.begin_transaction joinable: false
  51015. end
  51016. # Load fixtures for every test.
  51017. else
  51018. ActiveRecord::FixtureSet.reset_cache
  51019. @@already_loaded_fixtures[self.class] = nil
  51020. @loaded_fixtures = load_fixtures
  51021. end
  51022. # Instantiate fixtures for every test if requested.
  51023. instantiate_fixtures if use_instantiated_fixtures
  51024. end
  51025. def teardown_fixtures
  51026. return if ActiveRecord::Base.configurations.blank?
  51027. # Rollback changes if a transaction is active.
  51028. if run_in_transaction?
  51029. @fixture_connections.each do |connection|
  51030. connection.rollback_transaction if connection.transaction_open?
  51031. end
  51032. @fixture_connections.clear
  51033. else
  51034. ActiveRecord::FixtureSet.reset_cache
  51035. end
  51036. ActiveRecord::Base.clear_active_connections!
  51037. end
  51038. def enlist_fixture_connections
  51039. ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
  51040. end
  51041. private
  51042. def load_fixtures
  51043. fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
  51044. Hash[fixtures.map { |f| [f.name, f] }]
  51045. end
  51046. # for pre_loaded_fixtures, only require the classes once. huge speed improvement
  51047. @@required_fixture_classes = false
  51048. def instantiate_fixtures
  51049. if pre_loaded_fixtures
  51050. raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
  51051. unless @@required_fixture_classes
  51052. self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys
  51053. @@required_fixture_classes = true
  51054. end
  51055. ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
  51056. else
  51057. raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
  51058. @loaded_fixtures.each_value do |fixture_set|
  51059. ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
  51060. end
  51061. end
  51062. end
  51063. def load_instances?
  51064. use_instantiated_fixtures != :no_instances
  51065. end
  51066. end
  51067. end
  51068. require 'active_support/core_ext/hash/indifferent_access'
  51069. module ActiveRecord
  51070. module Inheritance
  51071. extend ActiveSupport::Concern
  51072. included do
  51073. # Determine whether to store the full constant name including namespace when using STI
  51074. class_attribute :store_full_sti_class, instance_writer: false
  51075. self.store_full_sti_class = true
  51076. end
  51077. module ClassMethods
  51078. # Determines if one of the attributes passed in is the inheritance column,
  51079. # and if the inheritance column is attr accessible, it initializes an
  51080. # instance of the given subclass instead of the base class
  51081. def new(*args, &block)
  51082. if (attrs = args.first).is_a?(Hash)
  51083. if subclass = subclass_from_attrs(attrs)
  51084. return subclass.new(*args, &block)
  51085. end
  51086. end
  51087. # Delegate to the original .new
  51088. super
  51089. end
  51090. # True if this isn't a concrete subclass needing a STI type condition.
  51091. def descends_from_active_record?
  51092. if self == Base
  51093. false
  51094. elsif superclass.abstract_class?
  51095. superclass.descends_from_active_record?
  51096. else
  51097. superclass == Base || !columns_hash.include?(inheritance_column)
  51098. end
  51099. end
  51100. def finder_needs_type_condition? #:nodoc:
  51101. # This is like this because benchmarking justifies the strange :false stuff
  51102. :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
  51103. end
  51104. def symbolized_base_class
  51105. @symbolized_base_class ||= base_class.to_s.to_sym
  51106. end
  51107. def symbolized_sti_name
  51108. @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
  51109. end
  51110. # Returns the class descending directly from ActiveRecord::Base, or
  51111. # an abstract class, if any, in the inheritance hierarchy.
  51112. #
  51113. # If A extends AR::Base, A.base_class will return A. If B descends from A
  51114. # through some arbitrarily deep hierarchy, B.base_class will return A.
  51115. #
  51116. # If B < A and C < B and if A is an abstract_class then both B.base_class
  51117. # and C.base_class would return B as the answer since A is an abstract_class.
  51118. def base_class
  51119. unless self < Base
  51120. raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
  51121. end
  51122. if superclass == Base || superclass.abstract_class?
  51123. self
  51124. else
  51125. superclass.base_class
  51126. end
  51127. end
  51128. # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
  51129. # If you are using inheritance with ActiveRecord and don't want child classes
  51130. # to utilize the implied STI table name of the parent class, this will need to be true.
  51131. # For example, given the following:
  51132. #
  51133. # class SuperClass < ActiveRecord::Base
  51134. # self.abstract_class = true
  51135. # end
  51136. # class Child < SuperClass
  51137. # self.table_name = 'the_table_i_really_want'
  51138. # end
  51139. #
  51140. #
  51141. # <tt>self.abstract_class = true</tt> is required to make <tt>Child<.find,.create, or any Arel method></tt> use <tt>the_table_i_really_want</tt> instead of a table called <tt>super_classes</tt>
  51142. #
  51143. attr_accessor :abstract_class
  51144. # Returns whether this class is an abstract class or not.
  51145. def abstract_class?
  51146. defined?(@abstract_class) && @abstract_class == true
  51147. end
  51148. def sti_name
  51149. store_full_sti_class ? name : name.demodulize
  51150. end
  51151. protected
  51152. # Returns the class type of the record using the current module as a prefix. So descendants of
  51153. # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
  51154. def compute_type(type_name)
  51155. if type_name.match(/^::/)
  51156. # If the type is prefixed with a scope operator then we assume that
  51157. # the type_name is an absolute reference.
  51158. ActiveSupport::Dependencies.constantize(type_name)
  51159. else
  51160. # Build a list of candidates to search for
  51161. candidates = []
  51162. name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
  51163. candidates << type_name
  51164. candidates.each do |candidate|
  51165. begin
  51166. constant = ActiveSupport::Dependencies.constantize(candidate)
  51167. return constant if candidate == constant.to_s
  51168. rescue NameError => e
  51169. # We don't want to swallow NoMethodError < NameError errors
  51170. raise e unless e.instance_of?(NameError)
  51171. end
  51172. end
  51173. raise NameError, "uninitialized constant #{candidates.first}"
  51174. end
  51175. end
  51176. private
  51177. # Called by +instantiate+ to decide which class to use for a new
  51178. # record instance. For single-table inheritance, we check the record
  51179. # for a +type+ column and return the corresponding class.
  51180. def discriminate_class_for_record(record)
  51181. if using_single_table_inheritance?(record)
  51182. find_sti_class(record[inheritance_column])
  51183. else
  51184. super
  51185. end
  51186. end
  51187. def using_single_table_inheritance?(record)
  51188. record[inheritance_column].present? && columns_hash.include?(inheritance_column)
  51189. end
  51190. def find_sti_class(type_name)
  51191. if store_full_sti_class
  51192. ActiveSupport::Dependencies.constantize(type_name)
  51193. else
  51194. compute_type(type_name)
  51195. end
  51196. rescue NameError
  51197. raise SubclassNotFound,
  51198. "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
  51199. "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
  51200. "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
  51201. "or overwrite #{name}.inheritance_column to use another column for that information."
  51202. end
  51203. def type_condition(table = arel_table)
  51204. sti_column = table[inheritance_column.to_sym]
  51205. sti_names = ([self] + descendants).map { |model| model.sti_name }
  51206. sti_column.in(sti_names)
  51207. end
  51208. # Detect the subclass from the inheritance column of attrs. If the inheritance column value
  51209. # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
  51210. # If this is a StrongParameters hash, and access to inheritance_column is not permitted,
  51211. # this will ignore the inheritance column and return nil
  51212. def subclass_from_attrs(attrs)
  51213. subclass_name = attrs.with_indifferent_access[inheritance_column]
  51214. return nil if subclass_name.blank? || subclass_name == self.name
  51215. unless subclass = subclasses.detect { |sub| sub.name == subclass_name }
  51216. raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
  51217. end
  51218. subclass
  51219. end
  51220. end
  51221. private
  51222. # Sets the attribute used for single table inheritance to this class name if this is not the
  51223. # ActiveRecord::Base descendant.
  51224. # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
  51225. # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
  51226. # No such attribute would be set for objects of the Message class in that example.
  51227. def ensure_proper_type
  51228. klass = self.class
  51229. if klass.finder_needs_type_condition?
  51230. write_attribute(klass.inheritance_column, klass.sti_name)
  51231. end
  51232. end
  51233. end
  51234. end
  51235. module ActiveRecord
  51236. module Integration
  51237. extend ActiveSupport::Concern
  51238. included do
  51239. ##
  51240. # :singleton-method:
  51241. # Indicates the format used to generate the timestamp format in the cache key.
  51242. # This is +:number+, by default.
  51243. class_attribute :cache_timestamp_format, :instance_writer => false
  51244. self.cache_timestamp_format = :nsec
  51245. end
  51246. # Returns a String, which Action Pack uses for constructing an URL to this
  51247. # object. The default implementation returns this record's id as a String,
  51248. # or nil if this record's unsaved.
  51249. #
  51250. # For example, suppose that you have a User model, and that you have a
  51251. # <tt>resources :users</tt> route. Normally, +user_path+ will
  51252. # construct a path with the user object's 'id' in it:
  51253. #
  51254. # user = User.find_by_name('Phusion')
  51255. # user_path(user) # => "/users/1"
  51256. #
  51257. # You can override +to_param+ in your model to make +user_path+ construct
  51258. # a path using the user's name instead of the user's id:
  51259. #
  51260. # class User < ActiveRecord::Base
  51261. # def to_param # overridden
  51262. # name
  51263. # end
  51264. # end
  51265. #
  51266. # user = User.find_by_name('Phusion')
  51267. # user_path(user) # => "/users/Phusion"
  51268. def to_param
  51269. # We can't use alias_method here, because method 'id' optimizes itself on the fly.
  51270. id && id.to_s # Be sure to stringify the id for routes
  51271. end
  51272. # Returns a cache key that can be used to identify this record.
  51273. #
  51274. # Product.new.cache_key # => "products/new"
  51275. # Product.find(5).cache_key # => "products/5" (updated_at not available)
  51276. # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
  51277. def cache_key
  51278. case
  51279. when new_record?
  51280. "#{self.class.model_name.cache_key}/new"
  51281. when timestamp = self[:updated_at]
  51282. timestamp = timestamp.utc.to_s(cache_timestamp_format)
  51283. "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
  51284. else
  51285. "#{self.class.model_name.cache_key}/#{id}"
  51286. end
  51287. end
  51288. end
  51289. end
  51290. module ActiveRecord
  51291. module Locking
  51292. # == What is Optimistic Locking
  51293. #
  51294. # Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
  51295. # conflicts with the data. It does this by checking whether another process has made changes to a record since
  51296. # it was opened, an <tt>ActiveRecord::StaleObjectError</tt> exception is thrown if that has occurred
  51297. # and the update is ignored.
  51298. #
  51299. # Check out <tt>ActiveRecord::Locking::Pessimistic</tt> for an alternative.
  51300. #
  51301. # == Usage
  51302. #
  51303. # Active Records support optimistic locking if the field +lock_version+ is present. Each update to the
  51304. # record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
  51305. # will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
  51306. #
  51307. # p1 = Person.find(1)
  51308. # p2 = Person.find(1)
  51309. #
  51310. # p1.first_name = "Michael"
  51311. # p1.save
  51312. #
  51313. # p2.first_name = "should fail"
  51314. # p2.save # Raises a ActiveRecord::StaleObjectError
  51315. #
  51316. # Optimistic locking will also check for stale data when objects are destroyed. Example:
  51317. #
  51318. # p1 = Person.find(1)
  51319. # p2 = Person.find(1)
  51320. #
  51321. # p1.first_name = "Michael"
  51322. # p1.save
  51323. #
  51324. # p2.destroy # Raises a ActiveRecord::StaleObjectError
  51325. #
  51326. # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
  51327. # or otherwise apply the business logic needed to resolve the conflict.
  51328. #
  51329. # This locking mechanism will function inside a single Ruby process. To make it work across all
  51330. # web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
  51331. #
  51332. # This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
  51333. # To override the name of the +lock_version+ column, set the <tt>locking_column</tt> class attribute:
  51334. #
  51335. # class Person < ActiveRecord::Base
  51336. # self.locking_column = :lock_person
  51337. # end
  51338. #
  51339. module Optimistic
  51340. extend ActiveSupport::Concern
  51341. included do
  51342. class_attribute :lock_optimistically, instance_writer: false
  51343. self.lock_optimistically = true
  51344. end
  51345. def locking_enabled? #:nodoc:
  51346. self.class.locking_enabled?
  51347. end
  51348. private
  51349. def increment_lock
  51350. lock_col = self.class.locking_column
  51351. previous_lock_value = send(lock_col).to_i
  51352. send(lock_col + '=', previous_lock_value + 1)
  51353. end
  51354. def update_record(attribute_names = @attributes.keys) #:nodoc:
  51355. return super unless locking_enabled?
  51356. return 0 if attribute_names.empty?
  51357. lock_col = self.class.locking_column
  51358. previous_lock_value = send(lock_col).to_i
  51359. increment_lock
  51360. attribute_names += [lock_col]
  51361. attribute_names.uniq!
  51362. begin
  51363. relation = self.class.unscoped
  51364. stmt = relation.where(
  51365. relation.table[self.class.primary_key].eq(id).and(
  51366. relation.table[lock_col].eq(self.class.quote_value(previous_lock_value))
  51367. )
  51368. ).arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
  51369. affected_rows = connection.update stmt
  51370. unless affected_rows == 1
  51371. raise ActiveRecord::StaleObjectError.new(self, "update")
  51372. end
  51373. affected_rows
  51374. # If something went wrong, revert the version.
  51375. rescue Exception
  51376. send(lock_col + '=', previous_lock_value)
  51377. raise
  51378. end
  51379. end
  51380. def destroy_row
  51381. affected_rows = super
  51382. if locking_enabled? && affected_rows != 1
  51383. raise ActiveRecord::StaleObjectError.new(self, "destroy")
  51384. end
  51385. affected_rows
  51386. end
  51387. def relation_for_destroy
  51388. relation = super
  51389. if locking_enabled?
  51390. column_name = self.class.locking_column
  51391. column = self.class.columns_hash[column_name]
  51392. substitute = connection.substitute_at(column, relation.bind_values.length)
  51393. relation = relation.where(self.class.arel_table[column_name].eq(substitute))
  51394. relation.bind_values << [column, self[column_name].to_i]
  51395. end
  51396. relation
  51397. end
  51398. module ClassMethods
  51399. DEFAULT_LOCKING_COLUMN = 'lock_version'
  51400. # Returns true if the +lock_optimistically+ flag is set to true
  51401. # (which it is, by default) and the table includes the
  51402. # +locking_column+ column (defaults to +lock_version+).
  51403. def locking_enabled?
  51404. lock_optimistically && columns_hash[locking_column]
  51405. end
  51406. # Set the column to use for optimistic locking. Defaults to +lock_version+.
  51407. def locking_column=(value)
  51408. @locking_column = value.to_s
  51409. end
  51410. # The version column used for optimistic locking. Defaults to +lock_version+.
  51411. def locking_column
  51412. reset_locking_column unless defined?(@locking_column)
  51413. @locking_column
  51414. end
  51415. # Quote the column name used for optimistic locking.
  51416. def quoted_locking_column
  51417. connection.quote_column_name(locking_column)
  51418. end
  51419. # Reset the column used for optimistic locking back to the +lock_version+ default.
  51420. def reset_locking_column
  51421. self.locking_column = DEFAULT_LOCKING_COLUMN
  51422. end
  51423. # Make sure the lock version column gets updated when counters are
  51424. # updated.
  51425. def update_counters(id, counters)
  51426. counters = counters.merge(locking_column => 1) if locking_enabled?
  51427. super
  51428. end
  51429. def column_defaults
  51430. @column_defaults ||= begin
  51431. defaults = super
  51432. if defaults.key?(locking_column) && lock_optimistically
  51433. defaults[locking_column] ||= 0
  51434. end
  51435. defaults
  51436. end
  51437. end
  51438. end
  51439. end
  51440. end
  51441. end
  51442. module ActiveRecord
  51443. module Locking
  51444. # Locking::Pessimistic provides support for row-level locking using
  51445. # SELECT ... FOR UPDATE and other lock types.
  51446. #
  51447. # Pass <tt>lock: true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive
  51448. # lock on the selected rows:
  51449. # # select * from accounts where id=1 for update
  51450. # Account.find(1, lock: true)
  51451. #
  51452. # Pass <tt>lock: 'some locking clause'</tt> to give a database-specific locking clause
  51453. # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
  51454. #
  51455. # Account.transaction do
  51456. # # select * from accounts where name = 'shugo' limit 1 for update
  51457. # shugo = Account.where("name = 'shugo'").lock(true).first
  51458. # yuko = Account.where("name = 'yuko'").lock(true).first
  51459. # shugo.balance -= 100
  51460. # shugo.save!
  51461. # yuko.balance += 100
  51462. # yuko.save!
  51463. # end
  51464. #
  51465. # You can also use <tt>ActiveRecord::Base#lock!</tt> method to lock one record by id.
  51466. # This may be better if you don't need to lock every row. Example:
  51467. #
  51468. # Account.transaction do
  51469. # # select * from accounts where ...
  51470. # accounts = Account.where(...)
  51471. # account1 = accounts.detect { |account| ... }
  51472. # account2 = accounts.detect { |account| ... }
  51473. # # select * from accounts where id=? for update
  51474. # account1.lock!
  51475. # account2.lock!
  51476. # account1.balance -= 100
  51477. # account1.save!
  51478. # account2.balance += 100
  51479. # account2.save!
  51480. # end
  51481. #
  51482. # You can start a transaction and acquire the lock in one go by calling
  51483. # <tt>with_lock</tt> with a block. The block is called from within
  51484. # a transaction, the object is already locked. Example:
  51485. #
  51486. # account = Account.first
  51487. # account.with_lock do
  51488. # # This block is called within a transaction,
  51489. # # account is already locked.
  51490. # account.balance -= 100
  51491. # account.save!
  51492. # end
  51493. #
  51494. # Database-specific information on row locking:
  51495. # MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
  51496. # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
  51497. module Pessimistic
  51498. # Obtain a row lock on this record. Reloads the record to obtain the requested
  51499. # lock. Pass an SQL locking clause to append the end of the SELECT statement
  51500. # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
  51501. # the locked record.
  51502. def lock!(lock = true)
  51503. reload(:lock => lock) if persisted?
  51504. self
  51505. end
  51506. # Wraps the passed block in a transaction, locking the object
  51507. # before yielding. You pass can the SQL locking clause
  51508. # as argument (see <tt>lock!</tt>).
  51509. def with_lock(lock = true)
  51510. transaction do
  51511. lock!(lock)
  51512. yield
  51513. end
  51514. end
  51515. end
  51516. end
  51517. end
  51518. module ActiveRecord
  51519. class LogSubscriber < ActiveSupport::LogSubscriber
  51520. IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
  51521. def self.runtime=(value)
  51522. Thread.current[:active_record_sql_runtime] = value
  51523. end
  51524. def self.runtime
  51525. Thread.current[:active_record_sql_runtime] ||= 0
  51526. end
  51527. def self.reset_runtime
  51528. rt, self.runtime = runtime, 0
  51529. rt
  51530. end
  51531. def initialize
  51532. super
  51533. @odd_or_even = false
  51534. end
  51535. def render_bind(column, value)
  51536. if column
  51537. if column.binary?
  51538. value = "<#{value.bytesize} bytes of binary data>"
  51539. end
  51540. [column.name, value]
  51541. else
  51542. [nil, value]
  51543. end
  51544. end
  51545. def sql(event)
  51546. self.class.runtime += event.duration
  51547. return unless logger.debug?
  51548. payload = event.payload
  51549. return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
  51550. name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
  51551. sql = payload[:sql].squeeze(' ')
  51552. binds = nil
  51553. unless (payload[:binds] || []).empty?
  51554. binds = " " + payload[:binds].map { |col,v|
  51555. render_bind(col, v)
  51556. }.inspect
  51557. end
  51558. if odd?
  51559. name = color(name, CYAN, true)
  51560. sql = color(sql, nil, true)
  51561. else
  51562. name = color(name, MAGENTA, true)
  51563. end
  51564. debug " #{name} #{sql}#{binds}"
  51565. end
  51566. def identity(event)
  51567. return unless logger.debug?
  51568. name = color(event.payload[:name], odd? ? CYAN : MAGENTA, true)
  51569. line = odd? ? color(event.payload[:line], nil, true) : event.payload[:line]
  51570. debug " #{name} #{line}"
  51571. end
  51572. def odd?
  51573. @odd_or_even = !@odd_or_even
  51574. end
  51575. def logger
  51576. ActiveRecord::Base.logger
  51577. end
  51578. end
  51579. end
  51580. ActiveRecord::LogSubscriber.attach_to :active_record
  51581. module ActiveRecord
  51582. class Migration
  51583. # <tt>ActiveRecord::Migration::CommandRecorder</tt> records commands done during
  51584. # a migration and knows how to reverse those commands. The CommandRecorder
  51585. # knows how to invert the following commands:
  51586. #
  51587. # * add_column
  51588. # * add_index
  51589. # * add_timestamps
  51590. # * create_table
  51591. # * create_join_table
  51592. # * remove_timestamps
  51593. # * rename_column
  51594. # * rename_index
  51595. # * rename_table
  51596. class CommandRecorder
  51597. include JoinTable
  51598. attr_accessor :commands, :delegate, :reverting
  51599. def initialize(delegate = nil)
  51600. @commands = []
  51601. @delegate = delegate
  51602. @reverting = false
  51603. end
  51604. # While executing the given block, the recorded will be in reverting mode.
  51605. # All commands recorded will end up being recorded reverted
  51606. # and in reverse order.
  51607. # For example:
  51608. #
  51609. # recorder.revert{ recorder.record(:rename_table, [:old, :new]) }
  51610. # # same effect as recorder.record(:rename_table, [:new, :old])
  51611. def revert
  51612. @reverting = !@reverting
  51613. previous = @commands
  51614. @commands = []
  51615. yield
  51616. ensure
  51617. @commands = previous.concat(@commands.reverse)
  51618. @reverting = !@reverting
  51619. end
  51620. # record +command+. +command+ should be a method name and arguments.
  51621. # For example:
  51622. #
  51623. # recorder.record(:method_name, [:arg1, :arg2])
  51624. def record(*command, &block)
  51625. if @reverting
  51626. @commands << inverse_of(*command, &block)
  51627. else
  51628. @commands << (command << block)
  51629. end
  51630. end
  51631. # Returns the inverse of the given command. For example:
  51632. #
  51633. # recorder.inverse_of(:rename_table, [:old, :new])
  51634. # # => [:rename_table, [:new, :old]]
  51635. #
  51636. # This method will raise an +IrreversibleMigration+ exception if it cannot
  51637. # invert the +command+.
  51638. def inverse_of(command, args, &block)
  51639. method = :"invert_#{command}"
  51640. raise IrreversibleMigration unless respond_to?(method, true)
  51641. send(method, args, &block)
  51642. end
  51643. def respond_to?(*args) # :nodoc:
  51644. super || delegate.respond_to?(*args)
  51645. end
  51646. [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
  51647. :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
  51648. :change_column_default, :add_reference, :remove_reference, :transaction,
  51649. :drop_join_table, :drop_table, :execute_block,
  51650. :change_column, :execute, :remove_columns, # irreversible methods need to be here too
  51651. ].each do |method|
  51652. class_eval <<-EOV, __FILE__, __LINE__ + 1
  51653. def #{method}(*args, &block) # def create_table(*args, &block)
  51654. record(:"#{method}", args, &block) # record(:create_table, args, &block)
  51655. end # end
  51656. EOV
  51657. end
  51658. alias :add_belongs_to :add_reference
  51659. alias :remove_belongs_to :remove_reference
  51660. def change_table(table_name, options = {})
  51661. yield ConnectionAdapters::Table.new(table_name, self)
  51662. end
  51663. private
  51664. module StraightReversions
  51665. private
  51666. { transaction: :transaction,
  51667. execute_block: :execute_block,
  51668. create_table: :drop_table,
  51669. create_join_table: :drop_join_table,
  51670. add_column: :remove_column,
  51671. add_timestamps: :remove_timestamps,
  51672. add_reference: :remove_reference,
  51673. }.each do |cmd, inv|
  51674. [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
  51675. class_eval <<-EOV, __FILE__, __LINE__ + 1
  51676. def invert_#{method}(args, &block) # def invert_create_table(args, &block)
  51677. [:#{inverse}, args, block] # [:drop_table, args, block]
  51678. end # end
  51679. EOV
  51680. end
  51681. end
  51682. end
  51683. include StraightReversions
  51684. def invert_drop_table(args, &block)
  51685. if args.size == 1 && block == nil
  51686. raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
  51687. end
  51688. super
  51689. end
  51690. def invert_rename_table(args)
  51691. [:rename_table, args.reverse]
  51692. end
  51693. def invert_remove_column(args)
  51694. raise ActiveRecord::IrreversibleMigration, "remove_column is only reversible if given a type." if args.size <= 2
  51695. super
  51696. end
  51697. def invert_rename_index(args)
  51698. [:rename_index, [args.first] + args.last(2).reverse]
  51699. end
  51700. def invert_rename_column(args)
  51701. [:rename_column, [args.first] + args.last(2).reverse]
  51702. end
  51703. def invert_add_index(args)
  51704. table, columns, options = *args
  51705. [:remove_index, [table, (options || {}).merge(column: columns)]]
  51706. end
  51707. def invert_remove_index(args)
  51708. table, options = *args
  51709. raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option." unless options && options[:column]
  51710. options = options.dup
  51711. [:add_index, [table, options.delete(:column), options]]
  51712. end
  51713. alias :invert_add_belongs_to :invert_add_reference
  51714. alias :invert_remove_belongs_to :invert_remove_reference
  51715. # Forwards any missing method call to the \target.
  51716. def method_missing(method, *args, &block)
  51717. @delegate.send(method, *args, &block)
  51718. rescue NoMethodError => e
  51719. raise e, e.message.sub(/ for #<.*$/, " via proxy for #{@delegate}")
  51720. end
  51721. end
  51722. end
  51723. end
  51724. module ActiveRecord
  51725. class Migration
  51726. module JoinTable #:nodoc:
  51727. private
  51728. def find_join_table_name(table_1, table_2, options = {})
  51729. options.delete(:table_name) || join_table_name(table_1, table_2)
  51730. end
  51731. def join_table_name(table_1, table_2)
  51732. [table_1.to_s, table_2.to_s].sort.join("_").to_sym
  51733. end
  51734. end
  51735. end
  51736. end
  51737. require "active_support/core_ext/class/attribute_accessors"
  51738. require 'set'
  51739. module ActiveRecord
  51740. # Exception that can be raised to stop migrations from going backwards.
  51741. class IrreversibleMigration < ActiveRecordError
  51742. end
  51743. class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
  51744. def initialize(version)
  51745. super("Multiple migrations have the version number #{version}")
  51746. end
  51747. end
  51748. class DuplicateMigrationNameError < ActiveRecordError#:nodoc:
  51749. def initialize(name)
  51750. super("Multiple migrations have the name #{name}")
  51751. end
  51752. end
  51753. class UnknownMigrationVersionError < ActiveRecordError #:nodoc:
  51754. def initialize(version)
  51755. super("No migration with version number #{version}")
  51756. end
  51757. end
  51758. class IllegalMigrationNameError < ActiveRecordError#:nodoc:
  51759. def initialize(name)
  51760. super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
  51761. end
  51762. end
  51763. class PendingMigrationError < ActiveRecordError#:nodoc:
  51764. def initialize
  51765. super("Migrations are pending; run 'rake db:migrate RAILS_ENV=#{Rails.env}' to resolve this issue.")
  51766. end
  51767. end
  51768. # = Active Record Migrations
  51769. #
  51770. # Migrations can manage the evolution of a schema used by several physical
  51771. # databases. It's a solution to the common problem of adding a field to make
  51772. # a new feature work in your local database, but being unsure of how to
  51773. # push that change to other developers and to the production server. With
  51774. # migrations, you can describe the transformations in self-contained classes
  51775. # that can be checked into version control systems and executed against
  51776. # another database that might be one, two, or five versions behind.
  51777. #
  51778. # Example of a simple migration:
  51779. #
  51780. # class AddSsl < ActiveRecord::Migration
  51781. # def up
  51782. # add_column :accounts, :ssl_enabled, :boolean, default: true
  51783. # end
  51784. #
  51785. # def down
  51786. # remove_column :accounts, :ssl_enabled
  51787. # end
  51788. # end
  51789. #
  51790. # This migration will add a boolean flag to the accounts table and remove it
  51791. # if you're backing out of the migration. It shows how all migrations have
  51792. # two methods +up+ and +down+ that describes the transformations
  51793. # required to implement or remove the migration. These methods can consist
  51794. # of both the migration specific methods like +add_column+ and +remove_column+,
  51795. # but may also contain regular Ruby code for generating data needed for the
  51796. # transformations.
  51797. #
  51798. # Example of a more complex migration that also needs to initialize data:
  51799. #
  51800. # class AddSystemSettings < ActiveRecord::Migration
  51801. # def up
  51802. # create_table :system_settings do |t|
  51803. # t.string :name
  51804. # t.string :label
  51805. # t.text :value
  51806. # t.string :type
  51807. # t.integer :position
  51808. # end
  51809. #
  51810. # SystemSetting.create name: 'notice',
  51811. # label: 'Use notice?',
  51812. # value: 1
  51813. # end
  51814. #
  51815. # def down
  51816. # drop_table :system_settings
  51817. # end
  51818. # end
  51819. #
  51820. # This migration first adds the +system_settings+ table, then creates the very
  51821. # first row in it using the Active Record model that relies on the table. It
  51822. # also uses the more advanced +create_table+ syntax where you can specify a
  51823. # complete table schema in one block call.
  51824. #
  51825. # == Available transformations
  51826. #
  51827. # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
  51828. # makes the table object available to a block that can then add columns to it,
  51829. # following the same format as +add_column+. See example above. The options hash
  51830. # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
  51831. # table definition.
  51832. # * <tt>drop_table(name)</tt>: Drops the table called +name+.
  51833. # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
  51834. # the table called +name+. It makes the table object availabe to a block that
  51835. # can then add/remove columns, indexes or foreign keys to it.
  51836. # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
  51837. # to +new_name+.
  51838. # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
  51839. # to the table called +table_name+
  51840. # named +column_name+ specified to be one of the following types:
  51841. # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
  51842. # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
  51843. # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
  51844. # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
  51845. # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
  51846. # <tt>{ limit: 50, null: false }</tt>) -- see
  51847. # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
  51848. # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
  51849. # a column but keeps the type and content.
  51850. # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
  51851. # the column to a different type using the same parameters as add_column.
  51852. # * <tt>remove_column(table_name, column_names)</tt>: Removes the column listed in
  51853. # +column_names+ from the table called +table_name+.
  51854. # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
  51855. # with the name of the column. Other options include
  51856. # <tt>:name</tt>, <tt>:unique</tt> (e.g.
  51857. # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
  51858. # (e.g. <tt>{ order: { name: :desc } }</tt>).
  51859. # * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
  51860. # specified by +column_name+.
  51861. # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
  51862. # specified by +index_name+.
  51863. #
  51864. # == Irreversible transformations
  51865. #
  51866. # Some transformations are destructive in a manner that cannot be reversed.
  51867. # Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
  51868. # exception in their +down+ method.
  51869. #
  51870. # == Running migrations from within Rails
  51871. #
  51872. # The Rails package has several tools to help create and apply migrations.
  51873. #
  51874. # To generate a new migration, you can use
  51875. # rails generate migration MyNewMigration
  51876. #
  51877. # where MyNewMigration is the name of your migration. The generator will
  51878. # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
  51879. # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
  51880. # UTC formatted date and time that the migration was generated.
  51881. #
  51882. # You may then edit the <tt>up</tt> and <tt>down</tt> methods of
  51883. # MyNewMigration.
  51884. #
  51885. # There is a special syntactic shortcut to generate migrations that add fields to a table.
  51886. #
  51887. # rails generate migration add_fieldname_to_tablename fieldname:string
  51888. #
  51889. # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
  51890. # class AddFieldnameToTablename < ActiveRecord::Migration
  51891. # def up
  51892. # add_column :tablenames, :fieldname, :string
  51893. # end
  51894. #
  51895. # def down
  51896. # remove_column :tablenames, :fieldname
  51897. # end
  51898. # end
  51899. #
  51900. # To run migrations against the currently configured database, use
  51901. # <tt>rake db:migrate</tt>. This will update the database by running all of the
  51902. # pending migrations, creating the <tt>schema_migrations</tt> table
  51903. # (see "About the schema_migrations table" section below) if missing. It will also
  51904. # invoke the db:schema:dump task, which will update your db/schema.rb file
  51905. # to match the structure of your database.
  51906. #
  51907. # To roll the database back to a previous migration version, use
  51908. # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
  51909. # you wish to downgrade. If any of the migrations throw an
  51910. # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
  51911. # have some manual work to do.
  51912. #
  51913. # == Database support
  51914. #
  51915. # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
  51916. # SQL Server, Sybase, and Oracle (all supported databases except DB2).
  51917. #
  51918. # == More examples
  51919. #
  51920. # Not all migrations change the schema. Some just fix the data:
  51921. #
  51922. # class RemoveEmptyTags < ActiveRecord::Migration
  51923. # def up
  51924. # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
  51925. # end
  51926. #
  51927. # def down
  51928. # # not much we can do to restore deleted data
  51929. # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
  51930. # end
  51931. # end
  51932. #
  51933. # Others remove columns when they migrate up instead of down:
  51934. #
  51935. # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
  51936. # def up
  51937. # remove_column :items, :incomplete_items_count
  51938. # remove_column :items, :completed_items_count
  51939. # end
  51940. #
  51941. # def down
  51942. # add_column :items, :incomplete_items_count
  51943. # add_column :items, :completed_items_count
  51944. # end
  51945. # end
  51946. #
  51947. # And sometimes you need to do something in SQL not abstracted directly by migrations:
  51948. #
  51949. # class MakeJoinUnique < ActiveRecord::Migration
  51950. # def up
  51951. # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
  51952. # end
  51953. #
  51954. # def down
  51955. # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
  51956. # end
  51957. # end
  51958. #
  51959. # == Using a model after changing its table
  51960. #
  51961. # Sometimes you'll want to add a column in a migration and populate it
  51962. # immediately after. In that case, you'll need to make a call to
  51963. # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
  51964. # latest column data from after the new column was added. Example:
  51965. #
  51966. # class AddPeopleSalary < ActiveRecord::Migration
  51967. # def up
  51968. # add_column :people, :salary, :integer
  51969. # Person.reset_column_information
  51970. # Person.all.each do |p|
  51971. # p.update_attribute :salary, SalaryCalculator.compute(p)
  51972. # end
  51973. # end
  51974. # end
  51975. #
  51976. # == Controlling verbosity
  51977. #
  51978. # By default, migrations will describe the actions they are taking, writing
  51979. # them to the console as they happen, along with benchmarks describing how
  51980. # long each step took.
  51981. #
  51982. # You can quiet them down by setting ActiveRecord::Migration.verbose = false.
  51983. #
  51984. # You can also insert your own messages and benchmarks by using the +say_with_time+
  51985. # method:
  51986. #
  51987. # def up
  51988. # ...
  51989. # say_with_time "Updating salaries..." do
  51990. # Person.all.each do |p|
  51991. # p.update_attribute :salary, SalaryCalculator.compute(p)
  51992. # end
  51993. # end
  51994. # ...
  51995. # end
  51996. #
  51997. # The phrase "Updating salaries..." would then be printed, along with the
  51998. # benchmark for the block when the block completes.
  51999. #
  52000. # == About the schema_migrations table
  52001. #
  52002. # Rails versions 2.0 and prior used to create a table called
  52003. # <tt>schema_info</tt> when using migrations. This table contained the
  52004. # version of the schema as of the last applied migration.
  52005. #
  52006. # Starting with Rails 2.1, the <tt>schema_info</tt> table is
  52007. # (automatically) replaced by the <tt>schema_migrations</tt> table, which
  52008. # contains the version numbers of all the migrations applied.
  52009. #
  52010. # As a result, it is now possible to add migration files that are numbered
  52011. # lower than the current schema version: when migrating up, those
  52012. # never-applied "interleaved" migrations will be automatically applied, and
  52013. # when migrating down, never-applied "interleaved" migrations will be skipped.
  52014. #
  52015. # == Timestamped Migrations
  52016. #
  52017. # By default, Rails generates migrations that look like:
  52018. #
  52019. # 20080717013526_your_migration_name.rb
  52020. #
  52021. # The prefix is a generation timestamp (in UTC).
  52022. #
  52023. # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
  52024. # off by setting:
  52025. #
  52026. # config.active_record.timestamped_migrations = false
  52027. #
  52028. # In application.rb.
  52029. #
  52030. # == Reversible Migrations
  52031. #
  52032. # Starting with Rails 3.1, you will be able to define reversible migrations.
  52033. # Reversible migrations are migrations that know how to go +down+ for you.
  52034. # You simply supply the +up+ logic, and the Migration system will figure out
  52035. # how to execute the down commands for you.
  52036. #
  52037. # To define a reversible migration, define the +change+ method in your
  52038. # migration like this:
  52039. #
  52040. # class TenderloveMigration < ActiveRecord::Migration
  52041. # def change
  52042. # create_table(:horses) do |t|
  52043. # t.column :content, :text
  52044. # t.column :remind_at, :datetime
  52045. # end
  52046. # end
  52047. # end
  52048. #
  52049. # This migration will create the horses table for you on the way up, and
  52050. # automatically figure out how to drop the table on the way down.
  52051. #
  52052. # Some commands like +remove_column+ cannot be reversed. If you care to
  52053. # define how to move up and down in these cases, you should define the +up+
  52054. # and +down+ methods as before.
  52055. #
  52056. # If a command cannot be reversed, an
  52057. # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
  52058. # the migration is moving down.
  52059. #
  52060. # For a list of commands that are reversible, please see
  52061. # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
  52062. class Migration
  52063. autoload :CommandRecorder, 'active_record/migration/command_recorder'
  52064. # This class is used to verify that all migrations have been run before
  52065. # loading a web page if config.active_record.migration_error is set to :page_load
  52066. class CheckPending
  52067. def initialize(app)
  52068. @app = app
  52069. end
  52070. def call(env)
  52071. ActiveRecord::Base.logger.silence do
  52072. ActiveRecord::Migration.check_pending!
  52073. end
  52074. @app.call(env)
  52075. end
  52076. end
  52077. class << self
  52078. attr_accessor :delegate # :nodoc:
  52079. end
  52080. def self.check_pending!
  52081. raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?
  52082. end
  52083. def self.method_missing(name, *args, &block) # :nodoc:
  52084. (delegate || superclass.delegate).send(name, *args, &block)
  52085. end
  52086. def self.migrate(direction)
  52087. new.migrate direction
  52088. end
  52089. cattr_accessor :verbose
  52090. attr_accessor :name, :version
  52091. def initialize(name = self.class.name, version = nil)
  52092. @name = name
  52093. @version = version
  52094. @connection = nil
  52095. end
  52096. # instantiate the delegate object after initialize is defined
  52097. self.verbose = true
  52098. self.delegate = new
  52099. # Reverses the migration commands for the given block and
  52100. # the given migrations.
  52101. #
  52102. # The following migration will remove the table 'horses'
  52103. # and create the table 'apples' on the way up, and the reverse
  52104. # on the way down.
  52105. #
  52106. # class FixTLMigration < ActiveRecord::Migration
  52107. # def change
  52108. # revert do
  52109. # create_table(:horses) do |t|
  52110. # t.text :content
  52111. # t.datetime :remind_at
  52112. # end
  52113. # end
  52114. # create_table(:apples) do |t|
  52115. # t.string :variety
  52116. # end
  52117. # end
  52118. # end
  52119. #
  52120. # Or equivalently, if +TenderloveMigration+ is defined as in the
  52121. # documentation for Migration:
  52122. #
  52123. # require_relative '2012121212_tenderlove_migration'
  52124. #
  52125. # class FixupTLMigration < ActiveRecord::Migration
  52126. # def change
  52127. # revert TenderloveMigration
  52128. #
  52129. # create_table(:apples) do |t|
  52130. # t.string :variety
  52131. # end
  52132. # end
  52133. # end
  52134. #
  52135. # This command can be nested.
  52136. def revert(*migration_classes)
  52137. run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
  52138. if block_given?
  52139. if @connection.respond_to? :revert
  52140. @connection.revert { yield }
  52141. else
  52142. recorder = CommandRecorder.new(@connection)
  52143. @connection = recorder
  52144. suppress_messages do
  52145. @connection.revert { yield }
  52146. end
  52147. @connection = recorder.delegate
  52148. recorder.commands.each do |cmd, args, block|
  52149. send(cmd, *args, &block)
  52150. end
  52151. end
  52152. end
  52153. end
  52154. def reverting?
  52155. @connection.respond_to?(:reverting) && @connection.reverting
  52156. end
  52157. class ReversibleBlockHelper < Struct.new(:reverting)
  52158. def up
  52159. yield unless reverting
  52160. end
  52161. def down
  52162. yield if reverting
  52163. end
  52164. end
  52165. # Used to specify an operation that can be run in one direction or another.
  52166. # Call the methods +up+ and +down+ of the yielded object to run a block
  52167. # only in one given direction.
  52168. # The whole block will be called in the right order within the migration.
  52169. #
  52170. # In the following example, the looping on users will always be done
  52171. # when the three columns 'first_name', 'last_name' and 'full_name' exist,
  52172. # even when migrating down:
  52173. #
  52174. # class SplitNameMigration < ActiveRecord::Migration
  52175. # def change
  52176. # add_column :users, :first_name, :string
  52177. # add_column :users, :last_name, :string
  52178. #
  52179. # reversible do |dir|
  52180. # User.reset_column_information
  52181. # User.all.each do |u|
  52182. # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
  52183. # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
  52184. # u.save
  52185. # end
  52186. # end
  52187. #
  52188. # revert { add_column :users, :full_name, :string }
  52189. # end
  52190. # end
  52191. def reversible
  52192. helper = ReversibleBlockHelper.new(reverting?)
  52193. execute_block{ yield helper }
  52194. end
  52195. # Runs the given migration classes.
  52196. # Last argument can specify options:
  52197. # - :direction (default is :up)
  52198. # - :revert (default is false)
  52199. def run(*migration_classes)
  52200. opts = migration_classes.extract_options!
  52201. dir = opts[:direction] || :up
  52202. dir = (dir == :down ? :up : :down) if opts[:revert]
  52203. if reverting?
  52204. # If in revert and going :up, say, we want to execute :down without reverting, so
  52205. revert { run(*migration_classes, direction: dir, revert: true) }
  52206. else
  52207. migration_classes.each do |migration_class|
  52208. migration_class.new.exec_migration(@connection, dir)
  52209. end
  52210. end
  52211. end
  52212. def up
  52213. self.class.delegate = self
  52214. return unless self.class.respond_to?(:up)
  52215. self.class.up
  52216. end
  52217. def down
  52218. self.class.delegate = self
  52219. return unless self.class.respond_to?(:down)
  52220. self.class.down
  52221. end
  52222. # Execute this migration in the named direction
  52223. def migrate(direction)
  52224. return unless respond_to?(direction)
  52225. case direction
  52226. when :up then announce "migrating"
  52227. when :down then announce "reverting"
  52228. end
  52229. time = nil
  52230. ActiveRecord::Base.connection_pool.with_connection do |conn|
  52231. time = Benchmark.measure do
  52232. exec_migration(conn, direction)
  52233. end
  52234. end
  52235. case direction
  52236. when :up then announce "migrated (%.4fs)" % time.real; write
  52237. when :down then announce "reverted (%.4fs)" % time.real; write
  52238. end
  52239. end
  52240. def exec_migration(conn, direction)
  52241. @connection = conn
  52242. if respond_to?(:change)
  52243. if direction == :down
  52244. revert { change }
  52245. else
  52246. change
  52247. end
  52248. else
  52249. send(direction)
  52250. end
  52251. ensure
  52252. @connection = nil
  52253. end
  52254. def write(text="")
  52255. puts(text) if verbose
  52256. end
  52257. def announce(message)
  52258. text = "#{version} #{name}: #{message}"
  52259. length = [0, 75 - text.length].max
  52260. write "== %s %s" % [text, "=" * length]
  52261. end
  52262. def say(message, subitem=false)
  52263. write "#{subitem ? " ->" : "--"} #{message}"
  52264. end
  52265. def say_with_time(message)
  52266. say(message)
  52267. result = nil
  52268. time = Benchmark.measure { result = yield }
  52269. say "%.4fs" % time.real, :subitem
  52270. say("#{result} rows", :subitem) if result.is_a?(Integer)
  52271. result
  52272. end
  52273. def suppress_messages
  52274. save, self.verbose = verbose, false
  52275. yield
  52276. ensure
  52277. self.verbose = save
  52278. end
  52279. def connection
  52280. @connection || ActiveRecord::Base.connection
  52281. end
  52282. def method_missing(method, *arguments, &block)
  52283. arg_list = arguments.map{ |a| a.inspect } * ', '
  52284. say_with_time "#{method}(#{arg_list})" do
  52285. unless @connection.respond_to? :revert
  52286. unless arguments.empty? || method == :execute
  52287. arguments[0] = Migrator.proper_table_name(arguments.first)
  52288. arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
  52289. end
  52290. end
  52291. return super unless connection.respond_to?(method)
  52292. connection.send(method, *arguments, &block)
  52293. end
  52294. end
  52295. def copy(destination, sources, options = {})
  52296. copied = []
  52297. FileUtils.mkdir_p(destination) unless File.exists?(destination)
  52298. destination_migrations = ActiveRecord::Migrator.migrations(destination)
  52299. last = destination_migrations.last
  52300. sources.each do |scope, path|
  52301. source_migrations = ActiveRecord::Migrator.migrations(path)
  52302. source_migrations.each do |migration|
  52303. source = File.read(migration.filename)
  52304. source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
  52305. if duplicate = destination_migrations.detect { |m| m.name == migration.name }
  52306. if options[:on_skip] && duplicate.scope != scope.to_s
  52307. options[:on_skip].call(scope, migration)
  52308. end
  52309. next
  52310. end
  52311. migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
  52312. new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
  52313. old_path, migration.filename = migration.filename, new_path
  52314. last = migration
  52315. File.open(migration.filename, "w") { |f| f.write source }
  52316. copied << migration
  52317. options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
  52318. destination_migrations << migration
  52319. end
  52320. end
  52321. copied
  52322. end
  52323. def next_migration_number(number)
  52324. if ActiveRecord::Base.timestamped_migrations
  52325. [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
  52326. else
  52327. "%.3d" % number
  52328. end
  52329. end
  52330. private
  52331. def execute_block
  52332. if connection.respond_to? :execute_block
  52333. super # use normal delegation to record the block
  52334. else
  52335. yield
  52336. end
  52337. end
  52338. end
  52339. # MigrationProxy is used to defer loading of the actual migration classes
  52340. # until they are needed
  52341. class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
  52342. def initialize(name, version, filename, scope)
  52343. super
  52344. @migration = nil
  52345. end
  52346. def basename
  52347. File.basename(filename)
  52348. end
  52349. delegate :migrate, :announce, :write, :to => :migration
  52350. private
  52351. def migration
  52352. @migration ||= load_migration
  52353. end
  52354. def load_migration
  52355. require(File.expand_path(filename))
  52356. name.constantize.new
  52357. end
  52358. end
  52359. class Migrator#:nodoc:
  52360. class << self
  52361. attr_writer :migrations_paths
  52362. alias :migrations_path= :migrations_paths=
  52363. def migrate(migrations_paths, target_version = nil, &block)
  52364. case
  52365. when target_version.nil?
  52366. up(migrations_paths, target_version, &block)
  52367. when current_version == 0 && target_version == 0
  52368. []
  52369. when current_version > target_version
  52370. down(migrations_paths, target_version, &block)
  52371. else
  52372. up(migrations_paths, target_version, &block)
  52373. end
  52374. end
  52375. def rollback(migrations_paths, steps=1)
  52376. move(:down, migrations_paths, steps)
  52377. end
  52378. def forward(migrations_paths, steps=1)
  52379. move(:up, migrations_paths, steps)
  52380. end
  52381. def up(migrations_paths, target_version = nil)
  52382. migrations = migrations(migrations_paths)
  52383. migrations.select! { |m| yield m } if block_given?
  52384. self.new(:up, migrations, target_version).migrate
  52385. end
  52386. def down(migrations_paths, target_version = nil, &block)
  52387. migrations = migrations(migrations_paths)
  52388. migrations.select! { |m| yield m } if block_given?
  52389. self.new(:down, migrations, target_version).migrate
  52390. end
  52391. def run(direction, migrations_paths, target_version)
  52392. self.new(direction, migrations(migrations_paths), target_version).run
  52393. end
  52394. def open(migrations_paths)
  52395. self.new(:up, migrations(migrations_paths), nil)
  52396. end
  52397. def schema_migrations_table_name
  52398. SchemaMigration.table_name
  52399. end
  52400. def get_all_versions
  52401. SchemaMigration.all.map { |x| x.version.to_i }.sort
  52402. end
  52403. def current_version
  52404. sm_table = schema_migrations_table_name
  52405. if Base.connection.table_exists?(sm_table)
  52406. get_all_versions.max || 0
  52407. else
  52408. 0
  52409. end
  52410. end
  52411. def needs_migration?
  52412. current_version < last_version
  52413. end
  52414. def last_version
  52415. migrations(migrations_paths).last.try(:version)||0
  52416. end
  52417. def proper_table_name(name)
  52418. # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
  52419. if name.respond_to? :table_name
  52420. name.table_name
  52421. else
  52422. "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
  52423. end
  52424. end
  52425. def migrations_paths
  52426. @migrations_paths ||= ['db/migrate']
  52427. # just to not break things if someone uses: migration_path = some_string
  52428. Array(@migrations_paths)
  52429. end
  52430. def migrations_path
  52431. migrations_paths.first
  52432. end
  52433. def migrations(paths)
  52434. paths = Array(paths)
  52435. files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
  52436. migrations = files.map do |file|
  52437. version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
  52438. raise IllegalMigrationNameError.new(file) unless version
  52439. version = version.to_i
  52440. name = name.camelize
  52441. MigrationProxy.new(name, version, file, scope)
  52442. end
  52443. migrations.sort_by(&:version)
  52444. end
  52445. private
  52446. def move(direction, migrations_paths, steps)
  52447. migrator = self.new(direction, migrations(migrations_paths))
  52448. start_index = migrator.migrations.index(migrator.current_migration)
  52449. if start_index
  52450. finish = migrator.migrations[start_index + steps]
  52451. version = finish ? finish.version : 0
  52452. send(direction, migrations_paths, version)
  52453. end
  52454. end
  52455. end
  52456. def initialize(direction, migrations, target_version = nil)
  52457. raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
  52458. @direction = direction
  52459. @target_version = target_version
  52460. @migrated_versions = nil
  52461. if Array(migrations).grep(String).empty?
  52462. @migrations = migrations
  52463. else
  52464. ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations"
  52465. @migrations = self.class.migrations(migrations)
  52466. end
  52467. validate(@migrations)
  52468. ActiveRecord::SchemaMigration.create_table
  52469. end
  52470. def current_version
  52471. migrated.sort.last || 0
  52472. end
  52473. def current_migration
  52474. migrations.detect { |m| m.version == current_version }
  52475. end
  52476. alias :current :current_migration
  52477. def run
  52478. target = migrations.detect { |m| m.version == @target_version }
  52479. raise UnknownMigrationVersionError.new(@target_version) if target.nil?
  52480. unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
  52481. target.migrate(@direction)
  52482. record_version_state_after_migrating(target.version)
  52483. end
  52484. end
  52485. def migrate
  52486. if !target && @target_version && @target_version > 0
  52487. raise UnknownMigrationVersionError.new(@target_version)
  52488. end
  52489. running = runnable
  52490. if block_given?
  52491. message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator"
  52492. ActiveSupport::Deprecation.warn message
  52493. running.select! { |m| yield m }
  52494. end
  52495. running.each do |migration|
  52496. Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
  52497. begin
  52498. ddl_transaction do
  52499. migration.migrate(@direction)
  52500. record_version_state_after_migrating(migration.version)
  52501. end
  52502. rescue => e
  52503. canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
  52504. raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
  52505. end
  52506. end
  52507. end
  52508. def runnable
  52509. runnable = migrations[start..finish]
  52510. if up?
  52511. runnable.reject { |m| ran?(m) }
  52512. else
  52513. # skip the last migration if we're headed down, but not ALL the way down
  52514. runnable.pop if target
  52515. runnable.find_all { |m| ran?(m) }
  52516. end
  52517. end
  52518. def migrations
  52519. down? ? @migrations.reverse : @migrations.sort_by(&:version)
  52520. end
  52521. def pending_migrations
  52522. already_migrated = migrated
  52523. migrations.reject { |m| already_migrated.include?(m.version) }
  52524. end
  52525. def migrated
  52526. @migrated_versions ||= Set.new(self.class.get_all_versions)
  52527. end
  52528. private
  52529. def ran?(migration)
  52530. migrated.include?(migration.version.to_i)
  52531. end
  52532. def target
  52533. migrations.detect { |m| m.version == @target_version }
  52534. end
  52535. def finish
  52536. migrations.index(target) || migrations.size - 1
  52537. end
  52538. def start
  52539. up? ? 0 : (migrations.index(current) || 0)
  52540. end
  52541. def validate(migrations)
  52542. name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
  52543. raise DuplicateMigrationNameError.new(name) if name
  52544. version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
  52545. raise DuplicateMigrationVersionError.new(version) if version
  52546. end
  52547. def record_version_state_after_migrating(version)
  52548. if down?
  52549. migrated.delete(version)
  52550. ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
  52551. else
  52552. migrated << version
  52553. ActiveRecord::SchemaMigration.create!(:version => version.to_s)
  52554. end
  52555. end
  52556. def up?
  52557. @direction == :up
  52558. end
  52559. def down?
  52560. @direction == :down
  52561. end
  52562. # Wrap the migration in a transaction only if supported by the adapter.
  52563. def ddl_transaction
  52564. if Base.connection.supports_ddl_transactions?
  52565. Base.transaction { yield }
  52566. else
  52567. yield
  52568. end
  52569. end
  52570. end
  52571. end
  52572. module ActiveRecord
  52573. module ModelSchema
  52574. extend ActiveSupport::Concern
  52575. included do
  52576. ##
  52577. # :singleton-method:
  52578. # Accessor for the prefix type that will be prepended to every primary key column name.
  52579. # The options are :table_name and :table_name_with_underscore. If the first is specified,
  52580. # the Product class will look for "productid" instead of "id" as the primary column. If the
  52581. # latter is specified, the Product class will look for "product_id" instead of "id". Remember
  52582. # that this is a global setting for all Active Records.
  52583. mattr_accessor :primary_key_prefix_type, instance_writer: false
  52584. ##
  52585. # :singleton-method:
  52586. # Accessor for the name of the prefix string to prepend to every table name. So if set
  52587. # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
  52588. # etc. This is a convenient way of creating a namespace for tables in a shared database.
  52589. # By default, the prefix is the empty string.
  52590. #
  52591. # If you are organising your models within modules you can add a prefix to the models within
  52592. # a namespace by defining a singleton method in the parent module called table_name_prefix which
  52593. # returns your chosen prefix.
  52594. class_attribute :table_name_prefix, instance_writer: false
  52595. self.table_name_prefix = ""
  52596. ##
  52597. # :singleton-method:
  52598. # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
  52599. # "people_basecamp"). By default, the suffix is the empty string.
  52600. class_attribute :table_name_suffix, instance_writer: false
  52601. self.table_name_suffix = ""
  52602. ##
  52603. # :singleton-method:
  52604. # Indicates whether table names should be the pluralized versions of the corresponding class names.
  52605. # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
  52606. # See table_name for the full rules on table/class naming. This is true, by default.
  52607. class_attribute :pluralize_table_names, instance_writer: false
  52608. self.pluralize_table_names = true
  52609. self.inheritance_column = 'type'
  52610. end
  52611. module ClassMethods
  52612. # Guesses the table name (in forced lower-case) based on the name of the class in the
  52613. # inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
  52614. # looks like: Reply < Message < ActiveRecord::Base, then Message is used
  52615. # to guess the table name even when called on Reply. The rules used to do the guess
  52616. # are handled by the Inflector class in Active Support, which knows almost all common
  52617. # English inflections. You can add new inflections in config/initializers/inflections.rb.
  52618. #
  52619. # Nested classes are given table names prefixed by the singular form of
  52620. # the parent's table name. Enclosing modules are not considered.
  52621. #
  52622. # ==== Examples
  52623. #
  52624. # class Invoice < ActiveRecord::Base
  52625. # end
  52626. #
  52627. # file class table_name
  52628. # invoice.rb Invoice invoices
  52629. #
  52630. # class Invoice < ActiveRecord::Base
  52631. # class Lineitem < ActiveRecord::Base
  52632. # end
  52633. # end
  52634. #
  52635. # file class table_name
  52636. # invoice.rb Invoice::Lineitem invoice_lineitems
  52637. #
  52638. # module Invoice
  52639. # class Lineitem < ActiveRecord::Base
  52640. # end
  52641. # end
  52642. #
  52643. # file class table_name
  52644. # invoice/lineitem.rb Invoice::Lineitem lineitems
  52645. #
  52646. # Additionally, the class-level +table_name_prefix+ is prepended and the
  52647. # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
  52648. # the table name guess for an Invoice class becomes "myapp_invoices".
  52649. # Invoice::Lineitem becomes "myapp_invoice_lineitems".
  52650. #
  52651. # You can also set your own table name explicitly:
  52652. #
  52653. # class Mouse < ActiveRecord::Base
  52654. # self.table_name = "mice"
  52655. # end
  52656. #
  52657. # Alternatively, you can override the table_name method to define your
  52658. # own computation. (Possibly using <tt>super</tt> to manipulate the default
  52659. # table name.) Example:
  52660. #
  52661. # class Post < ActiveRecord::Base
  52662. # def self.table_name
  52663. # "special_" + super
  52664. # end
  52665. # end
  52666. # Post.table_name # => "special_posts"
  52667. def table_name
  52668. reset_table_name unless defined?(@table_name)
  52669. @table_name
  52670. end
  52671. # Sets the table name explicitly. Example:
  52672. #
  52673. # class Project < ActiveRecord::Base
  52674. # self.table_name = "project"
  52675. # end
  52676. #
  52677. # You can also just define your own <tt>self.table_name</tt> method; see
  52678. # the documentation for ActiveRecord::Base#table_name.
  52679. def table_name=(value)
  52680. value = value && value.to_s
  52681. if defined?(@table_name)
  52682. return if value == @table_name
  52683. reset_column_information if connected?
  52684. end
  52685. @table_name = value
  52686. @quoted_table_name = nil
  52687. @arel_table = nil
  52688. @sequence_name = nil unless defined?(@explicit_sequence_name) && @explicit_sequence_name
  52689. @relation = Relation.new(self, arel_table)
  52690. end
  52691. # Returns a quoted version of the table name, used to construct SQL statements.
  52692. def quoted_table_name
  52693. @quoted_table_name ||= connection.quote_table_name(table_name)
  52694. end
  52695. # Computes the table name, (re)sets it internally, and returns it.
  52696. def reset_table_name #:nodoc:
  52697. self.table_name = if abstract_class?
  52698. superclass == Base ? nil : superclass.table_name
  52699. elsif superclass.abstract_class?
  52700. superclass.table_name || compute_table_name
  52701. else
  52702. compute_table_name
  52703. end
  52704. end
  52705. def full_table_name_prefix #:nodoc:
  52706. (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
  52707. end
  52708. # Defines the name of the table column which will store the class name on single-table
  52709. # inheritance situations.
  52710. #
  52711. # The default inheritance column name is +type+, which means it's a
  52712. # reserved word inside Active Record. To be able to use single-table
  52713. # inheritance with another column name, or to use the column +type+ in
  52714. # your own model for something else, you can set +inheritance_column+:
  52715. #
  52716. # self.inheritance_column = 'zoink'
  52717. def inheritance_column
  52718. (@inheritance_column ||= nil) || superclass.inheritance_column
  52719. end
  52720. # Sets the value of inheritance_column
  52721. def inheritance_column=(value)
  52722. @inheritance_column = value.to_s
  52723. @explicit_inheritance_column = true
  52724. end
  52725. def sequence_name
  52726. if base_class == self
  52727. @sequence_name ||= reset_sequence_name
  52728. else
  52729. (@sequence_name ||= nil) || base_class.sequence_name
  52730. end
  52731. end
  52732. def reset_sequence_name #:nodoc:
  52733. @explicit_sequence_name = false
  52734. @sequence_name = connection.default_sequence_name(table_name, primary_key)
  52735. end
  52736. # Sets the name of the sequence to use when generating ids to the given
  52737. # value, or (if the value is nil or false) to the value returned by the
  52738. # given block. This is required for Oracle and is useful for any
  52739. # database which relies on sequences for primary key generation.
  52740. #
  52741. # If a sequence name is not explicitly set when using Oracle or Firebird,
  52742. # it will default to the commonly used pattern of: #{table_name}_seq
  52743. #
  52744. # If a sequence name is not explicitly set when using PostgreSQL, it
  52745. # will discover the sequence corresponding to your primary key for you.
  52746. #
  52747. # class Project < ActiveRecord::Base
  52748. # self.sequence_name = "projectseq" # default would have been "project_seq"
  52749. # end
  52750. def sequence_name=(value)
  52751. @sequence_name = value.to_s
  52752. @explicit_sequence_name = true
  52753. end
  52754. # Indicates whether the table associated with this class exists
  52755. def table_exists?
  52756. connection.schema_cache.table_exists?(table_name)
  52757. end
  52758. # Returns an array of column objects for the table associated with this class.
  52759. def columns
  52760. @columns ||= connection.schema_cache.columns[table_name].map do |col|
  52761. col = col.dup
  52762. col.primary = (col.name == primary_key)
  52763. col
  52764. end
  52765. end
  52766. # Returns a hash of column objects for the table associated with this class.
  52767. def columns_hash
  52768. @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
  52769. end
  52770. def column_types # :nodoc:
  52771. @column_types ||= decorate_columns(columns_hash.dup)
  52772. end
  52773. def decorate_columns(columns_hash) # :nodoc:
  52774. return if columns_hash.empty?
  52775. columns_hash.each do |name, col|
  52776. if serialized_attributes.key?(name)
  52777. columns_hash[name] = AttributeMethods::Serialization::Type.new(col)
  52778. end
  52779. if create_time_zone_conversion_attribute?(name, col)
  52780. columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(col)
  52781. end
  52782. end
  52783. columns_hash
  52784. end
  52785. # Returns a hash where the keys are column names and the values are
  52786. # default values when instantiating the AR object for this table.
  52787. def column_defaults
  52788. @column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
  52789. end
  52790. # Returns an array of column names as strings.
  52791. def column_names
  52792. @column_names ||= columns.map { |column| column.name }
  52793. end
  52794. # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
  52795. # and columns used for single table inheritance have been removed.
  52796. def content_columns
  52797. @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
  52798. end
  52799. # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
  52800. # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
  52801. # is available.
  52802. def column_methods_hash #:nodoc:
  52803. @dynamic_methods_hash ||= column_names.each_with_object(Hash.new(false)) do |attr, methods|
  52804. attr_name = attr.to_s
  52805. methods[attr.to_sym] = attr_name
  52806. methods["#{attr}=".to_sym] = attr_name
  52807. methods["#{attr}?".to_sym] = attr_name
  52808. methods["#{attr}_before_type_cast".to_sym] = attr_name
  52809. end
  52810. end
  52811. # Resets all the cached information about columns, which will cause them
  52812. # to be reloaded on the next request.
  52813. #
  52814. # The most common usage pattern for this method is probably in a migration,
  52815. # when just after creating a table you want to populate it with some default
  52816. # values, eg:
  52817. #
  52818. # class CreateJobLevels < ActiveRecord::Migration
  52819. # def up
  52820. # create_table :job_levels do |t|
  52821. # t.integer :id
  52822. # t.string :name
  52823. #
  52824. # t.timestamps
  52825. # end
  52826. #
  52827. # JobLevel.reset_column_information
  52828. # %w{assistant executive manager director}.each do |type|
  52829. # JobLevel.create(name: type)
  52830. # end
  52831. # end
  52832. #
  52833. # def down
  52834. # drop_table :job_levels
  52835. # end
  52836. # end
  52837. def reset_column_information
  52838. connection.clear_cache!
  52839. undefine_attribute_methods
  52840. connection.schema_cache.clear_table_cache!(table_name) if table_exists?
  52841. @arel_engine = nil
  52842. @column_defaults = nil
  52843. @column_names = nil
  52844. @columns = nil
  52845. @columns_hash = nil
  52846. @column_types = nil
  52847. @content_columns = nil
  52848. @dynamic_methods_hash = nil
  52849. @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
  52850. @relation = nil
  52851. end
  52852. # This is a hook for use by modules that need to do extra stuff to
  52853. # attributes when they are initialized. (e.g. attribute
  52854. # serialization)
  52855. def initialize_attributes(attributes, options = {}) #:nodoc:
  52856. attributes
  52857. end
  52858. private
  52859. # Guesses the table name, but does not decorate it with prefix and suffix information.
  52860. def undecorated_table_name(class_name = base_class.name)
  52861. table_name = class_name.to_s.demodulize.underscore
  52862. pluralize_table_names ? table_name.pluralize : table_name
  52863. end
  52864. # Computes and returns a table name according to default conventions.
  52865. def compute_table_name
  52866. base = base_class
  52867. if self == base
  52868. # Nested classes are prefixed with singular parent table name.
  52869. if parent < Base && !parent.abstract_class?
  52870. contained = parent.table_name
  52871. contained = contained.singularize if parent.pluralize_table_names
  52872. contained += '_'
  52873. end
  52874. "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
  52875. else
  52876. # STI subclasses always use their superclass' table.
  52877. base.table_name
  52878. end
  52879. end
  52880. end
  52881. end
  52882. end
  52883. require 'active_support/core_ext/hash/except'
  52884. require 'active_support/core_ext/object/try'
  52885. require 'active_support/core_ext/hash/indifferent_access'
  52886. module ActiveRecord
  52887. module NestedAttributes #:nodoc:
  52888. class TooManyRecords < ActiveRecordError
  52889. end
  52890. extend ActiveSupport::Concern
  52891. included do
  52892. class_attribute :nested_attributes_options, instance_writer: false
  52893. self.nested_attributes_options = {}
  52894. end
  52895. # = Active Record Nested Attributes
  52896. #
  52897. # Nested attributes allow you to save attributes on associated records
  52898. # through the parent. By default nested attribute updating is turned off
  52899. # and you can enable it using the accepts_nested_attributes_for class
  52900. # method. When you enable nested attributes an attribute writer is
  52901. # defined on the model.
  52902. #
  52903. # The attribute writer is named after the association, which means that
  52904. # in the following example, two new methods are added to your model:
  52905. #
  52906. # <tt>author_attributes=(attributes)</tt> and
  52907. # <tt>pages_attributes=(attributes)</tt>.
  52908. #
  52909. # class Book < ActiveRecord::Base
  52910. # has_one :author
  52911. # has_many :pages
  52912. #
  52913. # accepts_nested_attributes_for :author, :pages
  52914. # end
  52915. #
  52916. # Note that the <tt>:autosave</tt> option is automatically enabled on every
  52917. # association that accepts_nested_attributes_for is used for.
  52918. #
  52919. # === One-to-one
  52920. #
  52921. # Consider a Member model that has one Avatar:
  52922. #
  52923. # class Member < ActiveRecord::Base
  52924. # has_one :avatar
  52925. # accepts_nested_attributes_for :avatar
  52926. # end
  52927. #
  52928. # Enabling nested attributes on a one-to-one association allows you to
  52929. # create the member and avatar in one go:
  52930. #
  52931. # params = { member: { name: 'Jack', avatar_attributes: { icon: 'smiling' } } }
  52932. # member = Member.create(params[:member])
  52933. # member.avatar.id # => 2
  52934. # member.avatar.icon # => 'smiling'
  52935. #
  52936. # It also allows you to update the avatar through the member:
  52937. #
  52938. # params = { member: { avatar_attributes: { id: '2', icon: 'sad' } } }
  52939. # member.update params[:member]
  52940. # member.avatar.icon # => 'sad'
  52941. #
  52942. # By default you will only be able to set and update attributes on the
  52943. # associated model. If you want to destroy the associated model through the
  52944. # attributes hash, you have to enable it first using the
  52945. # <tt>:allow_destroy</tt> option.
  52946. #
  52947. # class Member < ActiveRecord::Base
  52948. # has_one :avatar
  52949. # accepts_nested_attributes_for :avatar, allow_destroy: true
  52950. # end
  52951. #
  52952. # Now, when you add the <tt>_destroy</tt> key to the attributes hash, with a
  52953. # value that evaluates to +true+, you will destroy the associated model:
  52954. #
  52955. # member.avatar_attributes = { id: '2', _destroy: '1' }
  52956. # member.avatar.marked_for_destruction? # => true
  52957. # member.save
  52958. # member.reload.avatar # => nil
  52959. #
  52960. # Note that the model will _not_ be destroyed until the parent is saved.
  52961. #
  52962. # === One-to-many
  52963. #
  52964. # Consider a member that has a number of posts:
  52965. #
  52966. # class Member < ActiveRecord::Base
  52967. # has_many :posts
  52968. # accepts_nested_attributes_for :posts
  52969. # end
  52970. #
  52971. # You can now set or update attributes on an associated post model through
  52972. # the attribute hash.
  52973. #
  52974. # For each hash that does _not_ have an <tt>id</tt> key a new record will
  52975. # be instantiated, unless the hash also contains a <tt>_destroy</tt> key
  52976. # that evaluates to +true+.
  52977. #
  52978. # params = { member: {
  52979. # name: 'joe', posts_attributes: [
  52980. # { title: 'Kari, the awesome Ruby documentation browser!' },
  52981. # { title: 'The egalitarian assumption of the modern citizen' },
  52982. # { title: '', _destroy: '1' } # this will be ignored
  52983. # ]
  52984. # }}
  52985. #
  52986. # member = Member.create(params[:member])
  52987. # member.posts.length # => 2
  52988. # member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
  52989. # member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
  52990. #
  52991. # You may also set a :reject_if proc to silently ignore any new record
  52992. # hashes if they fail to pass your criteria. For example, the previous
  52993. # example could be rewritten as:
  52994. #
  52995. # class Member < ActiveRecord::Base
  52996. # has_many :posts
  52997. # accepts_nested_attributes_for :posts, reject_if: proc { |attributes| attributes['title'].blank? }
  52998. # end
  52999. #
  53000. # params = { member: {
  53001. # name: 'joe', posts_attributes: [
  53002. # { title: 'Kari, the awesome Ruby documentation browser!' },
  53003. # { title: 'The egalitarian assumption of the modern citizen' },
  53004. # { title: '' } # this will be ignored because of the :reject_if proc
  53005. # ]
  53006. # }}
  53007. #
  53008. # member = Member.create(params[:member])
  53009. # member.posts.length # => 2
  53010. # member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
  53011. # member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
  53012. #
  53013. # Alternatively, :reject_if also accepts a symbol for using methods:
  53014. #
  53015. # class Member < ActiveRecord::Base
  53016. # has_many :posts
  53017. # accepts_nested_attributes_for :posts, reject_if: :new_record?
  53018. # end
  53019. #
  53020. # class Member < ActiveRecord::Base
  53021. # has_many :posts
  53022. # accepts_nested_attributes_for :posts, reject_if: :reject_posts
  53023. #
  53024. # def reject_posts(attributed)
  53025. # attributed['title'].blank?
  53026. # end
  53027. # end
  53028. #
  53029. # If the hash contains an <tt>id</tt> key that matches an already
  53030. # associated record, the matching record will be modified:
  53031. #
  53032. # member.attributes = {
  53033. # name: 'Joe',
  53034. # posts_attributes: [
  53035. # { id: 1, title: '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!' },
  53036. # { id: 2, title: '[UPDATED] other post' }
  53037. # ]
  53038. # }
  53039. #
  53040. # member.posts.first.title # => '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!'
  53041. # member.posts.second.title # => '[UPDATED] other post'
  53042. #
  53043. # By default the associated records are protected from being destroyed. If
  53044. # you want to destroy any of the associated records through the attributes
  53045. # hash, you have to enable it first using the <tt>:allow_destroy</tt>
  53046. # option. This will allow you to also use the <tt>_destroy</tt> key to
  53047. # destroy existing records:
  53048. #
  53049. # class Member < ActiveRecord::Base
  53050. # has_many :posts
  53051. # accepts_nested_attributes_for :posts, allow_destroy: true
  53052. # end
  53053. #
  53054. # params = { member: {
  53055. # posts_attributes: [{ id: '2', _destroy: '1' }]
  53056. # }}
  53057. #
  53058. # member.attributes = params[:member]
  53059. # member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
  53060. # member.posts.length # => 2
  53061. # member.save
  53062. # member.reload.posts.length # => 1
  53063. #
  53064. # === Saving
  53065. #
  53066. # All changes to models, including the destruction of those marked for
  53067. # destruction, are saved and destroyed automatically and atomically when
  53068. # the parent model is saved. This happens inside the transaction initiated
  53069. # by the parents save method. See ActiveRecord::AutosaveAssociation.
  53070. #
  53071. # === Validating the presence of a parent model
  53072. #
  53073. # If you want to validate that a child record is associated with a parent
  53074. # record, you can use <tt>validates_presence_of</tt> and
  53075. # <tt>inverse_of</tt> as this example illustrates:
  53076. #
  53077. # class Member < ActiveRecord::Base
  53078. # has_many :posts, inverse_of: :member
  53079. # accepts_nested_attributes_for :posts
  53080. # end
  53081. #
  53082. # class Post < ActiveRecord::Base
  53083. # belongs_to :member, inverse_of: :posts
  53084. # validates_presence_of :member
  53085. # end
  53086. module ClassMethods
  53087. REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
  53088. # Defines an attributes writer for the specified association(s).
  53089. #
  53090. # Supported options:
  53091. # [:allow_destroy]
  53092. # If true, destroys any members from the attributes hash with a
  53093. # <tt>_destroy</tt> key and a value that evaluates to +true+
  53094. # (eg. 1, '1', true, or 'true'). This option is off by default.
  53095. # [:reject_if]
  53096. # Allows you to specify a Proc or a Symbol pointing to a method
  53097. # that checks whether a record should be built for a certain attribute
  53098. # hash. The hash is passed to the supplied Proc or the method
  53099. # and it should return either +true+ or +false+. When no :reject_if
  53100. # is specified, a record will be built for all attribute hashes that
  53101. # do not have a <tt>_destroy</tt> value that evaluates to true.
  53102. # Passing <tt>:all_blank</tt> instead of a Proc will create a proc
  53103. # that will reject a record where all the attributes are blank excluding
  53104. # any value for _destroy.
  53105. # [:limit]
  53106. # Allows you to specify the maximum number of the associated records that
  53107. # can be processed with the nested attributes. Limit also can be specified as a
  53108. # Proc or a Symbol pointing to a method that should return number. If the size of the
  53109. # nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
  53110. # exception is raised. If omitted, any number associations can be processed.
  53111. # Note that the :limit option is only applicable to one-to-many associations.
  53112. # [:update_only]
  53113. # For a one-to-one association, this option allows you to specify how
  53114. # nested attributes are to be used when an associated record already
  53115. # exists. In general, an existing record may either be updated with the
  53116. # new set of attribute values or be replaced by a wholly new record
  53117. # containing those values. By default the :update_only option is +false+
  53118. # and the nested attributes are used to update the existing record only
  53119. # if they include the record's <tt>:id</tt> value. Otherwise a new
  53120. # record will be instantiated and used to replace the existing one.
  53121. # However if the :update_only option is +true+, the nested attributes
  53122. # are used to update the record's attributes always, regardless of
  53123. # whether the <tt>:id</tt> is present. The option is ignored for collection
  53124. # associations.
  53125. #
  53126. # Examples:
  53127. # # creates avatar_attributes=
  53128. # accepts_nested_attributes_for :avatar, reject_if: proc { |attributes| attributes['name'].blank? }
  53129. # # creates avatar_attributes=
  53130. # accepts_nested_attributes_for :avatar, reject_if: :all_blank
  53131. # # creates avatar_attributes= and posts_attributes=
  53132. # accepts_nested_attributes_for :avatar, :posts, allow_destroy: true
  53133. def accepts_nested_attributes_for(*attr_names)
  53134. options = { :allow_destroy => false, :update_only => false }
  53135. options.update(attr_names.extract_options!)
  53136. options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
  53137. options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
  53138. attr_names.each do |association_name|
  53139. if reflection = reflect_on_association(association_name)
  53140. reflection.options[:autosave] = true
  53141. add_autosave_association_callbacks(reflection)
  53142. nested_attributes_options = self.nested_attributes_options.dup
  53143. nested_attributes_options[association_name.to_sym] = options
  53144. self.nested_attributes_options = nested_attributes_options
  53145. type = (reflection.collection? ? :collection : :one_to_one)
  53146. # def pirate_attributes=(attributes)
  53147. # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
  53148. # end
  53149. generated_feature_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
  53150. if method_defined?(:#{association_name}_attributes=)
  53151. remove_method(:#{association_name}_attributes=)
  53152. end
  53153. def #{association_name}_attributes=(attributes)
  53154. assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
  53155. end
  53156. eoruby
  53157. else
  53158. raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
  53159. end
  53160. end
  53161. end
  53162. end
  53163. # Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
  53164. # used in conjunction with fields_for to build a form element for the
  53165. # destruction of this association.
  53166. #
  53167. # See ActionView::Helpers::FormHelper::fields_for for more info.
  53168. def _destroy
  53169. marked_for_destruction?
  53170. end
  53171. private
  53172. # Attribute hash keys that should not be assigned as normal attributes.
  53173. # These hash keys are nested attributes implementation details.
  53174. UNASSIGNABLE_KEYS = %w( id _destroy )
  53175. # Assigns the given attributes to the association.
  53176. #
  53177. # If an associated record does not yet exist, one will be instantiated. If
  53178. # an associated record already exists, the method's behavior depends on
  53179. # the value of the update_only option. If update_only is +false+ and the
  53180. # given attributes include an <tt>:id</tt> that matches the existing record's
  53181. # id, then the existing record will be modified. If no <tt>:id</tt> is provided
  53182. # it will be replaced with a new record. If update_only is +true+ the existing
  53183. # record will be modified regardless of whether an <tt>:id</tt> is provided.
  53184. #
  53185. # If the given attributes include a matching <tt>:id</tt> attribute, or
  53186. # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
  53187. # then the existing record will be marked for destruction.
  53188. def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
  53189. options = self.nested_attributes_options[association_name]
  53190. attributes = attributes.with_indifferent_access
  53191. if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) &&
  53192. (options[:update_only] || record.id.to_s == attributes['id'].to_s)
  53193. assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
  53194. elsif attributes['id'].present?
  53195. raise_nested_attributes_record_not_found(association_name, attributes['id'])
  53196. elsif !reject_new_record?(association_name, attributes)
  53197. method = "build_#{association_name}"
  53198. if respond_to?(method)
  53199. send(method, attributes.except(*UNASSIGNABLE_KEYS))
  53200. else
  53201. raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
  53202. end
  53203. end
  53204. end
  53205. # Assigns the given attributes to the collection association.
  53206. #
  53207. # Hashes with an <tt>:id</tt> value matching an existing associated record
  53208. # will update that record. Hashes without an <tt>:id</tt> value will build
  53209. # a new record for the association. Hashes with a matching <tt>:id</tt>
  53210. # value and a <tt>:_destroy</tt> key set to a truthy value will mark the
  53211. # matched record for destruction.
  53212. #
  53213. # For example:
  53214. #
  53215. # assign_nested_attributes_for_collection_association(:people, {
  53216. # '1' => { id: '1', name: 'Peter' },
  53217. # '2' => { name: 'John' },
  53218. # '3' => { id: '2', _destroy: true }
  53219. # })
  53220. #
  53221. # Will update the name of the Person with ID 1, build a new associated
  53222. # person with the name 'John', and mark the associated Person with ID 2
  53223. # for destruction.
  53224. #
  53225. # Also accepts an Array of attribute hashes:
  53226. #
  53227. # assign_nested_attributes_for_collection_association(:people, [
  53228. # { id: '1', name: 'Peter' },
  53229. # { name: 'John' },
  53230. # { id: '2', _destroy: true }
  53231. # ])
  53232. def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
  53233. options = self.nested_attributes_options[association_name]
  53234. unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
  53235. raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
  53236. end
  53237. if limit = options[:limit]
  53238. limit = case limit
  53239. when Symbol
  53240. send(limit)
  53241. when Proc
  53242. limit.call
  53243. else
  53244. limit
  53245. end
  53246. if limit && attributes_collection.size > limit
  53247. raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
  53248. end
  53249. end
  53250. if attributes_collection.is_a? Hash
  53251. keys = attributes_collection.keys
  53252. attributes_collection = if keys.include?('id') || keys.include?(:id)
  53253. [attributes_collection]
  53254. else
  53255. attributes_collection.values
  53256. end
  53257. end
  53258. association = association(association_name)
  53259. existing_records = if association.loaded?
  53260. association.target
  53261. else
  53262. attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
  53263. attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
  53264. end
  53265. attributes_collection.each do |attributes|
  53266. attributes = attributes.with_indifferent_access
  53267. if attributes['id'].blank?
  53268. unless reject_new_record?(association_name, attributes)
  53269. association.build(attributes.except(*UNASSIGNABLE_KEYS))
  53270. end
  53271. elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
  53272. unless association.loaded? || call_reject_if(association_name, attributes)
  53273. # Make sure we are operating on the actual object which is in the association's
  53274. # proxy_target array (either by finding it, or adding it if not found)
  53275. target_record = association.target.detect { |record| record == existing_record }
  53276. if target_record
  53277. existing_record = target_record
  53278. else
  53279. association.add_to_target(existing_record)
  53280. end
  53281. end
  53282. if !call_reject_if(association_name, attributes)
  53283. assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
  53284. end
  53285. else
  53286. raise_nested_attributes_record_not_found(association_name, attributes['id'])
  53287. end
  53288. end
  53289. end
  53290. # Updates a record with the +attributes+ or marks it for destruction if
  53291. # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
  53292. def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
  53293. record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
  53294. record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
  53295. end
  53296. # Determines if a hash contains a truthy _destroy key.
  53297. def has_destroy_flag?(hash)
  53298. ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
  53299. end
  53300. # Determines if a new record should be build by checking for
  53301. # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
  53302. # association and evaluates to +true+.
  53303. def reject_new_record?(association_name, attributes)
  53304. has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
  53305. end
  53306. def call_reject_if(association_name, attributes)
  53307. return false if has_destroy_flag?(attributes)
  53308. case callback = self.nested_attributes_options[association_name][:reject_if]
  53309. when Symbol
  53310. method(callback).arity == 0 ? send(callback) : send(callback, attributes)
  53311. when Proc
  53312. callback.call(attributes)
  53313. end
  53314. end
  53315. def raise_nested_attributes_record_not_found(association_name, record_id)
  53316. raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
  53317. end
  53318. end
  53319. end
  53320. # -*- coding: utf-8 -*-
  53321. module ActiveRecord
  53322. module NullRelation # :nodoc:
  53323. def exec_queries
  53324. @records = []
  53325. end
  53326. def pluck(_column_name)
  53327. []
  53328. end
  53329. def delete_all(_conditions = nil)
  53330. 0
  53331. end
  53332. def update_all(_updates, _conditions = nil, _options = {})
  53333. 0
  53334. end
  53335. def delete(_id_or_array)
  53336. 0
  53337. end
  53338. def size
  53339. 0
  53340. end
  53341. def empty?
  53342. true
  53343. end
  53344. def any?
  53345. false
  53346. end
  53347. def many?
  53348. false
  53349. end
  53350. def to_sql
  53351. @to_sql ||= ""
  53352. end
  53353. def where_values_hash
  53354. {}
  53355. end
  53356. def count(*)
  53357. 0
  53358. end
  53359. def sum(*)
  53360. 0
  53361. end
  53362. def calculate(_operation, _column_name, _options = {})
  53363. nil
  53364. end
  53365. def exists?(_id = false)
  53366. false
  53367. end
  53368. end
  53369. end
  53370. module ActiveRecord
  53371. # = Active Record Persistence
  53372. module Persistence
  53373. extend ActiveSupport::Concern
  53374. module ClassMethods
  53375. # Creates an object (or multiple objects) and saves it to the database, if validations pass.
  53376. # The resulting object is returned whether the object was saved successfully to the database or not.
  53377. #
  53378. # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
  53379. # attributes on the objects that are to be created.
  53380. #
  53381. # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
  53382. # in the +options+ parameter.
  53383. #
  53384. # ==== Examples
  53385. # # Create a single new object
  53386. # User.create(first_name: 'Jamie')
  53387. #
  53388. # # Create an Array of new objects
  53389. # User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
  53390. #
  53391. # # Create a single object and pass it into a block to set other attributes.
  53392. # User.create(first_name: 'Jamie') do |u|
  53393. # u.is_admin = false
  53394. # end
  53395. #
  53396. # # Creating an Array of new objects using a block, where the block is executed for each object:
  53397. # User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
  53398. # u.is_admin = false
  53399. # end
  53400. def create(attributes = nil, &block)
  53401. if attributes.is_a?(Array)
  53402. attributes.collect { |attr| create(attr, &block) }
  53403. else
  53404. object = new(attributes, &block)
  53405. object.save
  53406. object
  53407. end
  53408. end
  53409. # Given an attributes hash, +instantiate+ returns a new instance of
  53410. # the appropriate class.
  53411. #
  53412. # For example, +Post.all+ may return Comments, Messages, and Emails
  53413. # by storing the record's subclass in a +type+ attribute. By calling
  53414. # +instantiate+ instead of +new+, finder methods ensure they get new
  53415. # instances of the appropriate class for each record.
  53416. #
  53417. # See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
  53418. # how this "single-table" inheritance mapping is implemented.
  53419. def instantiate(record, column_types = {})
  53420. klass = discriminate_class_for_record(record)
  53421. column_types = klass.decorate_columns(column_types)
  53422. klass.allocate.init_with('attributes' => record, 'column_types' => column_types)
  53423. end
  53424. private
  53425. # Called by +instantiate+ to decide which class to use for a new
  53426. # record instance.
  53427. #
  53428. # See +ActiveRecord::Inheritance#discriminate_class_for_record+ for
  53429. # the single-table inheritance discriminator.
  53430. def discriminate_class_for_record(record)
  53431. self
  53432. end
  53433. end
  53434. # Returns true if this object hasn't been saved yet -- that is, a record
  53435. # for the object doesn't exist in the data store yet; otherwise, returns false.
  53436. def new_record?
  53437. @new_record
  53438. end
  53439. # Returns true if this object has been destroyed, otherwise returns false.
  53440. def destroyed?
  53441. @destroyed
  53442. end
  53443. # Returns true if the record is persisted, i.e. it's not a new record and it was
  53444. # not destroyed, otherwise returns false.
  53445. def persisted?
  53446. !(new_record? || destroyed?)
  53447. end
  53448. # Saves the model.
  53449. #
  53450. # If the model is new a record gets created in the database, otherwise
  53451. # the existing record gets updated.
  53452. #
  53453. # By default, save always run validations. If any of them fail the action
  53454. # is cancelled and +save+ returns +false+. However, if you supply
  53455. # validate: false, validations are bypassed altogether. See
  53456. # ActiveRecord::Validations for more information.
  53457. #
  53458. # There's a series of callbacks associated with +save+. If any of the
  53459. # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
  53460. # +save+ returns +false+. See ActiveRecord::Callbacks for further
  53461. # details.
  53462. def save(*)
  53463. create_or_update
  53464. rescue ActiveRecord::RecordInvalid
  53465. false
  53466. end
  53467. # Saves the model.
  53468. #
  53469. # If the model is new a record gets created in the database, otherwise
  53470. # the existing record gets updated.
  53471. #
  53472. # With <tt>save!</tt> validations always run. If any of them fail
  53473. # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
  53474. # for more information.
  53475. #
  53476. # There's a series of callbacks associated with <tt>save!</tt>. If any of
  53477. # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
  53478. # and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
  53479. # ActiveRecord::Callbacks for further details.
  53480. def save!(*)
  53481. create_or_update || raise(RecordNotSaved)
  53482. end
  53483. # Deletes the record in the database and freezes this instance to
  53484. # reflect that no changes should be made (since they can't be
  53485. # persisted). Returns the frozen instance.
  53486. #
  53487. # The row is simply removed with an SQL +DELETE+ statement on the
  53488. # record's primary key, and no callbacks are executed.
  53489. #
  53490. # To enforce the object's +before_destroy+ and +after_destroy+
  53491. # callbacks or any <tt>:dependent</tt> association
  53492. # options, use <tt>#destroy</tt>.
  53493. def delete
  53494. self.class.delete(id) if persisted?
  53495. @destroyed = true
  53496. freeze
  53497. end
  53498. # Deletes the record in the database and freezes this instance to reflect
  53499. # that no changes should be made (since they can't be persisted).
  53500. #
  53501. # There's a series of callbacks associated with <tt>destroy</tt>. If
  53502. # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
  53503. # and <tt>destroy</tt> returns +false+. See
  53504. # ActiveRecord::Callbacks for further details.
  53505. def destroy
  53506. raise ReadOnlyRecord if readonly?
  53507. destroy_associations
  53508. destroy_row if persisted?
  53509. @destroyed = true
  53510. freeze
  53511. end
  53512. # Deletes the record in the database and freezes this instance to reflect
  53513. # that no changes should be made (since they can't be persisted).
  53514. #
  53515. # There's a series of callbacks associated with <tt>destroy!</tt>. If
  53516. # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
  53517. # and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
  53518. # ActiveRecord::Callbacks for further details.
  53519. def destroy!
  53520. destroy || raise(ActiveRecord::RecordNotDestroyed)
  53521. end
  53522. # Returns an instance of the specified +klass+ with the attributes of the
  53523. # current record. This is mostly useful in relation to single-table
  53524. # inheritance structures where you want a subclass to appear as the
  53525. # superclass. This can be used along with record identification in
  53526. # Action Pack to allow, say, <tt>Client < Company</tt> to do something
  53527. # like render <tt>partial: @client.becomes(Company)</tt> to render that
  53528. # instance using the companies/company partial instead of clients/client.
  53529. #
  53530. # Note: The new instance will share a link to the same attributes as the original class.
  53531. # So any change to the attributes in either instance will affect the other.
  53532. def becomes(klass)
  53533. became = klass.new
  53534. became.instance_variable_set("@attributes", @attributes)
  53535. became.instance_variable_set("@attributes_cache", @attributes_cache)
  53536. became.instance_variable_set("@new_record", new_record?)
  53537. became.instance_variable_set("@destroyed", destroyed?)
  53538. became.instance_variable_set("@errors", errors)
  53539. became
  53540. end
  53541. # Wrapper around +becomes+ that also changes the instance's sti column value.
  53542. # This is especially useful if you want to persist the changed class in your
  53543. # database.
  53544. #
  53545. # Note: The old instance's sti column value will be changed too, as both objects
  53546. # share the same set of attributes.
  53547. def becomes!(klass)
  53548. became = becomes(klass)
  53549. became.public_send("#{klass.inheritance_column}=", klass.sti_name) unless self.class.descends_from_active_record?
  53550. became
  53551. end
  53552. # Updates a single attribute and saves the record.
  53553. # This is especially useful for boolean flags on existing records. Also note that
  53554. #
  53555. # * Validation is skipped.
  53556. # * Callbacks are invoked.
  53557. # * updated_at/updated_on column is updated if that column is available.
  53558. # * Updates all the attributes that are dirty in this object.
  53559. #
  53560. def update_attribute(name, value)
  53561. name = name.to_s
  53562. verify_readonly_attribute(name)
  53563. send("#{name}=", value)
  53564. save(validate: false)
  53565. end
  53566. # Updates the attributes of the model from the passed-in hash and saves the
  53567. # record, all wrapped in a transaction. If the object is invalid, the saving
  53568. # will fail and false will be returned.
  53569. def update(attributes)
  53570. # The following transaction covers any possible database side-effects of the
  53571. # attributes assignment. For example, setting the IDs of a child collection.
  53572. with_transaction_returning_status do
  53573. assign_attributes(attributes)
  53574. save
  53575. end
  53576. end
  53577. alias update_attributes update
  53578. # Updates its receiver just like +update+ but calls <tt>save!</tt> instead
  53579. # of +save+, so an exception is raised if the record is invalid.
  53580. def update!(attributes)
  53581. # The following transaction covers any possible database side-effects of the
  53582. # attributes assignment. For example, setting the IDs of a child collection.
  53583. with_transaction_returning_status do
  53584. assign_attributes(attributes)
  53585. save!
  53586. end
  53587. end
  53588. alias update_attributes! update!
  53589. # Equivalent to <code>update_columns(name => value)</code>.
  53590. def update_column(name, value)
  53591. update_columns(name => value)
  53592. end
  53593. # Updates the attributes directly in the database issuing an UPDATE SQL
  53594. # statement and sets them in the receiver:
  53595. #
  53596. # user.update_columns(last_request_at: Time.current)
  53597. #
  53598. # This is the fastest way to update attributes because it goes straight to
  53599. # the database, but take into account that in consequence the regular update
  53600. # procedures are totally bypassed. In particular:
  53601. #
  53602. # * Validations are skipped.
  53603. # * Callbacks are skipped.
  53604. # * +updated_at+/+updated_on+ are not updated.
  53605. #
  53606. # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
  53607. # objects, or when at least one of the attributes is marked as readonly.
  53608. def update_columns(attributes)
  53609. raise ActiveRecordError, "can not update on a new record object" unless persisted?
  53610. attributes.each_key do |key|
  53611. verify_readonly_attribute(key.to_s)
  53612. end
  53613. updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
  53614. attributes.each do |k, v|
  53615. raw_write_attribute(k, v)
  53616. end
  53617. updated_count == 1
  53618. end
  53619. # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
  53620. # The increment is performed directly on the underlying attribute, no setter is invoked.
  53621. # Only makes sense for number-based attributes. Returns +self+.
  53622. def increment(attribute, by = 1)
  53623. self[attribute] ||= 0
  53624. self[attribute] += by
  53625. self
  53626. end
  53627. # Wrapper around +increment+ that saves the record. This method differs from
  53628. # its non-bang version in that it passes through the attribute setter.
  53629. # Saving is not subjected to validation checks. Returns +true+ if the
  53630. # record could be saved.
  53631. def increment!(attribute, by = 1)
  53632. increment(attribute, by).update_attribute(attribute, self[attribute])
  53633. end
  53634. # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
  53635. # The decrement is performed directly on the underlying attribute, no setter is invoked.
  53636. # Only makes sense for number-based attributes. Returns +self+.
  53637. def decrement(attribute, by = 1)
  53638. self[attribute] ||= 0
  53639. self[attribute] -= by
  53640. self
  53641. end
  53642. # Wrapper around +decrement+ that saves the record. This method differs from
  53643. # its non-bang version in that it passes through the attribute setter.
  53644. # Saving is not subjected to validation checks. Returns +true+ if the
  53645. # record could be saved.
  53646. def decrement!(attribute, by = 1)
  53647. decrement(attribute, by).update_attribute(attribute, self[attribute])
  53648. end
  53649. # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
  53650. # if the predicate returns +true+ the attribute will become +false+. This
  53651. # method toggles directly the underlying value without calling any setter.
  53652. # Returns +self+.
  53653. def toggle(attribute)
  53654. self[attribute] = !send("#{attribute}?")
  53655. self
  53656. end
  53657. # Wrapper around +toggle+ that saves the record. This method differs from
  53658. # its non-bang version in that it passes through the attribute setter.
  53659. # Saving is not subjected to validation checks. Returns +true+ if the
  53660. # record could be saved.
  53661. def toggle!(attribute)
  53662. toggle(attribute).update_attribute(attribute, self[attribute])
  53663. end
  53664. # Reloads the attributes of this object from the database.
  53665. # The optional options argument is passed to find when reloading so you
  53666. # may do e.g. record.reload(lock: true) to reload the same record with
  53667. # an exclusive row lock.
  53668. def reload(options = nil)
  53669. clear_aggregation_cache
  53670. clear_association_cache
  53671. fresh_object =
  53672. if options && options[:lock]
  53673. self.class.unscoped { self.class.lock.find(id) }
  53674. else
  53675. self.class.unscoped { self.class.find(id) }
  53676. end
  53677. @attributes.update(fresh_object.instance_variable_get('@attributes'))
  53678. @columns_hash = fresh_object.instance_variable_get('@columns_hash')
  53679. @attributes_cache = {}
  53680. self
  53681. end
  53682. # Saves the record with the updated_at/on attributes set to the current time.
  53683. # Please note that no validation is performed and no callbacks are executed.
  53684. # If an attribute name is passed, that attribute is updated along with
  53685. # updated_at/on attributes.
  53686. #
  53687. # product.touch # updates updated_at/on
  53688. # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
  53689. #
  53690. # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on associated object.
  53691. #
  53692. # class Brake < ActiveRecord::Base
  53693. # belongs_to :car, touch: true
  53694. # end
  53695. #
  53696. # class Car < ActiveRecord::Base
  53697. # belongs_to :corporation, touch: true
  53698. # end
  53699. #
  53700. # # triggers @brake.car.touch and @brake.car.corporation.touch
  53701. # @brake.touch
  53702. def touch(name = nil)
  53703. attributes = timestamp_attributes_for_update_in_model
  53704. attributes << name if name
  53705. unless attributes.empty?
  53706. current_time = current_time_from_proper_timezone
  53707. changes = {}
  53708. attributes.each do |column|
  53709. column = column.to_s
  53710. changes[column] = write_attribute(column, current_time)
  53711. end
  53712. changes[self.class.locking_column] = increment_lock if locking_enabled?
  53713. @changed_attributes.except!(*changes.keys)
  53714. primary_key = self.class.primary_key
  53715. self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
  53716. end
  53717. end
  53718. private
  53719. # A hook to be overridden by association modules.
  53720. def destroy_associations
  53721. end
  53722. def destroy_row
  53723. relation_for_destroy.delete_all
  53724. end
  53725. def relation_for_destroy
  53726. pk = self.class.primary_key
  53727. column = self.class.columns_hash[pk]
  53728. substitute = connection.substitute_at(column, 0)
  53729. relation = self.class.unscoped.where(
  53730. self.class.arel_table[pk].eq(substitute))
  53731. relation.bind_values = [[column, id]]
  53732. relation
  53733. end
  53734. def create_or_update
  53735. raise ReadOnlyRecord if readonly?
  53736. result = new_record? ? create_record : update_record
  53737. result != false
  53738. end
  53739. # Updates the associated record with values matching those of the instance attributes.
  53740. # Returns the number of affected rows.
  53741. def update_record(attribute_names = @attributes.keys)
  53742. attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
  53743. if attributes_with_values.empty?
  53744. 0
  53745. else
  53746. klass = self.class
  53747. stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
  53748. klass.connection.update stmt
  53749. end
  53750. end
  53751. # Creates a record with values matching those of the instance attributes
  53752. # and returns its id.
  53753. def create_record(attribute_names = @attributes.keys)
  53754. attributes_values = arel_attributes_with_values_for_create(attribute_names)
  53755. new_id = self.class.unscoped.insert attributes_values
  53756. self.id ||= new_id if self.class.primary_key
  53757. @new_record = false
  53758. id
  53759. end
  53760. def verify_readonly_attribute(name)
  53761. raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
  53762. end
  53763. end
  53764. end
  53765. module ActiveRecord
  53766. # = Active Record Query Cache
  53767. class QueryCache
  53768. module ClassMethods
  53769. # Enable the query cache within the block if Active Record is configured.
  53770. # If it's not, it will execute the given block.
  53771. def cache(&block)
  53772. if ActiveRecord::Base.connected?
  53773. connection.cache(&block)
  53774. else
  53775. yield
  53776. end
  53777. end
  53778. # Disable the query cache within the block if Active Record is configured.
  53779. # If it's not, it will execute the given block.
  53780. def uncached(&block)
  53781. if ActiveRecord::Base.connected?
  53782. connection.uncached(&block)
  53783. else
  53784. yield
  53785. end
  53786. end
  53787. end
  53788. def initialize(app)
  53789. @app = app
  53790. end
  53791. def call(env)
  53792. enabled = ActiveRecord::Base.connection.query_cache_enabled
  53793. connection_id = ActiveRecord::Base.connection_id
  53794. ActiveRecord::Base.connection.enable_query_cache!
  53795. response = @app.call(env)
  53796. response[2] = Rack::BodyProxy.new(response[2]) do
  53797. restore_query_cache_settings(connection_id, enabled)
  53798. end
  53799. response
  53800. rescue Exception => e
  53801. restore_query_cache_settings(connection_id, enabled)
  53802. raise e
  53803. end
  53804. private
  53805. def restore_query_cache_settings(connection_id, enabled)
  53806. ActiveRecord::Base.connection_id = connection_id
  53807. ActiveRecord::Base.connection.clear_query_cache
  53808. ActiveRecord::Base.connection.disable_query_cache! unless enabled
  53809. end
  53810. end
  53811. end
  53812. module ActiveRecord
  53813. module Querying
  53814. delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :to => :all
  53815. delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :all
  53816. delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :to => :all
  53817. delegate :find_by, :find_by!, :to => :all
  53818. delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :all
  53819. delegate :find_each, :find_in_batches, :to => :all
  53820. delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
  53821. :where, :preload, :eager_load, :includes, :from, :lock, :readonly,
  53822. :having, :create_with, :uniq, :references, :none, :to => :all
  53823. delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :ids, :to => :all
  53824. # Executes a custom SQL query against your database and returns all the results. The results will
  53825. # be returned as an array with columns requested encapsulated as attributes of the model you call
  53826. # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
  53827. # a Product object with the attributes you specified in the SQL query.
  53828. #
  53829. # If you call a complicated SQL query which spans multiple tables the columns specified by the
  53830. # SELECT will be attributes of the model, whether or not they are columns of the corresponding
  53831. # table.
  53832. #
  53833. # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
  53834. # no database agnostic conversions performed. This should be a last resort because using, for example,
  53835. # MySQL specific terms will lock you to using that particular database engine or require you to
  53836. # change your call if you switch engines.
  53837. #
  53838. # # A simple SQL query spanning multiple tables
  53839. # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
  53840. # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
  53841. #
  53842. # # You can use the same string replacement techniques as you can with ActiveRecord#find
  53843. # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
  53844. # # => [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
  53845. def find_by_sql(sql, binds = [])
  53846. logging_query_plan do
  53847. result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
  53848. column_types = {}
  53849. if result_set.respond_to? :column_types
  53850. column_types = result_set.column_types
  53851. else
  53852. ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`"
  53853. end
  53854. result_set.map { |record| instantiate(record, column_types) }
  53855. end
  53856. end
  53857. # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
  53858. # The use of this method should be restricted to complicated SQL queries that can't be executed
  53859. # using the ActiveRecord::Calculations class methods. Look into those before using this.
  53860. #
  53861. # ==== Parameters
  53862. #
  53863. # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
  53864. #
  53865. # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
  53866. def count_by_sql(sql)
  53867. logging_query_plan do
  53868. sql = sanitize_conditions(sql)
  53869. connection.select_value(sql, "#{name} Count").to_i
  53870. end
  53871. end
  53872. end
  53873. end
  53874. require "active_record"
  53875. require "rails"
  53876. require "active_model/railtie"
  53877. # For now, action_controller must always be present with
  53878. # rails, so let's make sure that it gets required before
  53879. # here. This is needed for correctly setting up the middleware.
  53880. # In the future, this might become an optional require.
  53881. require "action_controller/railtie"
  53882. module ActiveRecord
  53883. # = Active Record Railtie
  53884. class Railtie < Rails::Railtie # :nodoc:
  53885. config.active_record = ActiveSupport::OrderedOptions.new
  53886. config.app_generators.orm :active_record, :migration => true,
  53887. :timestamps => true
  53888. config.app_middleware.insert_after "::ActionDispatch::Callbacks",
  53889. "ActiveRecord::QueryCache"
  53890. config.app_middleware.insert_after "::ActionDispatch::Callbacks",
  53891. "ActiveRecord::ConnectionAdapters::ConnectionManagement"
  53892. config.action_dispatch.rescue_responses.merge!(
  53893. 'ActiveRecord::RecordNotFound' => :not_found,
  53894. 'ActiveRecord::StaleObjectError' => :conflict,
  53895. 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
  53896. 'ActiveRecord::RecordNotSaved' => :unprocessable_entity
  53897. )
  53898. config.active_record.use_schema_cache_dump = true
  53899. config.eager_load_namespaces << ActiveRecord
  53900. rake_tasks do
  53901. require "active_record/base"
  53902. load "active_record/railties/databases.rake"
  53903. end
  53904. # When loading console, force ActiveRecord::Base to be loaded
  53905. # to avoid cross references when loading a constant for the
  53906. # first time. Also, make it output to STDERR.
  53907. console do |app|
  53908. require "active_record/railties/console_sandbox" if app.sandbox?
  53909. require "active_record/base"
  53910. console = ActiveSupport::Logger.new(STDERR)
  53911. Rails.logger.extend ActiveSupport::Logger.broadcast console
  53912. end
  53913. runner do |app|
  53914. require "active_record/base"
  53915. end
  53916. initializer "active_record.initialize_timezone" do
  53917. ActiveSupport.on_load(:active_record) do
  53918. self.time_zone_aware_attributes = true
  53919. self.default_timezone = :utc
  53920. end
  53921. end
  53922. initializer "active_record.logger" do
  53923. ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
  53924. end
  53925. initializer "active_record.migration_error" do |app|
  53926. if config.active_record.delete(:migration_error) == :page_load
  53927. config.app_middleware.insert_after "::ActionDispatch::Callbacks",
  53928. "ActiveRecord::Migration::CheckPending"
  53929. end
  53930. end
  53931. initializer "active_record.check_schema_cache_dump" do
  53932. if config.active_record.delete(:use_schema_cache_dump)
  53933. config.after_initialize do |app|
  53934. ActiveSupport.on_load(:active_record) do
  53935. filename = File.join(app.config.paths["db"].first, "schema_cache.dump")
  53936. if File.file?(filename)
  53937. cache = Marshal.load File.binread filename
  53938. if cache.version == ActiveRecord::Migrator.current_version
  53939. self.connection.schema_cache = cache
  53940. else
  53941. warn "Ignoring db/schema_cache.dump because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
  53942. end
  53943. end
  53944. end
  53945. end
  53946. end
  53947. end
  53948. initializer "active_record.set_configs" do |app|
  53949. ActiveSupport.on_load(:active_record) do
  53950. begin
  53951. old_behavior, ActiveSupport::Deprecation.behavior = ActiveSupport::Deprecation.behavior, :stderr
  53952. whitelist_attributes = app.config.active_record.delete(:whitelist_attributes)
  53953. if respond_to?(:mass_assignment_sanitizer=)
  53954. mass_assignment_sanitizer = nil
  53955. else
  53956. mass_assignment_sanitizer = app.config.active_record.delete(:mass_assignment_sanitizer)
  53957. end
  53958. unless whitelist_attributes.nil? && mass_assignment_sanitizer.nil?
  53959. ActiveSupport::Deprecation.warn <<-EOF.strip_heredoc, []
  53960. Model based mass assignment security has been extracted
  53961. out of Rails into a gem. Please use the new recommended protection model for
  53962. params or add `protected_attributes` to your Gemfile to use the old one.
  53963. To disable this message remove the `whitelist_attributes` option from your
  53964. `config/application.rb` file and any `mass_assignment_sanitizer` options
  53965. from your `config/environments/*.rb` files.
  53966. See http://guides.rubyonrails.org/security.html#mass-assignment for more information
  53967. EOF
  53968. end
  53969. unless app.config.active_record.delete(:observers).nil?
  53970. ActiveSupport::Deprecation.warn <<-EOF.strip_heredoc, []
  53971. Active Record Observers has been extracted out of Rails into a gem.
  53972. Please use callbacks or add `rails-observers` to your Gemfile to use observers.
  53973. To disable this message remove the `observers` option from your
  53974. `config/application.rb` or from your initializers.
  53975. See http://guides.rubyonrails.org/4_0_release_notes.html for more information
  53976. EOF
  53977. end
  53978. ensure
  53979. ActiveSupport::Deprecation.behavior = old_behavior
  53980. end
  53981. app.config.active_record.each do |k,v|
  53982. send "#{k}=", v
  53983. end
  53984. end
  53985. end
  53986. # This sets the database configuration from Configuration#database_configuration
  53987. # and then establishes the connection.
  53988. initializer "active_record.initialize_database" do |app|
  53989. ActiveSupport.on_load(:active_record) do
  53990. unless ENV['DATABASE_URL']
  53991. self.configurations = app.config.database_configuration
  53992. end
  53993. establish_connection
  53994. end
  53995. end
  53996. initializer "active_record.validate_explain_support" do |app|
  53997. if app.config.active_record[:auto_explain_threshold_in_seconds] &&
  53998. !ActiveRecord::Base.connection.supports_explain?
  53999. warn "auto_explain_threshold_in_seconds is set but will be ignored because your adapter does not support this feature. Please unset the configuration to avoid this warning."
  54000. end
  54001. end
  54002. # Expose database runtime to controller for logging.
  54003. initializer "active_record.log_runtime" do |app|
  54004. require "active_record/railties/controller_runtime"
  54005. ActiveSupport.on_load(:action_controller) do
  54006. include ActiveRecord::Railties::ControllerRuntime
  54007. end
  54008. end
  54009. initializer "active_record.set_reloader_hooks" do |app|
  54010. hook = app.config.reload_classes_only_on_change ? :to_prepare : :to_cleanup
  54011. ActiveSupport.on_load(:active_record) do
  54012. ActionDispatch::Reloader.send(hook) do
  54013. if ActiveRecord::Base.connected?
  54014. ActiveRecord::Base.clear_reloadable_connections!
  54015. ActiveRecord::Base.clear_cache!
  54016. end
  54017. end
  54018. end
  54019. end
  54020. initializer "active_record.add_watchable_files" do |app|
  54021. path = app.paths["db"].first
  54022. config.watchable_files.concat ["#{path}/schema.rb", "#{path}/structure.sql"]
  54023. end
  54024. end
  54025. end
  54026. ActiveRecord::Base.connection.begin_db_transaction
  54027. at_exit do
  54028. ActiveRecord::Base.connection.rollback_db_transaction
  54029. end
  54030. require 'active_support/core_ext/module/attr_internal'
  54031. require 'active_record/log_subscriber'
  54032. module ActiveRecord
  54033. module Railties # :nodoc:
  54034. module ControllerRuntime #:nodoc:
  54035. extend ActiveSupport::Concern
  54036. protected
  54037. attr_internal :db_runtime
  54038. def process_action(action, *args)
  54039. # We also need to reset the runtime before each action
  54040. # because of queries in middleware or in cases we are streaming
  54041. # and it won't be cleaned up by the method below.
  54042. ActiveRecord::LogSubscriber.reset_runtime
  54043. super
  54044. end
  54045. def cleanup_view_runtime
  54046. if ActiveRecord::Base.connected?
  54047. db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
  54048. runtime = super
  54049. db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
  54050. self.db_runtime = db_rt_before_render + db_rt_after_render
  54051. runtime - db_rt_after_render
  54052. else
  54053. super
  54054. end
  54055. end
  54056. def append_info_to_payload(payload)
  54057. super
  54058. if ActiveRecord::Base.connected?
  54059. payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
  54060. end
  54061. end
  54062. module ClassMethods # :nodoc:
  54063. def log_process_action(payload)
  54064. messages, db_runtime = super, payload[:db_runtime]
  54065. messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime
  54066. messages
  54067. end
  54068. end
  54069. end
  54070. end
  54071. end
  54072. #FIXME Remove if ArJdbcMysql will give.
  54073. module ArJdbcMySQL #:nodoc:
  54074. class Error < StandardError #:nodoc:
  54075. attr_accessor :error_number, :sql_state
  54076. def initialize msg
  54077. super
  54078. @error_number = nil
  54079. @sql_state = nil
  54080. end
  54081. # Mysql gem compatibility
  54082. alias_method :errno, :error_number
  54083. alias_method :error, :message
  54084. end
  54085. end
  54086. module ActiveRecord
  54087. module ReadonlyAttributes
  54088. extend ActiveSupport::Concern
  54089. included do
  54090. class_attribute :_attr_readonly, instance_accessor: false
  54091. self._attr_readonly = []
  54092. end
  54093. module ClassMethods
  54094. # Attributes listed as readonly will be used to create a new record but update operations will
  54095. # ignore these fields.
  54096. def attr_readonly(*attributes)
  54097. self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
  54098. end
  54099. # Returns an array of all the attributes that have been specified as readonly.
  54100. def readonly_attributes
  54101. self._attr_readonly
  54102. end
  54103. end
  54104. def _attr_readonly
  54105. message = "Instance level _attr_readonly method is deprecated, please use class level method."
  54106. ActiveSupport::Deprecation.warn message
  54107. defined?(@_attr_readonly) ? @_attr_readonly : self.class._attr_readonly
  54108. end
  54109. end
  54110. end
  54111. module ActiveRecord
  54112. # = Active Record Reflection
  54113. module Reflection # :nodoc:
  54114. extend ActiveSupport::Concern
  54115. included do
  54116. class_attribute :reflections
  54117. self.reflections = {}
  54118. end
  54119. # Reflection enables to interrogate Active Record classes and objects
  54120. # about their associations and aggregations. This information can,
  54121. # for example, be used in a form builder that takes an Active Record object
  54122. # and creates input fields for all of the attributes depending on their type
  54123. # and displays the associations to other objects.
  54124. #
  54125. # MacroReflection class has info for AggregateReflection and AssociationReflection
  54126. # classes.
  54127. module ClassMethods
  54128. def create_reflection(macro, name, scope, options, active_record)
  54129. case macro
  54130. when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
  54131. klass = options[:through] ? ThroughReflection : AssociationReflection
  54132. reflection = klass.new(macro, name, scope, options, active_record)
  54133. when :composed_of
  54134. reflection = AggregateReflection.new(macro, name, scope, options, active_record)
  54135. end
  54136. self.reflections = self.reflections.merge(name => reflection)
  54137. reflection
  54138. end
  54139. # Returns an array of AggregateReflection objects for all the aggregations in the class.
  54140. def reflect_on_all_aggregations
  54141. reflections.values.grep(AggregateReflection)
  54142. end
  54143. # Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
  54144. #
  54145. # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
  54146. #
  54147. def reflect_on_aggregation(aggregation)
  54148. reflection = reflections[aggregation]
  54149. reflection if reflection.is_a?(AggregateReflection)
  54150. end
  54151. # Returns an array of AssociationReflection objects for all the
  54152. # associations in the class. If you only want to reflect on a certain
  54153. # association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
  54154. # <tt>:belongs_to</tt>) as the first parameter.
  54155. #
  54156. # Example:
  54157. #
  54158. # Account.reflect_on_all_associations # returns an array of all associations
  54159. # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
  54160. #
  54161. def reflect_on_all_associations(macro = nil)
  54162. association_reflections = reflections.values.grep(AssociationReflection)
  54163. macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
  54164. end
  54165. # Returns the AssociationReflection object for the +association+ (use the symbol).
  54166. #
  54167. # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
  54168. # Invoice.reflect_on_association(:line_items).macro # returns :has_many
  54169. #
  54170. def reflect_on_association(association)
  54171. reflection = reflections[association]
  54172. reflection if reflection.is_a?(AssociationReflection)
  54173. end
  54174. # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
  54175. def reflect_on_all_autosave_associations
  54176. reflections.values.select { |reflection| reflection.options[:autosave] }
  54177. end
  54178. end
  54179. # Abstract base class for AggregateReflection and AssociationReflection. Objects of
  54180. # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
  54181. class MacroReflection
  54182. # Returns the name of the macro.
  54183. #
  54184. # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
  54185. # <tt>has_many :clients</tt> returns <tt>:clients</tt>
  54186. attr_reader :name
  54187. # Returns the macro type.
  54188. #
  54189. # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
  54190. # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
  54191. attr_reader :macro
  54192. attr_reader :scope
  54193. # Returns the hash of options used for the macro.
  54194. #
  54195. # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
  54196. # <tt>has_many :clients</tt> returns +{}+
  54197. attr_reader :options
  54198. attr_reader :active_record
  54199. attr_reader :plural_name # :nodoc:
  54200. def initialize(macro, name, scope, options, active_record)
  54201. @macro = macro
  54202. @name = name
  54203. @scope = scope
  54204. @options = options
  54205. @active_record = active_record
  54206. @plural_name = active_record.pluralize_table_names ?
  54207. name.to_s.pluralize : name.to_s
  54208. end
  54209. # Returns the class for the macro.
  54210. #
  54211. # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
  54212. # <tt>has_many :clients</tt> returns the Client class
  54213. def klass
  54214. @klass ||= class_name.constantize
  54215. end
  54216. # Returns the class name for the macro.
  54217. #
  54218. # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
  54219. # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
  54220. def class_name
  54221. @class_name ||= (options[:class_name] || derive_class_name).to_s
  54222. end
  54223. # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
  54224. # and +other_aggregation+ has an options hash assigned to it.
  54225. def ==(other_aggregation)
  54226. super ||
  54227. other_aggregation.kind_of?(self.class) &&
  54228. name == other_aggregation.name &&
  54229. other_aggregation.options &&
  54230. active_record == other_aggregation.active_record
  54231. end
  54232. private
  54233. def derive_class_name
  54234. name.to_s.camelize
  54235. end
  54236. end
  54237. # Holds all the meta-data about an aggregation as it was specified in the
  54238. # Active Record class.
  54239. class AggregateReflection < MacroReflection #:nodoc:
  54240. def mapping
  54241. mapping = options[:mapping] || [name, name]
  54242. mapping.first.is_a?(Array) ? mapping : [mapping]
  54243. end
  54244. end
  54245. # Holds all the meta-data about an association as it was specified in the
  54246. # Active Record class.
  54247. class AssociationReflection < MacroReflection #:nodoc:
  54248. # Returns the target association's class.
  54249. #
  54250. # class Author < ActiveRecord::Base
  54251. # has_many :books
  54252. # end
  54253. #
  54254. # Author.reflect_on_association(:books).klass
  54255. # # => Book
  54256. #
  54257. # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
  54258. # a new association object. Use +build_association+ or +create_association+
  54259. # instead. This allows plugins to hook into association object creation.
  54260. def klass
  54261. @klass ||= active_record.send(:compute_type, class_name)
  54262. end
  54263. def initialize(*args)
  54264. super
  54265. @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
  54266. end
  54267. # Returns a new, unsaved instance of the associated class. +attributes+ will
  54268. # be passed to the class's constructor.
  54269. def build_association(attributes, &block)
  54270. klass.new(attributes, &block)
  54271. end
  54272. def table_name
  54273. @table_name ||= klass.table_name
  54274. end
  54275. def quoted_table_name
  54276. @quoted_table_name ||= klass.quoted_table_name
  54277. end
  54278. def join_table
  54279. @join_table ||= options[:join_table] || derive_join_table
  54280. end
  54281. def foreign_key
  54282. @foreign_key ||= options[:foreign_key] || derive_foreign_key
  54283. end
  54284. def foreign_type
  54285. @foreign_type ||= options[:foreign_type] || "#{name}_type"
  54286. end
  54287. def type
  54288. @type ||= options[:as] && "#{options[:as]}_type"
  54289. end
  54290. def primary_key_column
  54291. @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
  54292. end
  54293. def association_foreign_key
  54294. @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
  54295. end
  54296. # klass option is necessary to support loading polymorphic associations
  54297. def association_primary_key(klass = nil)
  54298. options[:primary_key] || primary_key(klass || self.klass)
  54299. end
  54300. def active_record_primary_key
  54301. @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
  54302. end
  54303. def counter_cache_column
  54304. if options[:counter_cache] == true
  54305. "#{active_record.name.demodulize.underscore.pluralize}_count"
  54306. elsif options[:counter_cache]
  54307. options[:counter_cache].to_s
  54308. end
  54309. end
  54310. def columns(tbl_name)
  54311. @columns ||= klass.connection.columns(tbl_name)
  54312. end
  54313. def reset_column_information
  54314. @columns = nil
  54315. end
  54316. def check_validity!
  54317. check_validity_of_inverse!
  54318. if has_and_belongs_to_many? && association_foreign_key == foreign_key
  54319. raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(self)
  54320. end
  54321. end
  54322. def check_validity_of_inverse!
  54323. unless options[:polymorphic]
  54324. if has_inverse? && inverse_of.nil?
  54325. raise InverseOfAssociationNotFoundError.new(self)
  54326. end
  54327. end
  54328. end
  54329. def through_reflection
  54330. nil
  54331. end
  54332. def source_reflection
  54333. nil
  54334. end
  54335. # A chain of reflections from this one back to the owner. For more see the explanation in
  54336. # ThroughReflection.
  54337. def chain
  54338. [self]
  54339. end
  54340. def nested?
  54341. false
  54342. end
  54343. # An array of arrays of scopes. Each item in the outside array corresponds to a reflection
  54344. # in the #chain.
  54345. def scope_chain
  54346. scope ? [[scope]] : [[]]
  54347. end
  54348. alias :source_macro :macro
  54349. def has_inverse?
  54350. @options[:inverse_of]
  54351. end
  54352. def inverse_of
  54353. if has_inverse?
  54354. @inverse_of ||= klass.reflect_on_association(options[:inverse_of])
  54355. end
  54356. end
  54357. def polymorphic_inverse_of(associated_class)
  54358. if has_inverse?
  54359. if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
  54360. inverse_relationship
  54361. else
  54362. raise InverseOfAssociationNotFoundError.new(self, associated_class)
  54363. end
  54364. end
  54365. end
  54366. # Returns whether or not this association reflection is for a collection
  54367. # association. Returns +true+ if the +macro+ is either +has_many+ or
  54368. # +has_and_belongs_to_many+, +false+ otherwise.
  54369. def collection?
  54370. @collection
  54371. end
  54372. # Returns whether or not the association should be validated as part of
  54373. # the parent's validation.
  54374. #
  54375. # Unless you explicitly disable validation with
  54376. # <tt>validate: false</tt>, validation will take place when:
  54377. #
  54378. # * you explicitly enable validation; <tt>validate: true</tt>
  54379. # * you use autosave; <tt>autosave: true</tt>
  54380. # * the association is a +has_many+ association
  54381. def validate?
  54382. !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
  54383. end
  54384. # Returns +true+ if +self+ is a +belongs_to+ reflection.
  54385. def belongs_to?
  54386. macro == :belongs_to
  54387. end
  54388. def has_and_belongs_to_many?
  54389. macro == :has_and_belongs_to_many
  54390. end
  54391. def association_class
  54392. case macro
  54393. when :belongs_to
  54394. if options[:polymorphic]
  54395. Associations::BelongsToPolymorphicAssociation
  54396. else
  54397. Associations::BelongsToAssociation
  54398. end
  54399. when :has_and_belongs_to_many
  54400. Associations::HasAndBelongsToManyAssociation
  54401. when :has_many
  54402. if options[:through]
  54403. Associations::HasManyThroughAssociation
  54404. else
  54405. Associations::HasManyAssociation
  54406. end
  54407. when :has_one
  54408. if options[:through]
  54409. Associations::HasOneThroughAssociation
  54410. else
  54411. Associations::HasOneAssociation
  54412. end
  54413. end
  54414. end
  54415. def polymorphic?
  54416. options.key? :polymorphic
  54417. end
  54418. private
  54419. def derive_class_name
  54420. class_name = name.to_s.camelize
  54421. class_name = class_name.singularize if collection?
  54422. class_name
  54423. end
  54424. def derive_foreign_key
  54425. if belongs_to?
  54426. "#{name}_id"
  54427. elsif options[:as]
  54428. "#{options[:as]}_id"
  54429. else
  54430. active_record.name.foreign_key
  54431. end
  54432. end
  54433. def derive_join_table
  54434. [active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
  54435. end
  54436. def primary_key(klass)
  54437. klass.primary_key || raise(UnknownPrimaryKey.new(klass))
  54438. end
  54439. end
  54440. # Holds all the meta-data about a :through association as it was specified
  54441. # in the Active Record class.
  54442. class ThroughReflection < AssociationReflection #:nodoc:
  54443. delegate :foreign_key, :foreign_type, :association_foreign_key,
  54444. :active_record_primary_key, :type, :to => :source_reflection
  54445. # Gets the source of the through reflection. It checks both a singularized
  54446. # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
  54447. #
  54448. # class Post < ActiveRecord::Base
  54449. # has_many :taggings
  54450. # has_many :tags, through: :taggings
  54451. # end
  54452. #
  54453. def source_reflection
  54454. @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
  54455. end
  54456. # Returns the AssociationReflection object specified in the <tt>:through</tt> option
  54457. # of a HasManyThrough or HasOneThrough association.
  54458. #
  54459. # class Post < ActiveRecord::Base
  54460. # has_many :taggings
  54461. # has_many :tags, through: :taggings
  54462. # end
  54463. #
  54464. # tags_reflection = Post.reflect_on_association(:tags)
  54465. # taggings_reflection = tags_reflection.through_reflection
  54466. #
  54467. def through_reflection
  54468. @through_reflection ||= active_record.reflect_on_association(options[:through])
  54469. end
  54470. # Returns an array of reflections which are involved in this association. Each item in the
  54471. # array corresponds to a table which will be part of the query for this association.
  54472. #
  54473. # The chain is built by recursively calling #chain on the source reflection and the through
  54474. # reflection. The base case for the recursion is a normal association, which just returns
  54475. # [self] as its #chain.
  54476. def chain
  54477. @chain ||= begin
  54478. chain = source_reflection.chain + through_reflection.chain
  54479. chain[0] = self # Use self so we don't lose the information from :source_type
  54480. chain
  54481. end
  54482. end
  54483. # Consider the following example:
  54484. #
  54485. # class Person
  54486. # has_many :articles
  54487. # has_many :comment_tags, through: :articles
  54488. # end
  54489. #
  54490. # class Article
  54491. # has_many :comments
  54492. # has_many :comment_tags, through: :comments, source: :tags
  54493. # end
  54494. #
  54495. # class Comment
  54496. # has_many :tags
  54497. # end
  54498. #
  54499. # There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
  54500. # but only Comment.tags will be represented in the #chain. So this method creates an array
  54501. # of scopes corresponding to the chain.
  54502. def scope_chain
  54503. @scope_chain ||= begin
  54504. scope_chain = source_reflection.scope_chain.map(&:dup)
  54505. # Add to it the scope from this reflection (if any)
  54506. scope_chain.first << scope if scope
  54507. through_scope_chain = through_reflection.scope_chain
  54508. if options[:source_type]
  54509. through_scope_chain.first <<
  54510. through_reflection.klass.where(foreign_type => options[:source_type])
  54511. end
  54512. # Recursively fill out the rest of the array from the through reflection
  54513. scope_chain + through_scope_chain
  54514. end
  54515. end
  54516. # The macro used by the source association
  54517. def source_macro
  54518. source_reflection.source_macro
  54519. end
  54520. # A through association is nested if there would be more than one join table
  54521. def nested?
  54522. chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
  54523. end
  54524. # We want to use the klass from this reflection, rather than just delegate straight to
  54525. # the source_reflection, because the source_reflection may be polymorphic. We still
  54526. # need to respect the source_reflection's :primary_key option, though.
  54527. def association_primary_key(klass = nil)
  54528. # Get the "actual" source reflection if the immediate source reflection has a
  54529. # source reflection itself
  54530. source_reflection = self.source_reflection
  54531. while source_reflection.source_reflection
  54532. source_reflection = source_reflection.source_reflection
  54533. end
  54534. source_reflection.options[:primary_key] || primary_key(klass || self.klass)
  54535. end
  54536. # Gets an array of possible <tt>:through</tt> source reflection names:
  54537. #
  54538. # [:singularized, :pluralized]
  54539. #
  54540. def source_reflection_names
  54541. @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
  54542. end
  54543. def source_options
  54544. source_reflection.options
  54545. end
  54546. def through_options
  54547. through_reflection.options
  54548. end
  54549. def check_validity!
  54550. if through_reflection.nil?
  54551. raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
  54552. end
  54553. if through_reflection.options[:polymorphic]
  54554. raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
  54555. end
  54556. if source_reflection.nil?
  54557. raise HasManyThroughSourceAssociationNotFoundError.new(self)
  54558. end
  54559. if options[:source_type] && source_reflection.options[:polymorphic].nil?
  54560. raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
  54561. end
  54562. if source_reflection.options[:polymorphic] && options[:source_type].nil?
  54563. raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
  54564. end
  54565. if macro == :has_one && through_reflection.collection?
  54566. raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
  54567. end
  54568. check_validity_of_inverse!
  54569. end
  54570. private
  54571. def derive_class_name
  54572. # get the class_name of the belongs_to association of the through reflection
  54573. options[:source_type] || source_reflection.class_name
  54574. end
  54575. end
  54576. end
  54577. end
  54578. module ActiveRecord
  54579. module Batches
  54580. # Looping through a collection of records from the database
  54581. # (using the +all+ method, for example) is very inefficient
  54582. # since it will try to instantiate all the objects at once.
  54583. #
  54584. # In that case, batch processing methods allow you to work
  54585. # with the records in batches, thereby greatly reducing memory consumption.
  54586. #
  54587. # The #find_each method uses #find_in_batches with a batch size of 1000 (or as
  54588. # specified by the +:batch_size+ option).
  54589. #
  54590. # Person.all.find_each do |person|
  54591. # person.do_awesome_stuff
  54592. # end
  54593. #
  54594. # Person.where("age > 21").find_each do |person|
  54595. # person.party_all_night!
  54596. # end
  54597. #
  54598. # You can also pass the +:start+ option to specify
  54599. # an offset to control the starting point.
  54600. def find_each(options = {})
  54601. find_in_batches(options) do |records|
  54602. records.each { |record| yield record }
  54603. end
  54604. end
  54605. # Yields each batch of records that was found by the find +options+ as
  54606. # an array. The size of each batch is set by the +:batch_size+
  54607. # option; the default is 1000.
  54608. #
  54609. # You can control the starting point for the batch processing by
  54610. # supplying the +:start+ option. This is especially useful if you
  54611. # want multiple workers dealing with the same processing queue. You can
  54612. # make worker 1 handle all the records between id 0 and 10,000 and
  54613. # worker 2 handle from 10,000 and beyond (by setting the +:start+
  54614. # option on that worker).
  54615. #
  54616. # It's not possible to set the order. That is automatically set to
  54617. # ascending on the primary key ("id ASC") to make the batch ordering
  54618. # work. This also means that this method only works with integer-based
  54619. # primary keys. You can't set the limit either, that's used to control
  54620. # the batch sizes.
  54621. #
  54622. # Person.where("age > 21").find_in_batches do |group|
  54623. # sleep(50) # Make sure it doesn't get too crowded in there!
  54624. # group.each { |person| person.party_all_night! }
  54625. # end
  54626. #
  54627. # # Let's process the next 2000 records
  54628. # Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group|
  54629. # group.each { |person| person.party_all_night! }
  54630. # end
  54631. def find_in_batches(options = {})
  54632. options.assert_valid_keys(:start, :batch_size)
  54633. relation = self
  54634. unless arel.orders.blank? && arel.taken.blank?
  54635. ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
  54636. end
  54637. start = options.delete(:start)
  54638. batch_size = options.delete(:batch_size) || 1000
  54639. relation = relation.reorder(batch_order).limit(batch_size)
  54640. records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
  54641. while records.any?
  54642. records_size = records.size
  54643. primary_key_offset = records.last.id
  54644. yield records
  54645. break if records_size < batch_size
  54646. if primary_key_offset
  54647. records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
  54648. else
  54649. raise "Primary key not included in the custom select clause"
  54650. end
  54651. end
  54652. end
  54653. private
  54654. def batch_order
  54655. "#{quoted_table_name}.#{quoted_primary_key} ASC"
  54656. end
  54657. end
  54658. end
  54659. module ActiveRecord
  54660. module Calculations
  54661. # Count the records.
  54662. #
  54663. # Person.count
  54664. # # => the total count of all people
  54665. #
  54666. # Person.count(:age)
  54667. # # => returns the total count of all people whose age is present in database
  54668. #
  54669. # Person.count(:all)
  54670. # # => performs a COUNT(*) (:all is an alias for '*')
  54671. #
  54672. # Person.count(:age, distinct: true)
  54673. # # => counts the number of different age values
  54674. #
  54675. # If +count+ is used with +group+, it returns a Hash whose keys represent the aggregated column,
  54676. # and the values are the respective amounts:
  54677. #
  54678. # Person.group(:city).count
  54679. # # => { 'Rome' => 5, 'Paris' => 3 }
  54680. def count(column_name = nil, options = {})
  54681. column_name, options = nil, column_name if column_name.is_a?(Hash)
  54682. calculate(:count, column_name, options)
  54683. end
  54684. # Calculates the average value on a given column. Returns +nil+ if there's
  54685. # no row. See +calculate+ for examples with options.
  54686. #
  54687. # Person.average('age') # => 35.8
  54688. def average(column_name, options = {})
  54689. calculate(:average, column_name, options)
  54690. end
  54691. # Calculates the minimum value on a given column. The value is returned
  54692. # with the same data type of the column, or +nil+ if there's no row. See
  54693. # +calculate+ for examples with options.
  54694. #
  54695. # Person.minimum('age') # => 7
  54696. def minimum(column_name, options = {})
  54697. calculate(:minimum, column_name, options)
  54698. end
  54699. # Calculates the maximum value on a given column. The value is returned
  54700. # with the same data type of the column, or +nil+ if there's no row. See
  54701. # +calculate+ for examples with options.
  54702. #
  54703. # Person.maximum('age') # => 93
  54704. def maximum(column_name, options = {})
  54705. calculate(:maximum, column_name, options)
  54706. end
  54707. # Calculates the sum of values on a given column. The value is returned
  54708. # with the same data type of the column, 0 if there's no row. See
  54709. # +calculate+ for examples with options.
  54710. #
  54711. # Person.sum('age') # => 4562
  54712. def sum(*args)
  54713. if block_given?
  54714. ActiveSupport::Deprecation.warn(
  54715. "Calling #sum with a block is deprecated and will be removed in Rails 4.1. " \
  54716. "If you want to perform sum calculation over the array of elements, use `to_a.sum(&block)`."
  54717. )
  54718. self.to_a.sum(*args) {|*block_args| yield(*block_args)}
  54719. else
  54720. calculate(:sum, *args)
  54721. end
  54722. end
  54723. # This calculates aggregate values in the given column. Methods for count, sum, average,
  54724. # minimum, and maximum have been added as shortcuts.
  54725. #
  54726. # There are two basic forms of output:
  54727. #
  54728. # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
  54729. # for AVG, and the given column's type for everything else.
  54730. #
  54731. # * Grouped values: This returns an ordered hash of the values and groups them. It
  54732. # takes either a column name, or the name of a belongs_to association.
  54733. #
  54734. # values = Person.group('last_name').maximum(:age)
  54735. # puts values["Drake"]
  54736. # # => 43
  54737. #
  54738. # drake = Family.find_by_last_name('Drake')
  54739. # values = Person.group(:family).maximum(:age) # Person belongs_to :family
  54740. # puts values[drake]
  54741. # # => 43
  54742. #
  54743. # values.each do |family, max_age|
  54744. # ...
  54745. # end
  54746. #
  54747. # Person.calculate(:count, :all) # The same as Person.count
  54748. # Person.average(:age) # SELECT AVG(age) FROM people...
  54749. #
  54750. # # Selects the minimum age for any family without any minors
  54751. # Person.group(:last_name).having("min(age) > 17").minimum(:age)
  54752. #
  54753. # Person.sum("2 * age")
  54754. def calculate(operation, column_name, options = {})
  54755. relation = with_default_scope
  54756. if relation.equal?(self)
  54757. if has_include?(column_name)
  54758. construct_relation_for_association_calculations.calculate(operation, column_name, options)
  54759. else
  54760. perform_calculation(operation, column_name, options)
  54761. end
  54762. else
  54763. relation.calculate(operation, column_name, options)
  54764. end
  54765. rescue ThrowResult
  54766. 0
  54767. end
  54768. # Use <tt>pluck</tt> as a shortcut to select one or more attributes without
  54769. # loading a bunch of records just to grab the attributes you want.
  54770. #
  54771. # Person.pluck(:name)
  54772. #
  54773. # instead of
  54774. #
  54775. # Person.all.map(&:name)
  54776. #
  54777. # Pluck returns an <tt>Array</tt> of attribute values type-casted to match
  54778. # the plucked column names, if they can be deduced. Plucking an SQL fragment
  54779. # returns String values by default.
  54780. #
  54781. # Person.pluck(:id)
  54782. # # SELECT people.id FROM people
  54783. # # => [1, 2, 3]
  54784. #
  54785. # Person.pluck(:id, :name)
  54786. # # SELECT people.id, people.name FROM people
  54787. # # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
  54788. #
  54789. # Person.uniq.pluck(:role)
  54790. # # SELECT DISTINCT role FROM people
  54791. # # => ['admin', 'member', 'guest']
  54792. #
  54793. # Person.where(age: 21).limit(5).pluck(:id)
  54794. # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
  54795. # # => [2, 3]
  54796. #
  54797. # Person.pluck('DATEDIFF(updated_at, created_at)')
  54798. # # SELECT DATEDIFF(updated_at, created_at) FROM people
  54799. # # => ['0', '27761', '173']
  54800. #
  54801. def pluck(*column_names)
  54802. column_names.map! do |column_name|
  54803. if column_name.is_a?(Symbol) && self.column_names.include?(column_name.to_s)
  54804. "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
  54805. else
  54806. column_name
  54807. end
  54808. end
  54809. if has_include?(column_names.first)
  54810. construct_relation_for_association_calculations.pluck(*column_names)
  54811. else
  54812. relation = spawn
  54813. relation.select_values = column_names
  54814. result = klass.connection.select_all(relation.arel, nil, bind_values)
  54815. columns = result.columns.map do |key|
  54816. klass.column_types.fetch(key) {
  54817. result.column_types.fetch(key) {
  54818. Class.new { def type_cast(v); v; end }.new
  54819. }
  54820. }
  54821. end
  54822. result = result.map do |attributes|
  54823. values = klass.initialize_attributes(attributes).values
  54824. columns.zip(values).map do |column, value|
  54825. column.type_cast(value)
  54826. end
  54827. end
  54828. columns.one? ? result.map!(&:first) : result
  54829. end
  54830. end
  54831. # Pluck all the ID's for the relation using the table's primary key
  54832. #
  54833. # Person.ids # SELECT people.id FROM people
  54834. # Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
  54835. def ids
  54836. pluck primary_key
  54837. end
  54838. private
  54839. def has_include?(column_name)
  54840. eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?))
  54841. end
  54842. def perform_calculation(operation, column_name, options = {})
  54843. operation = operation.to_s.downcase
  54844. # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
  54845. distinct = options[:distinct] || self.uniq_value
  54846. if operation == "count"
  54847. column_name ||= (select_for_count || :all)
  54848. unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
  54849. distinct = true
  54850. end
  54851. column_name = primary_key if column_name == :all && distinct
  54852. distinct = nil if column_name =~ /\s*DISTINCT\s+/i
  54853. end
  54854. if group_values.any?
  54855. execute_grouped_calculation(operation, column_name, distinct)
  54856. else
  54857. execute_simple_calculation(operation, column_name, distinct)
  54858. end
  54859. end
  54860. def aggregate_column(column_name)
  54861. if @klass.column_names.include?(column_name.to_s)
  54862. Arel::Attribute.new(@klass.unscoped.table, column_name)
  54863. else
  54864. Arel.sql(column_name == :all ? "*" : column_name.to_s)
  54865. end
  54866. end
  54867. def operation_over_aggregate_column(column, operation, distinct)
  54868. operation == 'count' ? column.count(distinct) : column.send(operation)
  54869. end
  54870. def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
  54871. # Postgresql doesn't like ORDER BY when there are no GROUP BY
  54872. relation = reorder(nil)
  54873. column_alias = column_name
  54874. if operation == "count" && (relation.limit_value || relation.offset_value)
  54875. # Shortcut when limit is zero.
  54876. return 0 if relation.limit_value == 0
  54877. query_builder = build_count_subquery(relation, column_name, distinct)
  54878. else
  54879. column = aggregate_column(column_name)
  54880. select_value = operation_over_aggregate_column(column, operation, distinct)
  54881. column_alias = select_value.alias
  54882. relation.select_values = [select_value]
  54883. query_builder = relation.arel
  54884. end
  54885. result = @klass.connection.select_all(query_builder, nil, relation.bind_values)
  54886. row = result.first
  54887. value = row && row.values.first
  54888. column = result.column_types.fetch(column_alias) do
  54889. column_for(column_name)
  54890. end
  54891. type_cast_calculated_value(value, column, operation)
  54892. end
  54893. def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
  54894. group_attrs = group_values
  54895. if group_attrs.first.respond_to?(:to_sym)
  54896. association = @klass.reflect_on_association(group_attrs.first.to_sym)
  54897. associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
  54898. group_fields = Array(associated ? association.foreign_key : group_attrs)
  54899. else
  54900. group_fields = group_attrs
  54901. end
  54902. group_aliases = group_fields.map { |field|
  54903. column_alias_for(field)
  54904. }
  54905. group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
  54906. [aliaz, field]
  54907. }
  54908. group = group_fields
  54909. if operation == 'count' && column_name == :all
  54910. aggregate_alias = 'count_all'
  54911. else
  54912. aggregate_alias = column_alias_for([operation, column_name].join(' '))
  54913. end
  54914. select_values = [
  54915. operation_over_aggregate_column(
  54916. aggregate_column(column_name),
  54917. operation,
  54918. distinct).as(aggregate_alias)
  54919. ]
  54920. select_values += select_values unless having_values.empty?
  54921. select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
  54922. if field.respond_to?(:as)
  54923. field.as(aliaz)
  54924. else
  54925. "#{field} AS #{aliaz}"
  54926. end
  54927. }
  54928. relation = except(:group)
  54929. relation.group_values = group
  54930. relation.select_values = select_values
  54931. calculated_data = @klass.connection.select_all(relation, nil, bind_values)
  54932. if association
  54933. key_ids = calculated_data.collect { |row| row[group_aliases.first] }
  54934. key_records = association.klass.base_class.find(key_ids)
  54935. key_records = Hash[key_records.map { |r| [r.id, r] }]
  54936. end
  54937. Hash[calculated_data.map do |row|
  54938. key = group_columns.map { |aliaz, col_name|
  54939. column = calculated_data.column_types.fetch(aliaz) do
  54940. column_for(col_name)
  54941. end
  54942. type_cast_calculated_value(row[aliaz], column)
  54943. }
  54944. key = key.first if key.size == 1
  54945. key = key_records[key] if associated
  54946. [key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
  54947. end]
  54948. end
  54949. # Converts the given keys to the value that the database adapter returns as
  54950. # a usable column name:
  54951. #
  54952. # column_alias_for("users.id") # => "users_id"
  54953. # column_alias_for("sum(id)") # => "sum_id"
  54954. # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
  54955. # column_alias_for("count(*)") # => "count_all"
  54956. # column_alias_for("count", "id") # => "count_id"
  54957. def column_alias_for(keys)
  54958. if keys.respond_to? :name
  54959. keys = "#{keys.relation.name}.#{keys.name}"
  54960. end
  54961. table_name = keys.to_s.downcase
  54962. table_name.gsub!(/\*/, 'all')
  54963. table_name.gsub!(/\W+/, ' ')
  54964. table_name.strip!
  54965. table_name.gsub!(/ +/, '_')
  54966. @klass.connection.table_alias_for(table_name)
  54967. end
  54968. def column_for(field)
  54969. field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
  54970. @klass.columns_hash[field_name]
  54971. end
  54972. def type_cast_calculated_value(value, column, operation = nil)
  54973. case operation
  54974. when 'count' then value.to_i
  54975. when 'sum' then type_cast_using_column(value || 0, column)
  54976. when 'average' then value.respond_to?(:to_d) ? value.to_d : value
  54977. else type_cast_using_column(value, column)
  54978. end
  54979. end
  54980. def type_cast_using_column(value, column)
  54981. column ? column.type_cast(value) : value
  54982. end
  54983. def select_for_count
  54984. if select_values.present?
  54985. select = select_values.join(", ")
  54986. select if select !~ /[,*]/
  54987. end
  54988. end
  54989. def build_count_subquery(relation, column_name, distinct)
  54990. column_alias = Arel.sql('count_column')
  54991. subquery_alias = Arel.sql('subquery_for_count')
  54992. aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
  54993. relation.select_values = [aliased_column]
  54994. subquery = relation.arel.as(subquery_alias)
  54995. sm = Arel::SelectManager.new relation.engine
  54996. select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
  54997. sm.project(select_value).from(subquery)
  54998. end
  54999. end
  55000. end
  55001. require 'thread'
  55002. require 'thread_safe'
  55003. module ActiveRecord
  55004. module Delegation # :nodoc:
  55005. extend ActiveSupport::Concern
  55006. # This module creates compiled delegation methods dynamically at runtime, which makes
  55007. # subsequent calls to that method faster by avoiding method_missing. The delegations
  55008. # may vary depending on the klass of a relation, so we create a subclass of Relation
  55009. # for each different klass, and the delegations are compiled into that subclass only.
  55010. delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
  55011. delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
  55012. :connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
  55013. module ClassSpecificRelation
  55014. extend ActiveSupport::Concern
  55015. included do
  55016. @delegation_mutex = Mutex.new
  55017. end
  55018. module ClassMethods
  55019. def name
  55020. superclass.name
  55021. end
  55022. def delegate_to_scoped_klass(method)
  55023. @delegation_mutex.synchronize do
  55024. return if method_defined?(method)
  55025. if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
  55026. module_eval <<-RUBY, __FILE__, __LINE__ + 1
  55027. def #{method}(*args, &block)
  55028. scoping { @klass.#{method}(*args, &block) }
  55029. end
  55030. RUBY
  55031. else
  55032. module_eval <<-RUBY, __FILE__, __LINE__ + 1
  55033. def #{method}(*args, &block)
  55034. scoping { @klass.send(#{method.inspect}, *args, &block) }
  55035. end
  55036. RUBY
  55037. end
  55038. end
  55039. end
  55040. def delegate(method, opts = {})
  55041. @delegation_mutex.synchronize do
  55042. return if method_defined?(method)
  55043. super
  55044. end
  55045. end
  55046. end
  55047. protected
  55048. def method_missing(method, *args, &block)
  55049. if @klass.respond_to?(method)
  55050. self.class.delegate_to_scoped_klass(method)
  55051. scoping { @klass.send(method, *args, &block) }
  55052. elsif Array.method_defined?(method)
  55053. self.class.delegate method, :to => :to_a
  55054. to_a.send(method, *args, &block)
  55055. elsif arel.respond_to?(method)
  55056. self.class.delegate method, :to => :arel
  55057. arel.send(method, *args, &block)
  55058. else
  55059. super
  55060. end
  55061. end
  55062. end
  55063. module ClassMethods
  55064. @@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2)
  55065. def new(klass, *args)
  55066. relation = relation_class_for(klass).allocate
  55067. relation.__send__(:initialize, klass, *args)
  55068. relation
  55069. end
  55070. # This doesn't have to be thread-safe. relation_class_for guarantees that this will only be
  55071. # called exactly once for a given const name.
  55072. def const_missing(name)
  55073. const_set(name, Class.new(self) { include ClassSpecificRelation })
  55074. end
  55075. private
  55076. # Cache the constants in @@subclasses because looking them up via const_get
  55077. # make instantiation significantly slower.
  55078. def relation_class_for(klass)
  55079. if klass && (klass_name = klass.name)
  55080. my_cache = @@subclasses.compute_if_absent(self) { ThreadSafe::Cache.new }
  55081. # This hash is keyed by klass.name to avoid memory leaks in development mode
  55082. my_cache.compute_if_absent(klass_name) do
  55083. # Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name
  55084. const_get("#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}", false)
  55085. end
  55086. else
  55087. ActiveRecord::Relation
  55088. end
  55089. end
  55090. end
  55091. def respond_to?(method, include_private = false)
  55092. super || Array.method_defined?(method) ||
  55093. @klass.respond_to?(method, include_private) ||
  55094. arel.respond_to?(method, include_private)
  55095. end
  55096. protected
  55097. def method_missing(method, *args, &block)
  55098. if @klass.respond_to?(method)
  55099. scoping { @klass.send(method, *args, &block) }
  55100. elsif Array.method_defined?(method)
  55101. to_a.send(method, *args, &block)
  55102. elsif arel.respond_to?(method)
  55103. arel.send(method, *args, &block)
  55104. else
  55105. super
  55106. end
  55107. end
  55108. end
  55109. end
  55110. module ActiveRecord
  55111. module FinderMethods
  55112. # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
  55113. # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
  55114. # is an integer, find by id coerces its arguments using +to_i+.
  55115. #
  55116. # Person.find(1) # returns the object for ID = 1
  55117. # Person.find("1") # returns the object for ID = 1
  55118. # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
  55119. # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
  55120. # Person.find([1]) # returns an array for the object with ID = 1
  55121. # Person.where("administrator = 1").order("created_on DESC").find(1)
  55122. #
  55123. # Note that returned records may not be in the same order as the ids you
  55124. # provide since database rows are unordered. Give an explicit <tt>order</tt>
  55125. # to ensure the results are sorted.
  55126. #
  55127. # ==== Find with lock
  55128. #
  55129. # Example for find with a lock: Imagine two concurrent transactions:
  55130. # each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
  55131. # in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
  55132. # transaction has to wait until the first is finished; we get the
  55133. # expected <tt>person.visits == 4</tt>.
  55134. #
  55135. # Person.transaction do
  55136. # person = Person.lock(true).find(1)
  55137. # person.visits += 1
  55138. # person.save!
  55139. # end
  55140. def find(*args)
  55141. if block_given?
  55142. to_a.find { |*block_args| yield(*block_args) }
  55143. else
  55144. find_with_ids(*args)
  55145. end
  55146. end
  55147. # Finds the first record matching the specified conditions. There
  55148. # is no implied ording so if order matters, you should specify it
  55149. # yourself.
  55150. #
  55151. # If no record is found, returns <tt>nil</tt>.
  55152. #
  55153. # Post.find_by name: 'Spartacus', rating: 4
  55154. # Post.find_by "published_at < ?", 2.weeks.ago
  55155. def find_by(*args)
  55156. where(*args).take
  55157. end
  55158. # Like <tt>find_by</tt>, except that if no record is found, raises
  55159. # an <tt>ActiveRecord::RecordNotFound</tt> error.
  55160. def find_by!(*args)
  55161. where(*args).take!
  55162. end
  55163. # Gives a record (or N records if a parameter is supplied) without any implied
  55164. # order. The order will depend on the database implementation.
  55165. # If an order is supplied it will be respected.
  55166. #
  55167. # Person.take # returns an object fetched by SELECT * FROM people
  55168. # Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
  55169. # Person.where(["name LIKE '%?'", name]).take
  55170. def take(limit = nil)
  55171. limit ? limit(limit).to_a : find_take
  55172. end
  55173. # Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
  55174. # is found. Note that <tt>take!</tt> accepts no arguments.
  55175. def take!
  55176. take or raise RecordNotFound
  55177. end
  55178. # Find the first record (or first N records if a parameter is supplied).
  55179. # If no order is defined it will order by primary key.
  55180. #
  55181. # Person.first # returns the first object fetched by SELECT * FROM people
  55182. # Person.where(["user_name = ?", user_name]).first
  55183. # Person.where(["user_name = :u", { u: user_name }]).first
  55184. # Person.order("created_on DESC").offset(5).first
  55185. # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
  55186. def first(limit = nil)
  55187. if limit
  55188. if order_values.empty? && primary_key
  55189. order(arel_table[primary_key].asc).limit(limit).to_a
  55190. else
  55191. limit(limit).to_a
  55192. end
  55193. else
  55194. find_first
  55195. end
  55196. end
  55197. # Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
  55198. # is found. Note that <tt>first!</tt> accepts no arguments.
  55199. def first!
  55200. first or raise RecordNotFound
  55201. end
  55202. # Find the last record (or last N records if a parameter is supplied).
  55203. # If no order is defined it will order by primary key.
  55204. #
  55205. # Person.last # returns the last object fetched by SELECT * FROM people
  55206. # Person.where(["user_name = ?", user_name]).last
  55207. # Person.order("created_on DESC").offset(5).last
  55208. # Person.last(3) # returns the last three objects fetched by SELECT * FROM people.
  55209. #
  55210. # Take note that in that last case, the results are sorted in ascending order:
  55211. #
  55212. # [#<Person id:2>, #<Person id:3>, #<Person id:4>]
  55213. #
  55214. # and not:
  55215. #
  55216. # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
  55217. def last(limit = nil)
  55218. if limit
  55219. if order_values.empty? && primary_key
  55220. order(arel_table[primary_key].desc).limit(limit).reverse
  55221. else
  55222. to_a.last(limit)
  55223. end
  55224. else
  55225. find_last
  55226. end
  55227. end
  55228. # Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
  55229. # is found. Note that <tt>last!</tt> accepts no arguments.
  55230. def last!
  55231. last or raise RecordNotFound
  55232. end
  55233. # Returns +true+ if a record exists in the table that matches the +id+ or
  55234. # conditions given, or +false+ otherwise. The argument can take six forms:
  55235. #
  55236. # * Integer - Finds the record with this primary key.
  55237. # * String - Finds the record with a primary key corresponding to this
  55238. # string (such as <tt>'5'</tt>).
  55239. # * Array - Finds the record that matches these +find+-style conditions
  55240. # (such as <tt>['color = ?', 'red']</tt>).
  55241. # * Hash - Finds the record that matches these +find+-style conditions
  55242. # (such as <tt>{color: 'red'}</tt>).
  55243. # * +false+ - Returns always +false+.
  55244. # * No args - Returns +false+ if the table is empty, +true+ otherwise.
  55245. #
  55246. # For more information about specifying conditions as a Hash or Array,
  55247. # see the Conditions section in the introduction to ActiveRecord::Base.
  55248. #
  55249. # Note: You can't pass in a condition as a string (like <tt>name =
  55250. # 'Jamie'</tt>), since it would be sanitized and then queried against
  55251. # the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
  55252. #
  55253. # Person.exists?(5)
  55254. # Person.exists?('5')
  55255. # Person.exists?(['name LIKE ?', "%#{query}%"])
  55256. # Person.exists?(name: 'David')
  55257. # Person.exists?(false)
  55258. # Person.exists?
  55259. def exists?(conditions = :none)
  55260. conditions = conditions.id if Base === conditions
  55261. return false if !conditions
  55262. join_dependency = construct_join_dependency_for_association_find
  55263. relation = construct_relation_for_association_find(join_dependency)
  55264. relation = relation.except(:select, :order).select("1 AS one").limit(1)
  55265. case conditions
  55266. when Array, Hash
  55267. relation = relation.where(conditions)
  55268. else
  55269. relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
  55270. end
  55271. connection.select_value(relation, "#{name} Exists", relation.bind_values)
  55272. rescue ThrowResult
  55273. false
  55274. end
  55275. protected
  55276. def find_with_associations
  55277. join_dependency = construct_join_dependency_for_association_find
  55278. relation = construct_relation_for_association_find(join_dependency)
  55279. rows = connection.select_all(relation, 'SQL', relation.bind_values.dup)
  55280. join_dependency.instantiate(rows)
  55281. rescue ThrowResult
  55282. []
  55283. end
  55284. def construct_join_dependency_for_association_find
  55285. including = (eager_load_values + includes_values).uniq
  55286. ActiveRecord::Associations::JoinDependency.new(@klass, including, [])
  55287. end
  55288. def construct_relation_for_association_calculations
  55289. including = (eager_load_values + includes_values).uniq
  55290. join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, arel.froms.first)
  55291. relation = except(:includes, :eager_load, :preload)
  55292. apply_join_dependency(relation, join_dependency)
  55293. end
  55294. def construct_relation_for_association_find(join_dependency)
  55295. relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns)
  55296. apply_join_dependency(relation, join_dependency)
  55297. end
  55298. def apply_join_dependency(relation, join_dependency)
  55299. join_dependency.join_associations.each do |association|
  55300. relation = association.join_relation(relation)
  55301. end
  55302. limitable_reflections = using_limitable_reflections?(join_dependency.reflections)
  55303. if !limitable_reflections && relation.limit_value
  55304. limited_id_condition = construct_limited_ids_condition(relation.except(:select))
  55305. relation = relation.where(limited_id_condition)
  55306. end
  55307. relation = relation.except(:limit, :offset) unless limitable_reflections
  55308. relation
  55309. end
  55310. def construct_limited_ids_condition(relation)
  55311. orders = relation.order_values.map { |val| val.presence }.compact
  55312. values = @klass.connection.distinct("#{quoted_table_name}.#{primary_key}", orders)
  55313. relation = relation.dup.select(values)
  55314. id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
  55315. ids_array = id_rows.map {|row| row[primary_key]}
  55316. ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
  55317. end
  55318. def find_with_ids(*ids)
  55319. expects_array = ids.first.kind_of?(Array)
  55320. return ids.first if expects_array && ids.first.empty?
  55321. ids = ids.flatten.compact.uniq
  55322. case ids.size
  55323. when 0
  55324. raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
  55325. when 1
  55326. result = find_one(ids.first)
  55327. expects_array ? [ result ] : result
  55328. else
  55329. find_some(ids)
  55330. end
  55331. end
  55332. def find_one(id)
  55333. id = id.id if ActiveRecord::Base === id
  55334. column = columns_hash[primary_key]
  55335. substitute = connection.substitute_at(column, bind_values.length)
  55336. relation = where(table[primary_key].eq(substitute))
  55337. relation.bind_values += [[column, id]]
  55338. record = relation.take
  55339. unless record
  55340. conditions = arel.where_sql
  55341. conditions = " [#{conditions}]" if conditions
  55342. raise RecordNotFound, "Couldn't find #{@klass.name} with #{primary_key}=#{id}#{conditions}"
  55343. end
  55344. record
  55345. end
  55346. def find_some(ids)
  55347. result = where(table[primary_key].in(ids)).to_a
  55348. expected_size =
  55349. if limit_value && ids.size > limit_value
  55350. limit_value
  55351. else
  55352. ids.size
  55353. end
  55354. # 11 ids with limit 3, offset 9 should give 2 results.
  55355. if offset_value && (ids.size - offset_value < expected_size)
  55356. expected_size = ids.size - offset_value
  55357. end
  55358. if result.size == expected_size
  55359. result
  55360. else
  55361. conditions = arel.where_sql
  55362. conditions = " [#{conditions}]" if conditions
  55363. error = "Couldn't find all #{@klass.name.pluralize} with IDs "
  55364. error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
  55365. raise RecordNotFound, error
  55366. end
  55367. end
  55368. def find_take
  55369. if loaded?
  55370. @records.first
  55371. else
  55372. @take ||= limit(1).to_a.first
  55373. end
  55374. end
  55375. def find_first
  55376. if loaded?
  55377. @records.first
  55378. else
  55379. @first ||=
  55380. if with_default_scope.order_values.empty? && primary_key
  55381. order(arel_table[primary_key].asc).limit(1).to_a.first
  55382. else
  55383. limit(1).to_a.first
  55384. end
  55385. end
  55386. end
  55387. def find_last
  55388. if loaded?
  55389. @records.last
  55390. else
  55391. @last ||=
  55392. if offset_value || limit_value
  55393. to_a.last
  55394. else
  55395. reverse_order.limit(1).to_a.first
  55396. end
  55397. end
  55398. end
  55399. def using_limitable_reflections?(reflections)
  55400. reflections.none? { |r| r.collection? }
  55401. end
  55402. end
  55403. end
  55404. require 'active_support/core_ext/hash/keys'
  55405. require "set"
  55406. module ActiveRecord
  55407. class Relation
  55408. class HashMerger # :nodoc:
  55409. attr_reader :relation, :hash
  55410. def initialize(relation, hash)
  55411. hash.assert_valid_keys(*Relation::VALUE_METHODS)
  55412. @relation = relation
  55413. @hash = hash
  55414. end
  55415. def merge
  55416. Merger.new(relation, other).merge
  55417. end
  55418. # Applying values to a relation has some side effects. E.g.
  55419. # interpolation might take place for where values. So we should
  55420. # build a relation to merge in rather than directly merging
  55421. # the values.
  55422. def other
  55423. other = Relation.new(relation.klass, relation.table)
  55424. hash.each { |k, v|
  55425. if k == :joins
  55426. if Hash === v
  55427. other.joins!(v)
  55428. else
  55429. other.joins!(*v)
  55430. end
  55431. else
  55432. other.send("#{k}!", v)
  55433. end
  55434. }
  55435. other
  55436. end
  55437. end
  55438. class Merger # :nodoc:
  55439. attr_reader :relation, :values
  55440. def initialize(relation, other)
  55441. if other.default_scoped? && other.klass != relation.klass
  55442. other = other.with_default_scope
  55443. end
  55444. @relation = relation
  55445. @values = other.values
  55446. end
  55447. NORMAL_VALUES = Relation::SINGLE_VALUE_METHODS +
  55448. Relation::MULTI_VALUE_METHODS -
  55449. [:where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
  55450. def normal_values
  55451. NORMAL_VALUES
  55452. end
  55453. def merge
  55454. normal_values.each do |name|
  55455. value = values[name]
  55456. relation.send("#{name}!", *value) unless value.blank?
  55457. end
  55458. merge_multi_values
  55459. merge_single_values
  55460. relation
  55461. end
  55462. private
  55463. def merge_multi_values
  55464. relation.where_values = merged_wheres
  55465. relation.bind_values = merged_binds
  55466. if values[:reordering]
  55467. # override any order specified in the original relation
  55468. relation.reorder! values[:order]
  55469. elsif values[:order]
  55470. # merge in order_values from r
  55471. relation.order! values[:order]
  55472. end
  55473. relation.extend(*values[:extending]) unless values[:extending].blank?
  55474. end
  55475. def merge_single_values
  55476. relation.from_value = values[:from] unless relation.from_value
  55477. relation.lock_value = values[:lock] unless relation.lock_value
  55478. relation.reverse_order_value = values[:reverse_order]
  55479. unless values[:create_with].blank?
  55480. relation.create_with_value = (relation.create_with_value || {}).merge(values[:create_with])
  55481. end
  55482. end
  55483. def merged_binds
  55484. if values[:bind]
  55485. (relation.bind_values + values[:bind]).uniq(&:first)
  55486. else
  55487. relation.bind_values
  55488. end
  55489. end
  55490. def merged_wheres
  55491. values[:where] ||= []
  55492. if values[:where].empty? || relation.where_values.empty?
  55493. relation.where_values + values[:where]
  55494. else
  55495. # Remove equalities from the existing relation with a LHS which is
  55496. # present in the relation being merged in.
  55497. seen = Set.new
  55498. values[:where].each { |w|
  55499. if w.respond_to?(:operator) && w.operator == :==
  55500. seen << w.left
  55501. end
  55502. }
  55503. relation.where_values.reject { |w|
  55504. w.respond_to?(:operator) &&
  55505. w.operator == :== &&
  55506. seen.include?(w.left)
  55507. } + values[:where]
  55508. end
  55509. end
  55510. end
  55511. end
  55512. end
  55513. module ActiveRecord
  55514. class PredicateBuilder # :nodoc:
  55515. def self.build_from_hash(klass, attributes, default_table)
  55516. queries = []
  55517. attributes.each do |column, value|
  55518. table = default_table
  55519. if value.is_a?(Hash)
  55520. if value.empty?
  55521. queries << '1=0'
  55522. else
  55523. table = Arel::Table.new(column, default_table.engine)
  55524. association = klass.reflect_on_association(column.to_sym)
  55525. value.each do |k, v|
  55526. queries.concat expand(association && association.klass, table, k, v)
  55527. end
  55528. end
  55529. else
  55530. column = column.to_s
  55531. if column.include?('.')
  55532. table_name, column = column.split('.', 2)
  55533. table = Arel::Table.new(table_name, default_table.engine)
  55534. end
  55535. queries.concat expand(klass, table, column, value)
  55536. end
  55537. end
  55538. queries
  55539. end
  55540. def self.expand(klass, table, column, value)
  55541. queries = []
  55542. # Find the foreign key when using queries such as:
  55543. # Post.where(author: author)
  55544. #
  55545. # For polymorphic relationships, find the foreign key and type:
  55546. # PriceEstimate.where(estimate_of: treasure)
  55547. if klass && value.class < Base && reflection = klass.reflect_on_association(column.to_sym)
  55548. if reflection.polymorphic?
  55549. queries << build(table[reflection.foreign_type], value.class.base_class)
  55550. end
  55551. column = reflection.foreign_key
  55552. end
  55553. queries << build(table[column.to_sym], value)
  55554. queries
  55555. end
  55556. def self.references(attributes)
  55557. attributes.map do |key, value|
  55558. if value.is_a?(Hash)
  55559. key
  55560. else
  55561. key = key.to_s
  55562. key.split('.').first if key.include?('.')
  55563. end
  55564. end.compact
  55565. end
  55566. private
  55567. def self.build(attribute, value)
  55568. case value
  55569. when Array
  55570. values = value.to_a.map {|x| x.is_a?(Base) ? x.id : x}
  55571. ranges, values = values.partition {|v| v.is_a?(Range)}
  55572. values_predicate = if values.include?(nil)
  55573. values = values.compact
  55574. case values.length
  55575. when 0
  55576. attribute.eq(nil)
  55577. when 1
  55578. attribute.eq(values.first).or(attribute.eq(nil))
  55579. else
  55580. attribute.in(values).or(attribute.eq(nil))
  55581. end
  55582. else
  55583. attribute.in(values)
  55584. end
  55585. array_predicates = ranges.map { |range| attribute.in(range) }
  55586. array_predicates << values_predicate
  55587. array_predicates.inject { |composite, predicate| composite.or(predicate) }
  55588. when ActiveRecord::Relation
  55589. value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
  55590. attribute.in(value.arel.ast)
  55591. when Range
  55592. attribute.in(value)
  55593. when ActiveRecord::Base
  55594. attribute.eq(value.id)
  55595. when Class
  55596. # FIXME: I think we need to deprecate this behavior
  55597. attribute.eq(value.name)
  55598. when Integer, ActiveSupport::Duration
  55599. # Arel treats integers as literals, but they should be quoted when compared with strings
  55600. table = attribute.relation
  55601. column = table.engine.connection.schema_cache.columns_hash(table.name)[attribute.name.to_s]
  55602. attribute.eq(Arel::Nodes::SqlLiteral.new(table.engine.connection.quote(value, column)))
  55603. else
  55604. attribute.eq(value)
  55605. end
  55606. end
  55607. end
  55608. end
  55609. require 'active_support/core_ext/array/wrap'
  55610. module ActiveRecord
  55611. module QueryMethods
  55612. extend ActiveSupport::Concern
  55613. # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
  55614. # In this case, #where must be chained with either #not, #like, or #not_like to return a new relation.
  55615. class WhereChain
  55616. def initialize(scope)
  55617. @scope = scope
  55618. end
  55619. # Returns a new relation expressing WHERE + NOT condition
  55620. # according to the conditions in the arguments.
  55621. #
  55622. # #not accepts conditions in one of these formats: String, Array, Hash.
  55623. # See #where for more details on each format.
  55624. #
  55625. # User.where.not("name = 'Jon'")
  55626. # # SELECT * FROM users WHERE NOT (name = 'Jon')
  55627. #
  55628. # User.where.not(["name = ?", "Jon"])
  55629. # # SELECT * FROM users WHERE NOT (name = 'Jon')
  55630. #
  55631. # User.where.not(name: "Jon")
  55632. # # SELECT * FROM users WHERE name != 'Jon'
  55633. #
  55634. # User.where.not(name: nil)
  55635. # # SELECT * FROM users WHERE name IS NOT NULL
  55636. #
  55637. # User.where.not(name: %w(Ko1 Nobu))
  55638. # # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
  55639. def not(opts, *rest)
  55640. where_value = @scope.send(:build_where, opts, rest).map do |rel|
  55641. case rel
  55642. when Arel::Nodes::In
  55643. Arel::Nodes::NotIn.new(rel.left, rel.right)
  55644. when Arel::Nodes::Equality
  55645. Arel::Nodes::NotEqual.new(rel.left, rel.right)
  55646. when String
  55647. Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel))
  55648. else
  55649. Arel::Nodes::Not.new(rel)
  55650. end
  55651. end
  55652. @scope.where_values += where_value
  55653. @scope
  55654. end
  55655. end
  55656. Relation::MULTI_VALUE_METHODS.each do |name|
  55657. class_eval <<-CODE, __FILE__, __LINE__ + 1
  55658. def #{name}_values # def select_values
  55659. @values[:#{name}] || [] # @values[:select] || []
  55660. end # end
  55661. #
  55662. def #{name}_values=(values) # def select_values=(values)
  55663. raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
  55664. @values[:#{name}] = values # @values[:select] = values
  55665. end # end
  55666. CODE
  55667. end
  55668. (Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
  55669. class_eval <<-CODE, __FILE__, __LINE__ + 1
  55670. def #{name}_value # def readonly_value
  55671. @values[:#{name}] # @values[:readonly]
  55672. end # end
  55673. CODE
  55674. end
  55675. Relation::SINGLE_VALUE_METHODS.each do |name|
  55676. class_eval <<-CODE, __FILE__, __LINE__ + 1
  55677. def #{name}_value=(value) # def readonly_value=(value)
  55678. raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
  55679. @values[:#{name}] = value # @values[:readonly] = value
  55680. end # end
  55681. CODE
  55682. end
  55683. def create_with_value # :nodoc:
  55684. @values[:create_with] || {}
  55685. end
  55686. alias extensions extending_values
  55687. # Specify relationships to be included in the result set. For
  55688. # example:
  55689. #
  55690. # users = User.includes(:address)
  55691. # users.each do |user|
  55692. # user.address.city
  55693. # end
  55694. #
  55695. # allows you to access the +address+ attribute of the +User+ model without
  55696. # firing an additional query. This will often result in a
  55697. # performance improvement over a simple +join+.
  55698. #
  55699. # === conditions
  55700. #
  55701. # If you want to add conditions to your included models you'll have
  55702. # to explicitly reference them. For example:
  55703. #
  55704. # User.includes(:posts).where('posts.name = ?', 'example')
  55705. #
  55706. # Will throw an error, but this will work:
  55707. #
  55708. # User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
  55709. def includes(*args)
  55710. args.empty? ? self : spawn.includes!(*args)
  55711. end
  55712. def includes!(*args) # :nodoc:
  55713. args.reject! {|a| a.blank? }
  55714. self.includes_values = (includes_values + args).flatten.uniq
  55715. self
  55716. end
  55717. # Forces eager loading by performing a LEFT OUTER JOIN on +args+:
  55718. #
  55719. # User.eager_load(:posts)
  55720. # => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
  55721. # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
  55722. # "users"."id"
  55723. def eager_load(*args)
  55724. args.blank? ? self : spawn.eager_load!(*args)
  55725. end
  55726. def eager_load!(*args) # :nodoc:
  55727. self.eager_load_values += args
  55728. self
  55729. end
  55730. # Allows preloading of +args+, in the same way that +includes+ does:
  55731. #
  55732. # User.preload(:posts)
  55733. # => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
  55734. def preload(*args)
  55735. args.blank? ? self : spawn.preload!(*args)
  55736. end
  55737. def preload!(*args) # :nodoc:
  55738. self.preload_values += args
  55739. self
  55740. end
  55741. # Used to indicate that an association is referenced by an SQL string, and should
  55742. # therefore be JOINed in any query rather than loaded separately.
  55743. #
  55744. # User.includes(:posts).where("posts.name = 'foo'")
  55745. # # => Doesn't JOIN the posts table, resulting in an error.
  55746. #
  55747. # User.includes(:posts).where("posts.name = 'foo'").references(:posts)
  55748. # # => Query now knows the string references posts, so adds a JOIN
  55749. def references(*args)
  55750. args.blank? ? self : spawn.references!(*args)
  55751. end
  55752. def references!(*args) # :nodoc:
  55753. args.flatten!
  55754. self.references_values = (references_values + args.map!(&:to_s)).uniq
  55755. self
  55756. end
  55757. # Works in two unique ways.
  55758. #
  55759. # First: takes a block so it can be used just like Array#select.
  55760. #
  55761. # Model.all.select { |m| m.field == value }
  55762. #
  55763. # This will build an array of objects from the database for the scope,
  55764. # converting them into an array and iterating through them using Array#select.
  55765. #
  55766. # Second: Modifies the SELECT statement for the query so that only certain
  55767. # fields are retrieved:
  55768. #
  55769. # Model.select(:field)
  55770. # # => [#<Model field:value>]
  55771. #
  55772. # Although in the above example it looks as though this method returns an
  55773. # array, it actually returns a relation object and can have other query
  55774. # methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
  55775. #
  55776. # The argument to the method can also be an array of fields.
  55777. #
  55778. # Model.select(:field, :other_field, :and_one_more)
  55779. # # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
  55780. #
  55781. # You can also use one or more strings, which will be used unchanged as SELECT fields.
  55782. #
  55783. # Model.select('field AS field_one', 'other_field AS field_two')
  55784. # # => [#<Model field: "value", other_field: "value">]
  55785. #
  55786. # If an alias was specified, it will be accessible from the resulting objects:
  55787. #
  55788. # Model.select('field AS field_one').first.field_one
  55789. # # => "value"
  55790. #
  55791. # Accessing attributes of an object that do not have fields retrieved by a select
  55792. # will throw <tt>ActiveModel::MissingAttributeError</tt>:
  55793. #
  55794. # Model.select(:field).first.other_field
  55795. # # => ActiveModel::MissingAttributeError: missing attribute: other_field
  55796. def select(*fields)
  55797. if block_given?
  55798. to_a.select { |*block_args| yield(*block_args) }
  55799. else
  55800. raise ArgumentError, 'Call this with at least one field' if fields.empty?
  55801. spawn.select!(*fields)
  55802. end
  55803. end
  55804. def select!(*fields) # :nodoc:
  55805. self.select_values += fields.flatten
  55806. self
  55807. end
  55808. # Allows to specify a group attribute:
  55809. #
  55810. # User.group(:name)
  55811. # => SELECT "users".* FROM "users" GROUP BY name
  55812. #
  55813. # Returns an array with distinct records based on the +group+ attribute:
  55814. #
  55815. # User.select([:id, :name])
  55816. # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
  55817. #
  55818. # User.group(:name)
  55819. # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
  55820. #
  55821. # User.group('name AS grouped_name, age')
  55822. # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
  55823. def group(*args)
  55824. args.blank? ? self : spawn.group!(*args)
  55825. end
  55826. def group!(*args) # :nodoc:
  55827. args.flatten!
  55828. self.group_values += args
  55829. self
  55830. end
  55831. # Allows to specify an order attribute:
  55832. #
  55833. # User.order('name')
  55834. # => SELECT "users".* FROM "users" ORDER BY name
  55835. #
  55836. # User.order('name DESC')
  55837. # => SELECT "users".* FROM "users" ORDER BY name DESC
  55838. #
  55839. # User.order('name DESC, email')
  55840. # => SELECT "users".* FROM "users" ORDER BY name DESC, email
  55841. #
  55842. # User.order(:name)
  55843. # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
  55844. #
  55845. # User.order(email: :desc)
  55846. # => SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
  55847. #
  55848. # User.order(:name, email: :desc)
  55849. # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
  55850. def order(*args)
  55851. args.blank? ? self : spawn.order!(*args)
  55852. end
  55853. def order!(*args) # :nodoc:
  55854. args.flatten!
  55855. validate_order_args args
  55856. references = args.reject { |arg| Arel::Node === arg }
  55857. references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
  55858. references!(references) if references.any?
  55859. self.order_values = args + self.order_values
  55860. self
  55861. end
  55862. # Replaces any existing order defined on the relation with the specified order.
  55863. #
  55864. # User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
  55865. #
  55866. # Subsequent calls to order on the same relation will be appended. For example:
  55867. #
  55868. # User.order('email DESC').reorder('id ASC').order('name ASC')
  55869. #
  55870. # generates a query with 'ORDER BY name ASC, id ASC'.
  55871. def reorder(*args)
  55872. args.blank? ? self : spawn.reorder!(*args)
  55873. end
  55874. def reorder!(*args) # :nodoc:
  55875. args.flatten!
  55876. validate_order_args args
  55877. self.reordering_value = true
  55878. self.order_values = args
  55879. self
  55880. end
  55881. # Performs a joins on +args+:
  55882. #
  55883. # User.joins(:posts)
  55884. # => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
  55885. #
  55886. # You can use strings in order to customize your joins:
  55887. #
  55888. # User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
  55889. # => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
  55890. def joins(*args)
  55891. args.compact.blank? ? self : spawn.joins!(*args.flatten)
  55892. end
  55893. def joins!(*args) # :nodoc:
  55894. self.joins_values += args
  55895. self
  55896. end
  55897. def bind(value)
  55898. spawn.bind!(value)
  55899. end
  55900. def bind!(value) # :nodoc:
  55901. self.bind_values += [value]
  55902. self
  55903. end
  55904. # Returns a new relation, which is the result of filtering the current relation
  55905. # according to the conditions in the arguments.
  55906. #
  55907. # #where accepts conditions in one of several formats. In the examples below, the resulting
  55908. # SQL is given as an illustration; the actual query generated may be different depending
  55909. # on the database adapter.
  55910. #
  55911. # === string
  55912. #
  55913. # A single string, without additional arguments, is passed to the query
  55914. # constructor as a SQL fragment, and used in the where clause of the query.
  55915. #
  55916. # Client.where("orders_count = '2'")
  55917. # # SELECT * from clients where orders_count = '2';
  55918. #
  55919. # Note that building your own string from user input may expose your application
  55920. # to injection attacks if not done properly. As an alternative, it is recommended
  55921. # to use one of the following methods.
  55922. #
  55923. # === array
  55924. #
  55925. # If an array is passed, then the first element of the array is treated as a template, and
  55926. # the remaining elements are inserted into the template to generate the condition.
  55927. # Active Record takes care of building the query to avoid injection attacks, and will
  55928. # convert from the ruby type to the database type where needed. Elements are inserted
  55929. # into the string in the order in which they appear.
  55930. #
  55931. # User.where(["name = ? and email = ?", "Joe", "joe@example.com"])
  55932. # # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
  55933. #
  55934. # Alternatively, you can use named placeholders in the template, and pass a hash as the
  55935. # second element of the array. The names in the template are replaced with the corresponding
  55936. # values from the hash.
  55937. #
  55938. # User.where(["name = :name and email = :email", { name: "Joe", email: "joe@example.com" }])
  55939. # # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
  55940. #
  55941. # This can make for more readable code in complex queries.
  55942. #
  55943. # Lastly, you can use sprintf-style % escapes in the template. This works slightly differently
  55944. # than the previous methods; you are responsible for ensuring that the values in the template
  55945. # are properly quoted. The values are passed to the connector for quoting, but the caller
  55946. # is responsible for ensuring they are enclosed in quotes in the resulting SQL. After quoting,
  55947. # the values are inserted using the same escapes as the Ruby core method <tt>Kernel::sprintf</tt>.
  55948. #
  55949. # User.where(["name = '%s' and email = '%s'", "Joe", "joe@example.com"])
  55950. # # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
  55951. #
  55952. # If #where is called with multiple arguments, these are treated as if they were passed as
  55953. # the elements of a single array.
  55954. #
  55955. # User.where("name = :name and email = :email", { name: "Joe", email: "joe@example.com" })
  55956. # # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
  55957. #
  55958. # When using strings to specify conditions, you can use any operator available from
  55959. # the database. While this provides the most flexibility, you can also unintentionally introduce
  55960. # dependencies on the underlying database. If your code is intended for general consumption,
  55961. # test with multiple database backends.
  55962. #
  55963. # === hash
  55964. #
  55965. # #where will also accept a hash condition, in which the keys are fields and the values
  55966. # are values to be searched for.
  55967. #
  55968. # Fields can be symbols or strings. Values can be single values, arrays, or ranges.
  55969. #
  55970. # User.where({ name: "Joe", email: "joe@example.com" })
  55971. # # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
  55972. #
  55973. # User.where({ name: ["Alice", "Bob"]})
  55974. # # SELECT * FROM users WHERE name IN ('Alice', 'Bob')
  55975. #
  55976. # User.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
  55977. # # SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
  55978. #
  55979. # In the case of a belongs_to relationship, an association key can be used
  55980. # to specify the model if an ActiveRecord object is used as the value.
  55981. #
  55982. # author = Author.find(1)
  55983. #
  55984. # # The following queries will be equivalent:
  55985. # Post.where(author: author)
  55986. # Post.where(author_id: author)
  55987. #
  55988. # This also works with polymorphic belongs_to relationships:
  55989. #
  55990. # treasure = Treasure.create(name: 'gold coins')
  55991. # treasure.price_estimates << PriceEstimate.create(price: 125)
  55992. #
  55993. # # The following queries will be equivalent:
  55994. # PriceEstimate.where(estimate_of: treasure)
  55995. # PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
  55996. #
  55997. # === Joins
  55998. #
  55999. # If the relation is the result of a join, you may create a condition which uses any of the
  56000. # tables in the join. For string and array conditions, use the table name in the condition.
  56001. #
  56002. # User.joins(:posts).where("posts.created_at < ?", Time.now)
  56003. #
  56004. # For hash conditions, you can either use the table name in the key, or use a sub-hash.
  56005. #
  56006. # User.joins(:posts).where({ "posts.published" => true })
  56007. # User.joins(:posts).where({ posts: { published: true } })
  56008. #
  56009. # === no argument
  56010. #
  56011. # If no argument is passed, #where returns a new instance of WhereChain, that
  56012. # can be chained with #not to return a new relation that negates the where clause.
  56013. #
  56014. # User.where.not(name: "Jon")
  56015. # # SELECT * FROM users WHERE name != 'Jon'
  56016. #
  56017. # See WhereChain for more details on #not.
  56018. #
  56019. # === blank condition
  56020. #
  56021. # If the condition is any blank-ish object, then #where is a no-op and returns
  56022. # the current relation.
  56023. def where(opts = :chain, *rest)
  56024. if opts == :chain
  56025. WhereChain.new(spawn)
  56026. elsif opts.blank?
  56027. self
  56028. else
  56029. spawn.where!(opts, *rest)
  56030. end
  56031. end
  56032. # #where! is identical to #where, except that instead of returning a new relation, it adds
  56033. # the condition to the existing relation.
  56034. def where!(opts = :chain, *rest) # :nodoc:
  56035. if opts == :chain
  56036. WhereChain.new(self)
  56037. else
  56038. references!(PredicateBuilder.references(opts)) if Hash === opts
  56039. self.where_values += build_where(opts, rest)
  56040. self
  56041. end
  56042. end
  56043. # Allows to specify a HAVING clause. Note that you can't use HAVING
  56044. # without also specifying a GROUP clause.
  56045. #
  56046. # Order.having('SUM(price) > 30').group('user_id')
  56047. def having(opts, *rest)
  56048. opts.blank? ? self : spawn.having!(opts, *rest)
  56049. end
  56050. def having!(opts, *rest) # :nodoc:
  56051. references!(PredicateBuilder.references(opts)) if Hash === opts
  56052. self.having_values += build_where(opts, rest)
  56053. self
  56054. end
  56055. # Specifies a limit for the number of records to retrieve.
  56056. #
  56057. # User.limit(10) # generated SQL has 'LIMIT 10'
  56058. #
  56059. # User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
  56060. def limit(value)
  56061. spawn.limit!(value)
  56062. end
  56063. def limit!(value) # :nodoc:
  56064. self.limit_value = value
  56065. self
  56066. end
  56067. # Specifies the number of rows to skip before returning rows.
  56068. #
  56069. # User.offset(10) # generated SQL has "OFFSET 10"
  56070. #
  56071. # Should be used with order.
  56072. #
  56073. # User.offset(10).order("name ASC")
  56074. def offset(value)
  56075. spawn.offset!(value)
  56076. end
  56077. def offset!(value) # :nodoc:
  56078. self.offset_value = value
  56079. self
  56080. end
  56081. # Specifies locking settings (default to +true+). For more information
  56082. # on locking, please see +ActiveRecord::Locking+.
  56083. def lock(locks = true)
  56084. spawn.lock!(locks)
  56085. end
  56086. def lock!(locks = true) # :nodoc:
  56087. case locks
  56088. when String, TrueClass, NilClass
  56089. self.lock_value = locks || true
  56090. else
  56091. self.lock_value = false
  56092. end
  56093. self
  56094. end
  56095. # Returns a chainable relation with zero records, specifically an
  56096. # instance of the <tt>ActiveRecord::NullRelation</tt> class.
  56097. #
  56098. # The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
  56099. # Null Object pattern. It is an object with defined null behavior and always returns an empty
  56100. # array of records without quering the database.
  56101. #
  56102. # Any subsequent condition chained to the returned relation will continue
  56103. # generating an empty relation and will not fire any query to the database.
  56104. #
  56105. # Used in cases where a method or scope could return zero records but the
  56106. # result needs to be chainable.
  56107. #
  56108. # For example:
  56109. #
  56110. # @posts = current_user.visible_posts.where(name: params[:name])
  56111. # # => the visible_posts method is expected to return a chainable Relation
  56112. #
  56113. # def visible_posts
  56114. # case role
  56115. # when 'Country Manager'
  56116. # Post.where(country: country)
  56117. # when 'Reviewer'
  56118. # Post.published
  56119. # when 'Bad User'
  56120. # Post.none # => returning [] instead breaks the previous code
  56121. # end
  56122. # end
  56123. #
  56124. def none
  56125. extending(NullRelation)
  56126. end
  56127. def none! # :nodoc:
  56128. extending!(NullRelation)
  56129. end
  56130. # Sets readonly attributes for the returned relation. If value is
  56131. # true (default), attempting to update a record will result in an error.
  56132. #
  56133. # users = User.readonly
  56134. # users.first.save
  56135. # => ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
  56136. def readonly(value = true)
  56137. spawn.readonly!(value)
  56138. end
  56139. def readonly!(value = true) # :nodoc:
  56140. self.readonly_value = value
  56141. self
  56142. end
  56143. # Sets attributes to be used when creating new records from a
  56144. # relation object.
  56145. #
  56146. # users = User.where(name: 'Oscar')
  56147. # users.new.name # => 'Oscar'
  56148. #
  56149. # users = users.create_with(name: 'DHH')
  56150. # users.new.name # => 'DHH'
  56151. #
  56152. # You can pass +nil+ to +create_with+ to reset attributes:
  56153. #
  56154. # users = users.create_with(nil)
  56155. # users.new.name # => 'Oscar'
  56156. def create_with(value)
  56157. spawn.create_with!(value)
  56158. end
  56159. def create_with!(value) # :nodoc:
  56160. self.create_with_value = value ? create_with_value.merge(value) : {}
  56161. self
  56162. end
  56163. # Specifies table from which the records will be fetched. For example:
  56164. #
  56165. # Topic.select('title').from('posts')
  56166. # #=> SELECT title FROM posts
  56167. #
  56168. # Can accept other relation objects. For example:
  56169. #
  56170. # Topic.select('title').from(Topic.approved)
  56171. # # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
  56172. #
  56173. # Topic.select('a.title').from(Topic.approved, :a)
  56174. # # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
  56175. #
  56176. def from(value, subquery_name = nil)
  56177. spawn.from!(value, subquery_name)
  56178. end
  56179. # Like #from, but modifies relation in place.
  56180. def from!(value, subquery_name = nil) # :nodoc:
  56181. self.from_value = [value, subquery_name]
  56182. self
  56183. end
  56184. # Specifies whether the records should be unique or not. For example:
  56185. #
  56186. # User.select(:name)
  56187. # # => Might return two records with the same name
  56188. #
  56189. # User.select(:name).uniq
  56190. # # => Returns 1 record per unique name
  56191. #
  56192. # User.select(:name).uniq.uniq(false)
  56193. # # => You can also remove the uniqueness
  56194. def uniq(value = true)
  56195. spawn.uniq!(value)
  56196. end
  56197. # Like #uniq, but modifies relation in place.
  56198. def uniq!(value = true) # :nodoc:
  56199. self.uniq_value = value
  56200. self
  56201. end
  56202. # Used to extend a scope with additional methods, either through
  56203. # a module or through a block provided.
  56204. #
  56205. # The object returned is a relation, which can be further extended.
  56206. #
  56207. # === Using a module
  56208. #
  56209. # module Pagination
  56210. # def page(number)
  56211. # # pagination code goes here
  56212. # end
  56213. # end
  56214. #
  56215. # scope = Model.all.extending(Pagination)
  56216. # scope.page(params[:page])
  56217. #
  56218. # You can also pass a list of modules:
  56219. #
  56220. # scope = Model.all.extending(Pagination, SomethingElse)
  56221. #
  56222. # === Using a block
  56223. #
  56224. # scope = Model.all.extending do
  56225. # def page(number)
  56226. # # pagination code goes here
  56227. # end
  56228. # end
  56229. # scope.page(params[:page])
  56230. #
  56231. # You can also use a block and a module list:
  56232. #
  56233. # scope = Model.all.extending(Pagination) do
  56234. # def per_page(number)
  56235. # # pagination code goes here
  56236. # end
  56237. # end
  56238. def extending(*modules, &block)
  56239. if modules.any? || block
  56240. spawn.extending!(*modules, &block)
  56241. else
  56242. self
  56243. end
  56244. end
  56245. def extending!(*modules, &block) # :nodoc:
  56246. modules << Module.new(&block) if block_given?
  56247. self.extending_values += modules.flatten
  56248. extend(*extending_values) if extending_values.any?
  56249. self
  56250. end
  56251. # Reverse the existing order clause on the relation.
  56252. #
  56253. # User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
  56254. def reverse_order
  56255. spawn.reverse_order!
  56256. end
  56257. def reverse_order! # :nodoc:
  56258. self.reverse_order_value = !reverse_order_value
  56259. self
  56260. end
  56261. # Returns the Arel object associated with the relation.
  56262. def arel
  56263. @arel ||= with_default_scope.build_arel
  56264. end
  56265. # Like #arel, but ignores the default scope of the model.
  56266. def build_arel
  56267. arel = Arel::SelectManager.new(table.engine, table)
  56268. build_joins(arel, joins_values) unless joins_values.empty?
  56269. collapse_wheres(arel, (where_values - ['']).uniq)
  56270. arel.having(*having_values.uniq.reject{|h| h.blank?}) unless having_values.empty?
  56271. arel.take(connection.sanitize_limit(limit_value)) if limit_value
  56272. arel.skip(offset_value.to_i) if offset_value
  56273. arel.group(*group_values.uniq.reject{|g| g.blank?}) unless group_values.empty?
  56274. build_order(arel)
  56275. build_select(arel, select_values.uniq)
  56276. arel.distinct(uniq_value)
  56277. arel.from(build_from) if from_value
  56278. arel.lock(lock_value) if lock_value
  56279. arel
  56280. end
  56281. private
  56282. def custom_join_ast(table, joins)
  56283. joins = joins.reject { |join| join.blank? }
  56284. return [] if joins.empty?
  56285. @implicit_readonly = true
  56286. joins.map do |join|
  56287. case join
  56288. when Array
  56289. join = Arel.sql(join.join(' ')) if array_of_strings?(join)
  56290. when String
  56291. join = Arel.sql(join)
  56292. end
  56293. table.create_string_join(join)
  56294. end
  56295. end
  56296. def collapse_wheres(arel, wheres)
  56297. equalities = wheres.grep(Arel::Nodes::Equality)
  56298. arel.where(Arel::Nodes::And.new(equalities)) unless equalities.empty?
  56299. (wheres - equalities).each do |where|
  56300. where = Arel.sql(where) if String === where
  56301. arel.where(Arel::Nodes::Grouping.new(where))
  56302. end
  56303. end
  56304. def build_where(opts, other = [])
  56305. case opts
  56306. when String, Array
  56307. [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
  56308. when Hash
  56309. attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
  56310. attributes.values.grep(ActiveRecord::Relation) do |rel|
  56311. self.bind_values += rel.bind_values
  56312. end
  56313. PredicateBuilder.build_from_hash(klass, attributes, table)
  56314. else
  56315. [opts]
  56316. end
  56317. end
  56318. def build_from
  56319. opts, name = from_value
  56320. case opts
  56321. when Relation
  56322. name ||= 'subquery'
  56323. opts.arel.as(name.to_s)
  56324. else
  56325. opts
  56326. end
  56327. end
  56328. def build_joins(manager, joins)
  56329. buckets = joins.group_by do |join|
  56330. case join
  56331. when String
  56332. :string_join
  56333. when Hash, Symbol, Array
  56334. :association_join
  56335. when ActiveRecord::Associations::JoinDependency::JoinAssociation
  56336. :stashed_join
  56337. when Arel::Nodes::Join
  56338. :join_node
  56339. else
  56340. raise 'unknown class: %s' % join.class.name
  56341. end
  56342. end
  56343. association_joins = buckets[:association_join] || []
  56344. stashed_association_joins = buckets[:stashed_join] || []
  56345. join_nodes = (buckets[:join_node] || []).uniq
  56346. string_joins = (buckets[:string_join] || []).map { |x|
  56347. x.strip
  56348. }.uniq
  56349. join_list = join_nodes + custom_join_ast(manager, string_joins)
  56350. join_dependency = ActiveRecord::Associations::JoinDependency.new(
  56351. @klass,
  56352. association_joins,
  56353. join_list
  56354. )
  56355. join_dependency.graft(*stashed_association_joins)
  56356. @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
  56357. # FIXME: refactor this to build an AST
  56358. join_dependency.join_associations.each do |association|
  56359. association.join_to(manager)
  56360. end
  56361. manager.join_sources.concat join_list
  56362. manager
  56363. end
  56364. def build_select(arel, selects)
  56365. unless selects.empty?
  56366. @implicit_readonly = false
  56367. arel.project(*selects)
  56368. else
  56369. arel.project(@klass.arel_table[Arel.star])
  56370. end
  56371. end
  56372. def reverse_sql_order(order_query)
  56373. order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
  56374. order_query.flat_map do |o|
  56375. case o
  56376. when Arel::Nodes::Ordering
  56377. o.reverse
  56378. when String
  56379. o.to_s.split(',').collect do |s|
  56380. s.strip!
  56381. s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
  56382. end
  56383. when Symbol
  56384. { o => :desc }
  56385. when Hash
  56386. o.each_with_object({}) do |(field, dir), memo|
  56387. memo[field] = (dir == :asc ? :desc : :asc )
  56388. end
  56389. else
  56390. o
  56391. end
  56392. end
  56393. end
  56394. def array_of_strings?(o)
  56395. o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
  56396. end
  56397. def build_order(arel)
  56398. orders = order_values
  56399. orders = reverse_sql_order(orders) if reverse_order_value
  56400. orders = orders.uniq.reject(&:blank?).flat_map do |order|
  56401. case order
  56402. when Symbol
  56403. table[order].asc
  56404. when Hash
  56405. order.map { |field, dir| table[field].send(dir) }
  56406. else
  56407. order
  56408. end
  56409. end
  56410. arel.order(*orders) unless orders.empty?
  56411. end
  56412. def validate_order_args(args)
  56413. args.select { |a| Hash === a }.each do |h|
  56414. unless (h.values - [:asc, :desc]).empty?
  56415. raise ArgumentError, 'Direction should be :asc or :desc'
  56416. end
  56417. end
  56418. end
  56419. end
  56420. end
  56421. require 'active_support/core_ext/hash/except'
  56422. require 'active_support/core_ext/hash/slice'
  56423. require 'active_record/relation/merger'
  56424. module ActiveRecord
  56425. module SpawnMethods
  56426. # This is overridden by Associations::CollectionProxy
  56427. def spawn #:nodoc:
  56428. clone
  56429. end
  56430. # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
  56431. # Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
  56432. # Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
  56433. # # Performs a single join query with both where conditions.
  56434. #
  56435. # recent_posts = Post.order('created_at DESC').first(5)
  56436. # Post.where(published: true).merge(recent_posts)
  56437. # # Returns the intersection of all published posts with the 5 most recently created posts.
  56438. # # (This is just an example. You'd probably want to do this with a single query!)
  56439. #
  56440. # Procs will be evaluated by merge:
  56441. #
  56442. # Post.where(published: true).merge(-> { joins(:comments) })
  56443. # # => Post.where(published: true).joins(:comments)
  56444. #
  56445. # This is mainly intended for sharing common conditions between multiple associations.
  56446. def merge(other)
  56447. if other.is_a?(Array)
  56448. to_a & other
  56449. elsif other
  56450. spawn.merge!(other)
  56451. else
  56452. self
  56453. end
  56454. end
  56455. def merge!(other) # :nodoc:
  56456. if !other.is_a?(Relation) && other.respond_to?(:to_proc)
  56457. instance_exec(&other)
  56458. else
  56459. klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
  56460. klass.new(self, other).merge
  56461. end
  56462. end
  56463. # Removes from the query the condition(s) specified in +skips+.
  56464. #
  56465. # Post.order('id asc').except(:order) # discards the order condition
  56466. # Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
  56467. def except(*skips)
  56468. relation_with values.except(*skips)
  56469. end
  56470. # Removes any condition from the query other than the one(s) specified in +onlies+.
  56471. #
  56472. # Post.order('id asc').only(:where) # discards the order condition
  56473. # Post.order('id asc').only(:where, :order) # uses the specified order
  56474. def only(*onlies)
  56475. relation_with values.slice(*onlies)
  56476. end
  56477. private
  56478. def relation_with(values) # :nodoc:
  56479. result = Relation.new(klass, table, values)
  56480. result.default_scoped = default_scoped
  56481. result.extend(*extending_values) if extending_values.any?
  56482. result
  56483. end
  56484. end
  56485. end
  56486. # -*- coding: utf-8 -*-
  56487. module ActiveRecord
  56488. # = Active Record Relation
  56489. class Relation
  56490. JoinOperation = Struct.new(:relation, :join_class, :on)
  56491. MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
  56492. :order, :joins, :where, :having, :bind, :references,
  56493. :extending]
  56494. SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
  56495. :reverse_order, :uniq, :create_with]
  56496. VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
  56497. include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
  56498. attr_reader :table, :klass, :loaded
  56499. attr_accessor :default_scoped
  56500. alias :model :klass
  56501. alias :loaded? :loaded
  56502. alias :default_scoped? :default_scoped
  56503. def initialize(klass, table, values = {})
  56504. @klass = klass
  56505. @table = table
  56506. @values = values
  56507. @implicit_readonly = nil
  56508. @loaded = false
  56509. @default_scoped = false
  56510. end
  56511. def initialize_copy(other)
  56512. # This method is a hot spot, so for now, use Hash[] to dup the hash.
  56513. # https://bugs.ruby-lang.org/issues/7166
  56514. @values = Hash[@values]
  56515. @values[:bind] = @values[:bind].dup if @values.key? :bind
  56516. reset
  56517. end
  56518. def insert(values)
  56519. primary_key_value = nil
  56520. if primary_key && Hash === values
  56521. primary_key_value = values[values.keys.find { |k|
  56522. k.name == primary_key
  56523. }]
  56524. if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
  56525. primary_key_value = connection.next_sequence_value(klass.sequence_name)
  56526. values[klass.arel_table[klass.primary_key]] = primary_key_value
  56527. end
  56528. end
  56529. im = arel.create_insert
  56530. im.into @table
  56531. conn = @klass.connection
  56532. substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
  56533. binds = substitutes.map do |arel_attr, value|
  56534. [@klass.columns_hash[arel_attr.name], value]
  56535. end
  56536. substitutes.each_with_index do |tuple, i|
  56537. tuple[1] = conn.substitute_at(binds[i][0], i)
  56538. end
  56539. if values.empty? # empty insert
  56540. im.values = Arel.sql(connection.empty_insert_statement_value)
  56541. else
  56542. im.insert substitutes
  56543. end
  56544. conn.insert(
  56545. im,
  56546. 'SQL',
  56547. primary_key,
  56548. primary_key_value,
  56549. nil,
  56550. binds)
  56551. end
  56552. # Initializes new record from relation while maintaining the current
  56553. # scope.
  56554. #
  56555. # Expects arguments in the same format as +Base.new+.
  56556. #
  56557. # users = User.where(name: 'DHH')
  56558. # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
  56559. #
  56560. # You can also pass a block to new with the new record as argument:
  56561. #
  56562. # user = users.new { |user| user.name = 'Oscar' }
  56563. # user.name # => Oscar
  56564. def new(*args, &block)
  56565. scoping { @klass.new(*args, &block) }
  56566. end
  56567. alias build new
  56568. # Tries to create a new record with the same scoped attributes
  56569. # defined in the relation. Returns the initialized object if validation fails.
  56570. #
  56571. # Expects arguments in the same format as +Base.create+.
  56572. #
  56573. # ==== Examples
  56574. # users = User.where(name: 'Oscar')
  56575. # users.create # #<User id: 3, name: "oscar", ...>
  56576. #
  56577. # users.create(name: 'fxn')
  56578. # users.create # #<User id: 4, name: "fxn", ...>
  56579. #
  56580. # users.create { |user| user.name = 'tenderlove' }
  56581. # # #<User id: 5, name: "tenderlove", ...>
  56582. #
  56583. # users.create(name: nil) # validation on name
  56584. # # #<User id: nil, name: nil, ...>
  56585. def create(*args, &block)
  56586. scoping { @klass.create(*args, &block) }
  56587. end
  56588. # Similar to #create, but calls +create!+ on the base class. Raises
  56589. # an exception if a validation error occurs.
  56590. #
  56591. # Expects arguments in the same format as <tt>Base.create!</tt>.
  56592. def create!(*args, &block)
  56593. scoping { @klass.create!(*args, &block) }
  56594. end
  56595. def first_or_create(attributes = nil, &block) # :nodoc:
  56596. first || create(attributes, &block)
  56597. end
  56598. def first_or_create!(attributes = nil, &block) # :nodoc:
  56599. first || create!(attributes, &block)
  56600. end
  56601. def first_or_initialize(attributes = nil, &block) # :nodoc:
  56602. first || new(attributes, &block)
  56603. end
  56604. # Finds the first record with the given attributes, or creates a record with the attributes
  56605. # if one is not found.
  56606. #
  56607. # ==== Examples
  56608. # # Find the first user named Penélope or create a new one.
  56609. # User.find_or_create_by(first_name: 'Penélope')
  56610. # # => <User id: 1, first_name: 'Penélope', last_name: nil>
  56611. #
  56612. # # Find the first user named Penélope or create a new one.
  56613. # # We already have one so the existing record will be returned.
  56614. # User.find_or_create_by(first_name: 'Penélope')
  56615. # # => <User id: 1, first_name: 'Penélope', last_name: nil>
  56616. #
  56617. # # Find the first user named Scarlett or create a new one with a particular last name.
  56618. # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
  56619. # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
  56620. #
  56621. # # Find the first user named Scarlett or create a new one with a different last name.
  56622. # # We already have one so the existing record will be returned.
  56623. # User.find_or_create_by(first_name: 'Scarlett') do |user|
  56624. # user.last_name = "O'Hara"
  56625. # end
  56626. # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
  56627. def find_or_create_by(attributes, &block)
  56628. find_by(attributes) || create(attributes, &block)
  56629. end
  56630. # Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
  56631. def find_or_create_by!(attributes, &block)
  56632. find_by(attributes) || create!(attributes, &block)
  56633. end
  56634. # Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
  56635. def find_or_initialize_by(attributes, &block)
  56636. find_by(attributes) || new(attributes, &block)
  56637. end
  56638. # Runs EXPLAIN on the query or queries triggered by this relation and
  56639. # returns the result as a string. The string is formatted imitating the
  56640. # ones printed by the database shell.
  56641. #
  56642. # Note that this method actually runs the queries, since the results of some
  56643. # are needed by the next ones when eager loading is going on.
  56644. #
  56645. # Please see further details in the
  56646. # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
  56647. def explain
  56648. _, queries = collecting_queries_for_explain { exec_queries }
  56649. exec_explain(queries)
  56650. end
  56651. # Converts relation objects to Array.
  56652. def to_a
  56653. load
  56654. @records
  56655. end
  56656. def as_json(options = nil) #:nodoc:
  56657. to_a.as_json(options)
  56658. end
  56659. # Returns size of the records.
  56660. def size
  56661. loaded? ? @records.length : count
  56662. end
  56663. # Returns true if there are no records.
  56664. def empty?
  56665. return @records.empty? if loaded?
  56666. c = count
  56667. c.respond_to?(:zero?) ? c.zero? : c.empty?
  56668. end
  56669. # Returns true if there are any records.
  56670. def any?
  56671. if block_given?
  56672. to_a.any? { |*block_args| yield(*block_args) }
  56673. else
  56674. !empty?
  56675. end
  56676. end
  56677. # Returns true if there is more than one record.
  56678. def many?
  56679. if block_given?
  56680. to_a.many? { |*block_args| yield(*block_args) }
  56681. else
  56682. limit_value ? to_a.many? : size > 1
  56683. end
  56684. end
  56685. # Scope all queries to the current scope.
  56686. #
  56687. # Comment.where(post_id: 1).scoping do
  56688. # Comment.first # SELECT * FROM comments WHERE post_id = 1
  56689. # end
  56690. #
  56691. # Please check unscoped if you want to remove all previous scopes (including
  56692. # the default_scope) during the execution of a block.
  56693. def scoping
  56694. previous, klass.current_scope = klass.current_scope, self
  56695. yield
  56696. ensure
  56697. klass.current_scope = previous
  56698. end
  56699. # Updates all records with details given if they match a set of conditions supplied, limits and order can
  56700. # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
  56701. # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
  56702. # or validations.
  56703. #
  56704. # ==== Parameters
  56705. #
  56706. # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
  56707. #
  56708. # ==== Examples
  56709. #
  56710. # # Update all customers with the given attributes
  56711. # Customer.update_all wants_email: true
  56712. #
  56713. # # Update all books with 'Rails' in their title
  56714. # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
  56715. #
  56716. # # Update all books that match conditions, but limit it to 5 ordered by date
  56717. # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
  56718. def update_all(updates)
  56719. raise ArgumentError, "Empty list of attributes to change" if updates.blank?
  56720. stmt = Arel::UpdateManager.new(arel.engine)
  56721. stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
  56722. stmt.table(table)
  56723. stmt.key = table[primary_key]
  56724. if with_default_scope.joins_values.any?
  56725. @klass.connection.join_to_update(stmt, arel)
  56726. else
  56727. stmt.take(arel.limit)
  56728. stmt.order(*arel.orders)
  56729. stmt.wheres = arel.constraints
  56730. end
  56731. @klass.connection.update stmt, 'SQL', bind_values
  56732. end
  56733. # Updates an object (or multiple objects) and saves it to the database, if validations pass.
  56734. # The resulting object is returned whether the object was saved successfully to the database or not.
  56735. #
  56736. # ==== Parameters
  56737. #
  56738. # * +id+ - This should be the id or an array of ids to be updated.
  56739. # * +attributes+ - This should be a hash of attributes or an array of hashes.
  56740. #
  56741. # ==== Examples
  56742. #
  56743. # # Updates one record
  56744. # Person.update(15, user_name: 'Samuel', group: 'expert')
  56745. #
  56746. # # Updates multiple records
  56747. # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
  56748. # Person.update(people.keys, people.values)
  56749. def update(id, attributes)
  56750. if id.is_a?(Array)
  56751. id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
  56752. else
  56753. object = find(id)
  56754. object.update(attributes)
  56755. object
  56756. end
  56757. end
  56758. # Destroys the records matching +conditions+ by instantiating each
  56759. # record and calling its +destroy+ method. Each object's callbacks are
  56760. # executed (including <tt>:dependent</tt> association options). Returns the
  56761. # collection of objects that were destroyed; each will be frozen, to
  56762. # reflect that no changes should be made (since they can't be persisted).
  56763. #
  56764. # Note: Instantiation, callback execution, and deletion of each
  56765. # record can be time consuming when you're removing many records at
  56766. # once. It generates at least one SQL +DELETE+ query per record (or
  56767. # possibly more, to enforce your callbacks). If you want to delete many
  56768. # rows quickly, without concern for their associations or callbacks, use
  56769. # +delete_all+ instead.
  56770. #
  56771. # ==== Parameters
  56772. #
  56773. # * +conditions+ - A string, array, or hash that specifies which records
  56774. # to destroy. If omitted, all records are destroyed. See the
  56775. # Conditions section in the introduction to ActiveRecord::Base for
  56776. # more information.
  56777. #
  56778. # ==== Examples
  56779. #
  56780. # Person.destroy_all("last_login < '2004-04-04'")
  56781. # Person.destroy_all(status: "inactive")
  56782. # Person.where(age: 0..18).destroy_all
  56783. def destroy_all(conditions = nil)
  56784. if conditions
  56785. where(conditions).destroy_all
  56786. else
  56787. to_a.each {|object| object.destroy }.tap { reset }
  56788. end
  56789. end
  56790. # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
  56791. # therefore all callbacks and filters are fired off before the object is deleted. This method is
  56792. # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
  56793. #
  56794. # This essentially finds the object (or multiple objects) with the given id, creates a new object
  56795. # from the attributes, and then calls destroy on it.
  56796. #
  56797. # ==== Parameters
  56798. #
  56799. # * +id+ - Can be either an Integer or an Array of Integers.
  56800. #
  56801. # ==== Examples
  56802. #
  56803. # # Destroy a single object
  56804. # Todo.destroy(1)
  56805. #
  56806. # # Destroy multiple objects
  56807. # todos = [1,2,3]
  56808. # Todo.destroy(todos)
  56809. def destroy(id)
  56810. if id.is_a?(Array)
  56811. id.map { |one_id| destroy(one_id) }
  56812. else
  56813. find(id).destroy
  56814. end
  56815. end
  56816. # Deletes the records matching +conditions+ without instantiating the records
  56817. # first, and hence not calling the +destroy+ method nor invoking callbacks. This
  56818. # is a single SQL DELETE statement that goes straight to the database, much more
  56819. # efficient than +destroy_all+. Be careful with relations though, in particular
  56820. # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
  56821. # number of rows affected.
  56822. #
  56823. # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
  56824. # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
  56825. # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
  56826. #
  56827. # Both calls delete the affected posts all at once with a single DELETE statement.
  56828. # If you need to destroy dependent associations or call your <tt>before_*</tt> or
  56829. # +after_destroy+ callbacks, use the +destroy_all+ method instead.
  56830. #
  56831. # If a limit scope is supplied, +delete_all+ raises an ActiveRecord error:
  56832. #
  56833. # Post.limit(100).delete_all
  56834. # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit scope
  56835. def delete_all(conditions = nil)
  56836. raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
  56837. if conditions
  56838. where(conditions).delete_all
  56839. else
  56840. stmt = Arel::DeleteManager.new(arel.engine)
  56841. stmt.from(table)
  56842. if with_default_scope.joins_values.any?
  56843. @klass.connection.join_to_delete(stmt, arel, table[primary_key])
  56844. else
  56845. stmt.wheres = arel.constraints
  56846. end
  56847. affected = @klass.connection.delete(stmt, 'SQL', bind_values)
  56848. reset
  56849. affected
  56850. end
  56851. end
  56852. # Deletes the row with a primary key matching the +id+ argument, using a
  56853. # SQL +DELETE+ statement, and returns the number of rows deleted. Active
  56854. # Record objects are not instantiated, so the object's callbacks are not
  56855. # executed, including any <tt>:dependent</tt> association options.
  56856. #
  56857. # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
  56858. #
  56859. # Note: Although it is often much faster than the alternative,
  56860. # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
  56861. # your application that ensures referential integrity or performs other
  56862. # essential jobs.
  56863. #
  56864. # ==== Examples
  56865. #
  56866. # # Delete a single row
  56867. # Todo.delete(1)
  56868. #
  56869. # # Delete multiple rows
  56870. # Todo.delete([2,3,4])
  56871. def delete(id_or_array)
  56872. where(primary_key => id_or_array).delete_all
  56873. end
  56874. # Causes the records to be loaded from the database if they have not
  56875. # been loaded already. You can use this if for some reason you need
  56876. # to explicitly load some records before actually using them. The
  56877. # return value is the relation itself, not the records.
  56878. #
  56879. # Post.where(published: true).load # => #<ActiveRecord::Relation>
  56880. def load
  56881. unless loaded?
  56882. # We monitor here the entire execution rather than individual SELECTs
  56883. # because from the point of view of the user fetching the records of a
  56884. # relation is a single unit of work. You want to know if this call takes
  56885. # too long, not if the individual queries take too long.
  56886. #
  56887. # It could be the case that none of the queries involved surpass the
  56888. # threshold, and at the same time the sum of them all does. The user
  56889. # should get a query plan logged in that case.
  56890. logging_query_plan { exec_queries }
  56891. end
  56892. self
  56893. end
  56894. # Forces reloading of relation.
  56895. def reload
  56896. reset
  56897. load
  56898. end
  56899. def reset
  56900. @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
  56901. @should_eager_load = @join_dependency = nil
  56902. @records = []
  56903. self
  56904. end
  56905. # Returns sql statement for the relation.
  56906. #
  56907. # User.where(name: 'Oscar').to_sql
  56908. # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
  56909. def to_sql
  56910. @to_sql ||= klass.connection.to_sql(arel, bind_values.dup)
  56911. end
  56912. # Returns a hash of where conditions.
  56913. #
  56914. # User.where(name: 'Oscar').where_values_hash
  56915. # # => {name: "Oscar"}
  56916. def where_values_hash
  56917. equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
  56918. node.left.relation.name == table_name
  56919. }
  56920. binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
  56921. Hash[equalities.map { |where|
  56922. name = where.left.name
  56923. [name, binds.fetch(name.to_s) { where.right }]
  56924. }]
  56925. end
  56926. def scope_for_create
  56927. @scope_for_create ||= where_values_hash.merge(create_with_value)
  56928. end
  56929. # Returns true if relation needs eager loading.
  56930. def eager_loading?
  56931. @should_eager_load ||=
  56932. eager_load_values.any? ||
  56933. includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
  56934. end
  56935. # Joins that are also marked for preloading. In which case we should just eager load them.
  56936. # Note that this is a naive implementation because we could have strings and symbols which
  56937. # represent the same association, but that aren't matched by this. Also, we could have
  56938. # nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
  56939. def joined_includes_values
  56940. includes_values & joins_values
  56941. end
  56942. # Compares two relations for equality.
  56943. def ==(other)
  56944. case other
  56945. when Relation
  56946. other.to_sql == to_sql
  56947. when Array
  56948. to_a == other
  56949. end
  56950. end
  56951. def pretty_print(q)
  56952. q.pp(self.to_a)
  56953. end
  56954. def with_default_scope #:nodoc:
  56955. if default_scoped? && default_scope = klass.send(:build_default_scope)
  56956. default_scope = default_scope.merge(self)
  56957. default_scope.default_scoped = false
  56958. default_scope
  56959. else
  56960. self
  56961. end
  56962. end
  56963. # Returns true if relation is blank.
  56964. def blank?
  56965. to_a.blank?
  56966. end
  56967. def values
  56968. Hash[@values]
  56969. end
  56970. def inspect
  56971. entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
  56972. entries[10] = '...' if entries.size == 11
  56973. "#<#{self.class.name} [#{entries.join(', ')}]>"
  56974. end
  56975. private
  56976. def exec_queries
  56977. default_scoped = with_default_scope
  56978. if default_scoped.equal?(self)
  56979. @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
  56980. preload = preload_values
  56981. preload += includes_values unless eager_loading?
  56982. preload.each do |associations|
  56983. ActiveRecord::Associations::Preloader.new(@records, associations).run
  56984. end
  56985. # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
  56986. # are JOINS and no explicit SELECT.
  56987. readonly = readonly_value.nil? ? @implicit_readonly : readonly_value
  56988. @records.each { |record| record.readonly! } if readonly
  56989. else
  56990. @records = default_scoped.to_a
  56991. end
  56992. @loaded = true
  56993. @records
  56994. end
  56995. def references_eager_loaded_tables?
  56996. joined_tables = arel.join_sources.map do |join|
  56997. if join.is_a?(Arel::Nodes::StringJoin)
  56998. tables_in_string(join.left)
  56999. else
  57000. [join.left.table_name, join.left.table_alias]
  57001. end
  57002. end
  57003. joined_tables += [table.name, table.table_alias]
  57004. # always convert table names to downcase as in Oracle quoted table names are in uppercase
  57005. joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
  57006. string_tables = tables_in_string(to_sql)
  57007. if (references_values - joined_tables).any?
  57008. true
  57009. elsif (string_tables - joined_tables).any?
  57010. ActiveSupport::Deprecation.warn(
  57011. "It looks like you are eager loading table(s) (one of: #{string_tables.join(', ')}) " \
  57012. "that are referenced in a string SQL snippet. For example: \n" \
  57013. "\n" \
  57014. " Post.includes(:comments).where(\"comments.title = 'foo'\")\n" \
  57015. "\n" \
  57016. "Currently, Active Record recognises the table in the string, and knows to JOIN the " \
  57017. "comments table to the query, rather than loading comments in a separate query. " \
  57018. "However, doing this without writing a full-blown SQL parser is inherently flawed. " \
  57019. "Since we don't want to write an SQL parser, we are removing this functionality. " \
  57020. "From now on, you must explicitly tell Active Record when you are referencing a table " \
  57021. "from a string:\n" \
  57022. "\n" \
  57023. " Post.includes(:comments).where(\"comments.title = 'foo'\").references(:comments)\n\n"
  57024. )
  57025. true
  57026. else
  57027. false
  57028. end
  57029. end
  57030. def tables_in_string(string)
  57031. return [] if string.blank?
  57032. # always convert table names to downcase as in Oracle quoted table names are in uppercase
  57033. # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
  57034. string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
  57035. end
  57036. end
  57037. end
  57038. module ActiveRecord
  57039. ###
  57040. # This class encapsulates a Result returned from calling +exec_query+ on any
  57041. # database connection adapter. For example:
  57042. #
  57043. # x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
  57044. # x # => #<ActiveRecord::Result:0xdeadbeef>
  57045. class Result
  57046. include Enumerable
  57047. attr_reader :columns, :rows, :column_types
  57048. def initialize(columns, rows, column_types = {})
  57049. @columns = columns
  57050. @rows = rows
  57051. @hash_rows = nil
  57052. @column_types = column_types
  57053. end
  57054. def each
  57055. hash_rows.each { |row| yield row }
  57056. end
  57057. def to_hash
  57058. hash_rows
  57059. end
  57060. alias :map! :map
  57061. alias :collect! :map
  57062. # Returns true if there are no records.
  57063. def empty?
  57064. rows.empty?
  57065. end
  57066. def to_ary
  57067. hash_rows
  57068. end
  57069. def [](idx)
  57070. hash_rows[idx]
  57071. end
  57072. def last
  57073. hash_rows.last
  57074. end
  57075. def initialize_copy(other)
  57076. @columns = columns.dup
  57077. @rows = rows.dup
  57078. @hash_rows = nil
  57079. end
  57080. private
  57081. def hash_rows
  57082. @hash_rows ||=
  57083. begin
  57084. # We freeze the strings to prevent them getting duped when
  57085. # used as keys in ActiveRecord::Base's @attributes hash
  57086. columns = @columns.map { |c| c.dup.freeze }
  57087. @rows.map { |row|
  57088. Hash[columns.zip(row)]
  57089. }
  57090. end
  57091. end
  57092. end
  57093. end
  57094. module ActiveRecord
  57095. module Sanitization
  57096. extend ActiveSupport::Concern
  57097. module ClassMethods
  57098. def quote_value(value, column = nil) #:nodoc:
  57099. connection.quote(value,column)
  57100. end
  57101. # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
  57102. def sanitize(object) #:nodoc:
  57103. connection.quote(object)
  57104. end
  57105. protected
  57106. # Accepts an array, hash, or string of SQL conditions and sanitizes
  57107. # them into a valid SQL fragment for a WHERE clause.
  57108. # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
  57109. # { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
  57110. # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
  57111. def sanitize_sql_for_conditions(condition, table_name = self.table_name)
  57112. return nil if condition.blank?
  57113. case condition
  57114. when Array; sanitize_sql_array(condition)
  57115. when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
  57116. else condition
  57117. end
  57118. end
  57119. alias_method :sanitize_sql, :sanitize_sql_for_conditions
  57120. # Accepts an array, hash, or string of SQL conditions and sanitizes
  57121. # them into a valid SQL fragment for a SET clause.
  57122. # { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
  57123. def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
  57124. case assignments
  57125. when Array; sanitize_sql_array(assignments)
  57126. when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
  57127. else assignments
  57128. end
  57129. end
  57130. # Accepts a hash of SQL conditions and replaces those attributes
  57131. # that correspond to a +composed_of+ relationship with their expanded
  57132. # aggregate attribute values.
  57133. # Given:
  57134. # class Person < ActiveRecord::Base
  57135. # composed_of :address, class_name: "Address",
  57136. # mapping: [%w(address_street street), %w(address_city city)]
  57137. # end
  57138. # Then:
  57139. # { address: Address.new("813 abc st.", "chicago") }
  57140. # # => { address_street: "813 abc st.", address_city: "chicago" }
  57141. def expand_hash_conditions_for_aggregates(attrs)
  57142. expanded_attrs = {}
  57143. attrs.each do |attr, value|
  57144. if aggregation = reflect_on_aggregation(attr.to_sym)
  57145. mapping = aggregation.mapping
  57146. mapping.each do |field_attr, aggregate_attr|
  57147. if mapping.size == 1 && !value.respond_to?(aggregate_attr)
  57148. expanded_attrs[field_attr] = value
  57149. else
  57150. expanded_attrs[field_attr] = value.send(aggregate_attr)
  57151. end
  57152. end
  57153. else
  57154. expanded_attrs[attr] = value
  57155. end
  57156. end
  57157. expanded_attrs
  57158. end
  57159. # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
  57160. # { name: "foo'bar", group_id: 4 }
  57161. # # => "name='foo''bar' and group_id= 4"
  57162. # { status: nil, group_id: [1,2,3] }
  57163. # # => "status IS NULL and group_id IN (1,2,3)"
  57164. # { age: 13..18 }
  57165. # # => "age BETWEEN 13 AND 18"
  57166. # { 'other_records.id' => 7 }
  57167. # # => "`other_records`.`id` = 7"
  57168. # { other_records: { id: 7 } }
  57169. # # => "`other_records`.`id` = 7"
  57170. # And for value objects on a composed_of relationship:
  57171. # { address: Address.new("123 abc st.", "chicago") }
  57172. # # => "address_street='123 abc st.' and address_city='chicago'"
  57173. def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
  57174. attrs = expand_hash_conditions_for_aggregates(attrs)
  57175. table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
  57176. PredicateBuilder.build_from_hash(self.class, attrs, table).map { |b|
  57177. connection.visitor.accept b
  57178. }.join(' AND ')
  57179. end
  57180. alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
  57181. # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
  57182. # { status: nil, group_id: 1 }
  57183. # # => "status = NULL , group_id = 1"
  57184. def sanitize_sql_hash_for_assignment(attrs, table)
  57185. attrs.map do |attr, value|
  57186. "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}"
  57187. end.join(', ')
  57188. end
  57189. # Accepts an array of conditions. The array has each value
  57190. # sanitized and interpolated into the SQL statement.
  57191. # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
  57192. def sanitize_sql_array(ary)
  57193. statement, *values = ary
  57194. if values.first.is_a?(Hash) && statement =~ /:\w+/
  57195. replace_named_bind_variables(statement, values.first)
  57196. elsif statement.include?('?')
  57197. replace_bind_variables(statement, values)
  57198. elsif statement.blank?
  57199. statement
  57200. else
  57201. statement % values.collect { |value| connection.quote_string(value.to_s) }
  57202. end
  57203. end
  57204. alias_method :sanitize_conditions, :sanitize_sql
  57205. def replace_bind_variables(statement, values) #:nodoc:
  57206. raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
  57207. bound = values.dup
  57208. c = connection
  57209. statement.gsub('?') { quote_bound_value(bound.shift, c) }
  57210. end
  57211. def replace_named_bind_variables(statement, bind_vars) #:nodoc:
  57212. statement.gsub(/(:?):([a-zA-Z]\w*)/) do
  57213. if $1 == ':' # skip postgresql casts
  57214. $& # return the whole match
  57215. elsif bind_vars.include?(match = $2.to_sym)
  57216. quote_bound_value(bind_vars[match])
  57217. else
  57218. raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
  57219. end
  57220. end
  57221. end
  57222. def quote_bound_value(value, c = connection) #:nodoc:
  57223. if value.respond_to?(:map) && !value.acts_like?(:string)
  57224. if value.respond_to?(:empty?) && value.empty?
  57225. c.quote(nil)
  57226. else
  57227. value.map { |v| c.quote(v) }.join(',')
  57228. end
  57229. else
  57230. c.quote(value)
  57231. end
  57232. end
  57233. def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
  57234. unless expected == provided
  57235. raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
  57236. end
  57237. end
  57238. end
  57239. # TODO: Deprecate this
  57240. def quoted_id
  57241. self.class.quote_value(id, column_for_attribute(self.class.primary_key))
  57242. end
  57243. end
  57244. end
  57245. module ActiveRecord
  57246. # = Active Record Schema
  57247. #
  57248. # Allows programmers to programmatically define a schema in a portable
  57249. # DSL. This means you can define tables, indexes, etc. without using SQL
  57250. # directly, so your applications can more easily support multiple
  57251. # databases.
  57252. #
  57253. # Usage:
  57254. #
  57255. # ActiveRecord::Schema.define do
  57256. # create_table :authors do |t|
  57257. # t.string :name, null: false
  57258. # end
  57259. #
  57260. # add_index :authors, :name, :unique
  57261. #
  57262. # create_table :posts do |t|
  57263. # t.integer :author_id, null: false
  57264. # t.string :subject
  57265. # t.text :body
  57266. # t.boolean :private, default: false
  57267. # end
  57268. #
  57269. # add_index :posts, :author_id
  57270. # end
  57271. #
  57272. # ActiveRecord::Schema is only supported by database adapters that also
  57273. # support migrations, the two features being very similar.
  57274. class Schema < Migration
  57275. # Returns the migrations paths.
  57276. #
  57277. # ActiveRecord::Schema.new.migrations_paths
  57278. # # => ["db/migrate"] # Rails migration path by default.
  57279. def migrations_paths
  57280. ActiveRecord::Migrator.migrations_paths
  57281. end
  57282. def define(info, &block) # :nodoc:
  57283. instance_eval(&block)
  57284. unless info[:version].blank?
  57285. initialize_schema_migrations_table
  57286. assume_migrated_upto_version(info[:version], migrations_paths)
  57287. end
  57288. end
  57289. # Eval the given block. All methods available to the current connection
  57290. # adapter are available within the block, so you can easily use the
  57291. # database definition DSL to build up your schema (+create_table+,
  57292. # +add_index+, etc.).
  57293. #
  57294. # The +info+ hash is optional, and if given is used to define metadata
  57295. # about the current schema (currently, only the schema's version):
  57296. #
  57297. # ActiveRecord::Schema.define(version: 20380119000001) do
  57298. # ...
  57299. # end
  57300. def self.define(info={}, &block)
  57301. new.define(info, &block)
  57302. end
  57303. end
  57304. end
  57305. require 'stringio'
  57306. require 'active_support/core_ext/big_decimal'
  57307. module ActiveRecord
  57308. # = Active Record Schema Dumper
  57309. #
  57310. # This class is used to dump the database schema for some connection to some
  57311. # output format (i.e., ActiveRecord::Schema).
  57312. class SchemaDumper #:nodoc:
  57313. private_class_method :new
  57314. ##
  57315. # :singleton-method:
  57316. # A list of tables which should not be dumped to the schema.
  57317. # Acceptable values are strings as well as regexp.
  57318. # This setting is only used if ActiveRecord::Base.schema_format == :ruby
  57319. cattr_accessor :ignore_tables
  57320. @@ignore_tables = []
  57321. def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)
  57322. new(connection).dump(stream)
  57323. stream
  57324. end
  57325. def dump(stream)
  57326. header(stream)
  57327. extensions(stream)
  57328. tables(stream)
  57329. trailer(stream)
  57330. stream
  57331. end
  57332. private
  57333. def initialize(connection)
  57334. @connection = connection
  57335. @types = @connection.native_database_types
  57336. @version = Migrator::current_version rescue nil
  57337. end
  57338. def header(stream)
  57339. define_params = @version ? "version: #{@version}" : ""
  57340. if stream.respond_to?(:external_encoding) && stream.external_encoding
  57341. stream.puts "# encoding: #{stream.external_encoding.name}"
  57342. end
  57343. stream.puts <<HEADER
  57344. # This file is auto-generated from the current state of the database. Instead
  57345. # of editing this file, please use the migrations feature of Active Record to
  57346. # incrementally modify your database, and then regenerate this schema definition.
  57347. #
  57348. # Note that this schema.rb definition is the authoritative source for your
  57349. # database schema. If you need to create the application database on another
  57350. # system, you should be using db:schema:load, not running all the migrations
  57351. # from scratch. The latter is a flawed and unsustainable approach (the more migrations
  57352. # you'll amass, the slower it'll run and the greater likelihood for issues).
  57353. #
  57354. # It's strongly recommended that you check this file into your version control system.
  57355. ActiveRecord::Schema.define(#{define_params}) do
  57356. HEADER
  57357. end
  57358. def trailer(stream)
  57359. stream.puts "end"
  57360. end
  57361. def extensions(stream)
  57362. return unless @connection.supports_extensions?
  57363. extensions = @connection.extensions
  57364. if extensions.any?
  57365. stream.puts " # These are extensions that must be enabled in order to support this database"
  57366. extensions.each do |extension|
  57367. stream.puts " enable_extension #{extension.inspect}"
  57368. end
  57369. stream.puts
  57370. end
  57371. end
  57372. def tables(stream)
  57373. @connection.tables.sort.each do |tbl|
  57374. next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
  57375. case ignored
  57376. when String; remove_prefix_and_suffix(tbl) == ignored
  57377. when Regexp; remove_prefix_and_suffix(tbl) =~ ignored
  57378. else
  57379. raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
  57380. end
  57381. end
  57382. table(tbl, stream)
  57383. end
  57384. end
  57385. def table(table, stream)
  57386. columns = @connection.columns(table)
  57387. begin
  57388. tbl = StringIO.new
  57389. # first dump primary key column
  57390. if @connection.respond_to?(:pk_and_sequence_for)
  57391. pk, _ = @connection.pk_and_sequence_for(table)
  57392. elsif @connection.respond_to?(:primary_key)
  57393. pk = @connection.primary_key(table)
  57394. end
  57395. tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
  57396. if columns.detect { |c| c.name == pk }
  57397. if pk != 'id'
  57398. tbl.print %Q(, primary_key: "#{pk}")
  57399. end
  57400. else
  57401. tbl.print ", id: false"
  57402. end
  57403. tbl.print ", force: true"
  57404. tbl.puts " do |t|"
  57405. # then dump all non-primary key columns
  57406. column_specs = columns.map do |column|
  57407. raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
  57408. next if column.name == pk
  57409. @connection.column_spec(column, @types)
  57410. end.compact
  57411. # find all migration keys used in this table
  57412. keys = @connection.migration_keys
  57413. # figure out the lengths for each column based on above keys
  57414. lengths = keys.map { |key|
  57415. column_specs.map { |spec|
  57416. spec[key] ? spec[key].length + 2 : 0
  57417. }.max
  57418. }
  57419. # the string we're going to sprintf our values against, with standardized column widths
  57420. format_string = lengths.map{ |len| "%-#{len}s" }
  57421. # find the max length for the 'type' column, which is special
  57422. type_length = column_specs.map{ |column| column[:type].length }.max
  57423. # add column type definition to our format string
  57424. format_string.unshift " t.%-#{type_length}s "
  57425. format_string *= ''
  57426. column_specs.each do |colspec|
  57427. values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
  57428. values.unshift colspec[:type]
  57429. tbl.print((format_string % values).gsub(/,\s*$/, ''))
  57430. tbl.puts
  57431. end
  57432. tbl.puts " end"
  57433. tbl.puts
  57434. indexes(table, tbl)
  57435. tbl.rewind
  57436. stream.print tbl.read
  57437. rescue => e
  57438. stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
  57439. stream.puts "# #{e.message}"
  57440. stream.puts
  57441. end
  57442. stream
  57443. end
  57444. def indexes(table, stream)
  57445. if (indexes = @connection.indexes(table)).any?
  57446. add_index_statements = indexes.map do |index|
  57447. statement_parts = [
  57448. ('add_index ' + remove_prefix_and_suffix(index.table).inspect),
  57449. index.columns.inspect,
  57450. ('name: ' + index.name.inspect),
  57451. ]
  57452. statement_parts << 'unique: true' if index.unique
  57453. index_lengths = (index.lengths || []).compact
  57454. statement_parts << ('length: ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
  57455. index_orders = (index.orders || {})
  57456. statement_parts << ('order: ' + index.orders.inspect) unless index_orders.empty?
  57457. statement_parts << ('where: ' + index.where.inspect) if index.where
  57458. ' ' + statement_parts.join(', ')
  57459. end
  57460. stream.puts add_index_statements.sort.join("\n")
  57461. stream.puts
  57462. end
  57463. end
  57464. def remove_prefix_and_suffix(table)
  57465. table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
  57466. end
  57467. end
  57468. end
  57469. require 'active_record/scoping/default'
  57470. require 'active_record/scoping/named'
  57471. require 'active_record/base'
  57472. module ActiveRecord
  57473. class SchemaMigration < ActiveRecord::Base
  57474. def self.table_name
  57475. "#{Base.table_name_prefix}schema_migrations#{Base.table_name_suffix}"
  57476. end
  57477. def self.index_name
  57478. "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
  57479. end
  57480. def self.create_table
  57481. unless connection.table_exists?(table_name)
  57482. connection.create_table(table_name, :id => false) do |t|
  57483. t.column :version, :string, :null => false
  57484. end
  57485. connection.add_index table_name, :version, :unique => true, :name => index_name
  57486. end
  57487. end
  57488. def self.drop_table
  57489. if connection.table_exists?(table_name)
  57490. connection.remove_index table_name, :name => index_name
  57491. connection.drop_table(table_name)
  57492. end
  57493. end
  57494. def version
  57495. super.to_i
  57496. end
  57497. end
  57498. end
  57499. module ActiveRecord
  57500. module Scoping
  57501. module Default
  57502. extend ActiveSupport::Concern
  57503. included do
  57504. # Stores the default scope for the class.
  57505. class_attribute :default_scopes, instance_writer: false
  57506. self.default_scopes = []
  57507. end
  57508. module ClassMethods
  57509. # Returns a scope for the model without the +default_scope+.
  57510. #
  57511. # class Post < ActiveRecord::Base
  57512. # def self.default_scope
  57513. # where published: true
  57514. # end
  57515. # end
  57516. #
  57517. # Post.all # Fires "SELECT * FROM posts WHERE published = true"
  57518. # Post.unscoped.all # Fires "SELECT * FROM posts"
  57519. #
  57520. # This method also accepts a block. All queries inside the block will
  57521. # not use the +default_scope+:
  57522. #
  57523. # Post.unscoped {
  57524. # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
  57525. # }
  57526. #
  57527. # It is recommended that you use the block form of unscoped because
  57528. # chaining unscoped with +scope+ does not work. Assuming that
  57529. # +published+ is a +scope+, the following two statements
  57530. # are equal: the +default_scope+ is applied on both.
  57531. #
  57532. # Post.unscoped.published
  57533. # Post.published
  57534. def unscoped
  57535. block_given? ? relation.scoping { yield } : relation
  57536. end
  57537. def before_remove_const #:nodoc:
  57538. self.current_scope = nil
  57539. end
  57540. protected
  57541. # Use this macro in your model to set a default scope for all operations on
  57542. # the model.
  57543. #
  57544. # class Article < ActiveRecord::Base
  57545. # default_scope { where(published: true) }
  57546. # end
  57547. #
  57548. # Article.all # => SELECT * FROM articles WHERE published = true
  57549. #
  57550. # The +default_scope+ is also applied while creating/building a record.
  57551. # It is not applied while updating a record.
  57552. #
  57553. # Article.new.published # => true
  57554. # Article.create.published # => true
  57555. #
  57556. # (You can also pass any object which responds to +call+ to the
  57557. # +default_scope+ macro, and it will be called when building the
  57558. # default scope.)
  57559. #
  57560. # If you use multiple +default_scope+ declarations in your model then
  57561. # they will be merged together:
  57562. #
  57563. # class Article < ActiveRecord::Base
  57564. # default_scope { where(published: true) }
  57565. # default_scope { where(rating: 'G') }
  57566. # end
  57567. #
  57568. # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
  57569. #
  57570. # This is also the case with inheritance and module includes where the
  57571. # parent or module defines a +default_scope+ and the child or including
  57572. # class defines a second one.
  57573. #
  57574. # If you need to do more complex things with a default scope, you can
  57575. # alternatively define it as a class method:
  57576. #
  57577. # class Article < ActiveRecord::Base
  57578. # def self.default_scope
  57579. # # Should return a scope, you can call 'super' here etc.
  57580. # end
  57581. # end
  57582. def default_scope(scope = nil)
  57583. scope = Proc.new if block_given?
  57584. if scope.is_a?(Relation) || !scope.respond_to?(:call)
  57585. ActiveSupport::Deprecation.warn(
  57586. "Calling #default_scope without a block is deprecated. For example instead " \
  57587. "of `default_scope where(color: 'red')`, please use " \
  57588. "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
  57589. "self.default_scope.)"
  57590. )
  57591. end
  57592. self.default_scopes += [scope]
  57593. end
  57594. def build_default_scope # :nodoc:
  57595. if !Base.is_a?(method(:default_scope).owner)
  57596. # The user has defined their own default scope method, so call that
  57597. evaluate_default_scope { default_scope }
  57598. elsif default_scopes.any?
  57599. evaluate_default_scope do
  57600. default_scopes.inject(relation) do |default_scope, scope|
  57601. if !scope.is_a?(Relation) && scope.respond_to?(:call)
  57602. default_scope.merge(unscoped { scope.call })
  57603. else
  57604. default_scope.merge(scope)
  57605. end
  57606. end
  57607. end
  57608. end
  57609. end
  57610. def ignore_default_scope? # :nodoc:
  57611. Thread.current["#{self}_ignore_default_scope"]
  57612. end
  57613. def ignore_default_scope=(ignore) # :nodoc:
  57614. Thread.current["#{self}_ignore_default_scope"] = ignore
  57615. end
  57616. # The ignore_default_scope flag is used to prevent an infinite recursion
  57617. # situation where a default scope references a scope which has a default
  57618. # scope which references a scope...
  57619. def evaluate_default_scope # :nodoc:
  57620. return if ignore_default_scope?
  57621. begin
  57622. self.ignore_default_scope = true
  57623. yield
  57624. ensure
  57625. self.ignore_default_scope = false
  57626. end
  57627. end
  57628. end
  57629. end
  57630. end
  57631. end
  57632. require 'active_support/core_ext/array'
  57633. require 'active_support/core_ext/hash/except'
  57634. require 'active_support/core_ext/kernel/singleton_class'
  57635. module ActiveRecord
  57636. # = Active Record \Named \Scopes
  57637. module Scoping
  57638. module Named
  57639. extend ActiveSupport::Concern
  57640. module ClassMethods
  57641. # Returns an <tt>ActiveRecord::Relation</tt> scope object.
  57642. #
  57643. # posts = Post.all
  57644. # posts.size # Fires "select count(*) from posts" and returns the count
  57645. # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
  57646. #
  57647. # fruits = Fruit.all
  57648. # fruits = fruits.where(color: 'red') if options[:red_only]
  57649. # fruits = fruits.limit(10) if limited?
  57650. #
  57651. # You can define a scope that applies to all finders using
  57652. # <tt>ActiveRecord::Base.default_scope</tt>.
  57653. def all
  57654. if current_scope
  57655. current_scope.clone
  57656. else
  57657. scope = relation
  57658. scope.default_scoped = true
  57659. scope
  57660. end
  57661. end
  57662. # Collects attributes from scopes that should be applied when creating
  57663. # an AR instance for the particular class this is called on.
  57664. def scope_attributes # :nodoc:
  57665. if current_scope
  57666. current_scope.scope_for_create
  57667. else
  57668. scope = relation
  57669. scope.default_scoped = true
  57670. scope.scope_for_create
  57671. end
  57672. end
  57673. # Are there default attributes associated with this scope?
  57674. def scope_attributes? # :nodoc:
  57675. current_scope || default_scopes.any?
  57676. end
  57677. # Adds a class method for retrieving and querying objects. A \scope
  57678. # represents a narrowing of a database query, such as
  57679. # <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
  57680. #
  57681. # class Shirt < ActiveRecord::Base
  57682. # scope :red, -> { where(color: 'red') }
  57683. # scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
  57684. # end
  57685. #
  57686. # The above calls to +scope+ define class methods <tt>Shirt.red</tt> and
  57687. # <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
  57688. # represents the query <tt>Shirt.where(color: 'red')</tt>.
  57689. #
  57690. # You should always pass a callable object to the scopes defined
  57691. # with +scope+. This ensures that the scope is re-evaluated each
  57692. # time it is called.
  57693. #
  57694. # Note that this is simply 'syntactic sugar' for defining an actual
  57695. # class method:
  57696. #
  57697. # class Shirt < ActiveRecord::Base
  57698. # def self.red
  57699. # where(color: 'red')
  57700. # end
  57701. # end
  57702. #
  57703. # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
  57704. # <tt>Shirt.red</tt> is not an Array; it resembles the association object
  57705. # constructed by a +has_many+ declaration. For instance, you can invoke
  57706. # <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
  57707. # <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
  57708. # association objects, named \scopes act like an Array, implementing
  57709. # Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
  57710. # and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
  57711. # <tt>Shirt.red</tt> really was an Array.
  57712. #
  57713. # These named \scopes are composable. For instance,
  57714. # <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
  57715. # both red and dry clean only. Nested finds and calculations also work
  57716. # with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
  57717. # returns the number of garments for which these criteria obtain.
  57718. # Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
  57719. #
  57720. # All scopes are available as class methods on the ActiveRecord::Base
  57721. # descendant upon which the \scopes were defined. But they are also
  57722. # available to +has_many+ associations. If,
  57723. #
  57724. # class Person < ActiveRecord::Base
  57725. # has_many :shirts
  57726. # end
  57727. #
  57728. # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
  57729. # Elton's red, dry clean only shirts.
  57730. #
  57731. # \Named scopes can also have extensions, just as with +has_many+
  57732. # declarations:
  57733. #
  57734. # class Shirt < ActiveRecord::Base
  57735. # scope :red, -> { where(color: 'red') } do
  57736. # def dom_id
  57737. # 'red_shirts'
  57738. # end
  57739. # end
  57740. # end
  57741. #
  57742. # Scopes can also be used while creating/building a record.
  57743. #
  57744. # class Article < ActiveRecord::Base
  57745. # scope :published, -> { where(published: true) }
  57746. # end
  57747. #
  57748. # Article.published.new.published # => true
  57749. # Article.published.create.published # => true
  57750. #
  57751. # \Class methods on your model are automatically available
  57752. # on scopes. Assuming the following setup:
  57753. #
  57754. # class Article < ActiveRecord::Base
  57755. # scope :published, -> { where(published: true) }
  57756. # scope :featured, -> { where(featured: true) }
  57757. #
  57758. # def self.latest_article
  57759. # order('published_at desc').first
  57760. # end
  57761. #
  57762. # def self.titles
  57763. # pluck(:title)
  57764. # end
  57765. # end
  57766. #
  57767. # We are able to call the methods like this:
  57768. #
  57769. # Article.published.featured.latest_article
  57770. # Article.featured.titles
  57771. def scope(name, body, &block)
  57772. extension = Module.new(&block) if block
  57773. # Check body.is_a?(Relation) to prevent the relation actually being
  57774. # loaded by respond_to?
  57775. if body.is_a?(Relation) || !body.respond_to?(:call)
  57776. ActiveSupport::Deprecation.warn(
  57777. "Using #scope without passing a callable object is deprecated. For " \
  57778. "example `scope :red, where(color: 'red')` should be changed to " \
  57779. "`scope :red, -> { where(color: 'red') }`. There are numerous gotchas " \
  57780. "in the former usage and it makes the implementation more complicated " \
  57781. "and buggy. (If you prefer, you can just define a class method named " \
  57782. "`self.red`.)"
  57783. )
  57784. end
  57785. singleton_class.send(:define_method, name) do |*args|
  57786. options = body.respond_to?(:call) ? unscoped { body.call(*args) } : body
  57787. relation = all.merge(options)
  57788. extension ? relation.extending(extension) : relation
  57789. end
  57790. end
  57791. end
  57792. end
  57793. end
  57794. end
  57795. module ActiveRecord
  57796. module Scoping
  57797. extend ActiveSupport::Concern
  57798. included do
  57799. include Default
  57800. include Named
  57801. end
  57802. module ClassMethods
  57803. def current_scope #:nodoc:
  57804. Thread.current["#{self}_current_scope"]
  57805. end
  57806. def current_scope=(scope) #:nodoc:
  57807. Thread.current["#{self}_current_scope"] = scope
  57808. end
  57809. end
  57810. def populate_with_current_scope_attributes
  57811. return unless self.class.scope_attributes?
  57812. self.class.scope_attributes.each do |att,value|
  57813. send("#{att}=", value) if respond_to?("#{att}=")
  57814. end
  57815. end
  57816. end
  57817. end
  57818. module ActiveRecord #:nodoc:
  57819. # = Active Record Serialization
  57820. module Serialization
  57821. extend ActiveSupport::Concern
  57822. include ActiveModel::Serializers::JSON
  57823. included do
  57824. self.include_root_in_json = true
  57825. end
  57826. def serializable_hash(options = nil)
  57827. options = options.try(:clone) || {}
  57828. options[:except] = Array(options[:except]).map { |n| n.to_s }
  57829. options[:except] |= Array(self.class.inheritance_column)
  57830. super(options)
  57831. end
  57832. end
  57833. end
  57834. require 'active_record/serializers/xml_serializer'
  57835. require 'active_support/core_ext/hash/conversions'
  57836. module ActiveRecord #:nodoc:
  57837. module Serialization
  57838. include ActiveModel::Serializers::Xml
  57839. # Builds an XML document to represent the model. Some configuration is
  57840. # available through +options+. However more complicated cases should
  57841. # override ActiveRecord::Base#to_xml.
  57842. #
  57843. # By default the generated XML document will include the processing
  57844. # instruction and all the object's attributes. For example:
  57845. #
  57846. # <?xml version="1.0" encoding="UTF-8"?>
  57847. # <topic>
  57848. # <title>The First Topic</title>
  57849. # <author-name>David</author-name>
  57850. # <id type="integer">1</id>
  57851. # <approved type="boolean">false</approved>
  57852. # <replies-count type="integer">0</replies-count>
  57853. # <bonus-time type="dateTime">2000-01-01T08:28:00+12:00</bonus-time>
  57854. # <written-on type="dateTime">2003-07-16T09:28:00+1200</written-on>
  57855. # <content>Have a nice day</content>
  57856. # <author-email-address>david@loudthinking.com</author-email-address>
  57857. # <parent-id></parent-id>
  57858. # <last-read type="date">2004-04-15</last-read>
  57859. # </topic>
  57860. #
  57861. # This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
  57862. # <tt>:skip_instruct</tt>, <tt>:skip_types</tt>, <tt>:dasherize</tt> and <tt>:camelize</tt> .
  57863. # The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
  57864. # +attributes+ method. The default is to dasherize all column names, but you
  57865. # can disable this setting <tt>:dasherize</tt> to +false+. Setting <tt>:camelize</tt>
  57866. # to +true+ will camelize all column names - this also overrides <tt>:dasherize</tt>.
  57867. # To not have the column type included in the XML output set <tt>:skip_types</tt> to +true+.
  57868. #
  57869. # For instance:
  57870. #
  57871. # topic.to_xml(skip_instruct: true, except: [ :id, :bonus_time, :written_on, :replies_count ])
  57872. #
  57873. # <topic>
  57874. # <title>The First Topic</title>
  57875. # <author-name>David</author-name>
  57876. # <approved type="boolean">false</approved>
  57877. # <content>Have a nice day</content>
  57878. # <author-email-address>david@loudthinking.com</author-email-address>
  57879. # <parent-id></parent-id>
  57880. # <last-read type="date">2004-04-15</last-read>
  57881. # </topic>
  57882. #
  57883. # To include first level associations use <tt>:include</tt>:
  57884. #
  57885. # firm.to_xml include: [ :account, :clients ]
  57886. #
  57887. # <?xml version="1.0" encoding="UTF-8"?>
  57888. # <firm>
  57889. # <id type="integer">1</id>
  57890. # <rating type="integer">1</rating>
  57891. # <name>37signals</name>
  57892. # <clients type="array">
  57893. # <client>
  57894. # <rating type="integer">1</rating>
  57895. # <name>Summit</name>
  57896. # </client>
  57897. # <client>
  57898. # <rating type="integer">1</rating>
  57899. # <name>Microsoft</name>
  57900. # </client>
  57901. # </clients>
  57902. # <account>
  57903. # <id type="integer">1</id>
  57904. # <credit-limit type="integer">50</credit-limit>
  57905. # </account>
  57906. # </firm>
  57907. #
  57908. # Additionally, the record being serialized will be passed to a Proc's second
  57909. # parameter. This allows for ad hoc additions to the resultant document that
  57910. # incorporate the context of the record being serialized. And by leveraging the
  57911. # closure created by a Proc, to_xml can be used to add elements that normally fall
  57912. # outside of the scope of the model -- for example, generating and appending URLs
  57913. # associated with models.
  57914. #
  57915. # proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
  57916. # firm.to_xml procs: [ proc ]
  57917. #
  57918. # <firm>
  57919. # # ... normal attributes as shown above ...
  57920. # <name-reverse>slangis73</name-reverse>
  57921. # </firm>
  57922. #
  57923. # To include deeper levels of associations pass a hash like this:
  57924. #
  57925. # firm.to_xml include: {account: {}, clients: {include: :address}}
  57926. # <?xml version="1.0" encoding="UTF-8"?>
  57927. # <firm>
  57928. # <id type="integer">1</id>
  57929. # <rating type="integer">1</rating>
  57930. # <name>37signals</name>
  57931. # <clients type="array">
  57932. # <client>
  57933. # <rating type="integer">1</rating>
  57934. # <name>Summit</name>
  57935. # <address>
  57936. # ...
  57937. # </address>
  57938. # </client>
  57939. # <client>
  57940. # <rating type="integer">1</rating>
  57941. # <name>Microsoft</name>
  57942. # <address>
  57943. # ...
  57944. # </address>
  57945. # </client>
  57946. # </clients>
  57947. # <account>
  57948. # <id type="integer">1</id>
  57949. # <credit-limit type="integer">50</credit-limit>
  57950. # </account>
  57951. # </firm>
  57952. #
  57953. # To include any methods on the model being called use <tt>:methods</tt>:
  57954. #
  57955. # firm.to_xml methods: [ :calculated_earnings, :real_earnings ]
  57956. #
  57957. # <firm>
  57958. # # ... normal attributes as shown above ...
  57959. # <calculated-earnings>100000000000000000</calculated-earnings>
  57960. # <real-earnings>5</real-earnings>
  57961. # </firm>
  57962. #
  57963. # To call any additional Procs use <tt>:procs</tt>. The Procs are passed a
  57964. # modified version of the options hash that was given to +to_xml+:
  57965. #
  57966. # proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
  57967. # firm.to_xml procs: [ proc ]
  57968. #
  57969. # <firm>
  57970. # # ... normal attributes as shown above ...
  57971. # <abc>def</abc>
  57972. # </firm>
  57973. #
  57974. # Alternatively, you can yield the builder object as part of the +to_xml+ call:
  57975. #
  57976. # firm.to_xml do |xml|
  57977. # xml.creator do
  57978. # xml.first_name "David"
  57979. # xml.last_name "Heinemeier Hansson"
  57980. # end
  57981. # end
  57982. #
  57983. # <firm>
  57984. # # ... normal attributes as shown above ...
  57985. # <creator>
  57986. # <first_name>David</first_name>
  57987. # <last_name>Heinemeier Hansson</last_name>
  57988. # </creator>
  57989. # </firm>
  57990. #
  57991. # As noted above, you may override +to_xml+ in your ActiveRecord::Base
  57992. # subclasses to have complete control about what's generated. The general
  57993. # form of doing this is:
  57994. #
  57995. # class IHaveMyOwnXML < ActiveRecord::Base
  57996. # def to_xml(options = {})
  57997. # require 'builder'
  57998. # options[:indent] ||= 2
  57999. # xml = options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])
  58000. # xml.instruct! unless options[:skip_instruct]
  58001. # xml.level_one do
  58002. # xml.tag!(:second_level, 'content')
  58003. # end
  58004. # end
  58005. # end
  58006. def to_xml(options = {}, &block)
  58007. XmlSerializer.new(self, options).serialize(&block)
  58008. end
  58009. end
  58010. class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
  58011. class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
  58012. def compute_type
  58013. klass = @serializable.class
  58014. type = if klass.serialized_attributes.key?(name)
  58015. super
  58016. elsif klass.columns_hash.key?(name)
  58017. klass.columns_hash[name].type
  58018. else
  58019. NilClass
  58020. end
  58021. { :text => :string,
  58022. :time => :datetime }[type] || type
  58023. end
  58024. protected :compute_type
  58025. end
  58026. end
  58027. end
  58028. require 'active_support/core_ext/hash/indifferent_access'
  58029. module ActiveRecord
  58030. # Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
  58031. # It's like a simple key/value store baked into your record when you don't care about being able to
  58032. # query that store outside the context of a single record.
  58033. #
  58034. # You can then declare accessors to this store that are then accessible just like any other attribute
  58035. # of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
  58036. # already built around just accessing attributes on the model.
  58037. #
  58038. # Make sure that you declare the database column used for the serialized store as a text, so there's
  58039. # plenty of room.
  58040. #
  58041. # You can set custom coder to encode/decode your serialized attributes to/from different formats.
  58042. # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
  58043. #
  58044. # Examples:
  58045. #
  58046. # class User < ActiveRecord::Base
  58047. # store :settings, accessors: [ :color, :homepage ], coder: JSON
  58048. # end
  58049. #
  58050. # u = User.new(color: 'black', homepage: '37signals.com')
  58051. # u.color # Accessor stored attribute
  58052. # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
  58053. #
  58054. # # There is no difference between strings and symbols for accessing custom attributes
  58055. # u.settings[:country] # => 'Denmark'
  58056. # u.settings['country'] # => 'Denmark'
  58057. #
  58058. # # Add additional accessors to an existing store through store_accessor
  58059. # class SuperUser < User
  58060. # store_accessor :settings, :privileges, :servants
  58061. # end
  58062. #
  58063. # The stored attribute names can be retrieved using +stored_attributes+.
  58064. #
  58065. # User.stored_attributes[:settings] # [:color, :homepage]
  58066. #
  58067. # == Overwriting default accessors
  58068. #
  58069. # All stored values are automatically available through accessors on the Active Record
  58070. # object, but sometimes you want to specialize this behavior. This can be done by overwriting
  58071. # the default accessors (using the same name as the attribute) and calling <tt>super</tt>
  58072. # to actually change things.
  58073. #
  58074. # class Song < ActiveRecord::Base
  58075. # # Uses a stored integer to hold the volume adjustment of the song
  58076. # store :settings, accessors: [:volume_adjustment]
  58077. #
  58078. # def volume_adjustment=(decibels)
  58079. # super(decibels.to_i)
  58080. # end
  58081. #
  58082. # def volume_adjustment
  58083. # super.to_i
  58084. # end
  58085. # end
  58086. module Store
  58087. extend ActiveSupport::Concern
  58088. included do
  58089. class_attribute :stored_attributes, instance_accessor: false
  58090. self.stored_attributes = {}
  58091. end
  58092. module ClassMethods
  58093. def store(store_attribute, options = {})
  58094. serialize store_attribute, IndifferentCoder.new(options[:coder])
  58095. store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
  58096. end
  58097. def store_accessor(store_attribute, *keys)
  58098. keys = keys.flatten
  58099. _store_accessors_module.module_eval do
  58100. keys.each do |key|
  58101. define_method("#{key}=") do |value|
  58102. write_store_attribute(store_attribute, key, value)
  58103. end
  58104. define_method(key) do
  58105. read_store_attribute(store_attribute, key)
  58106. end
  58107. end
  58108. end
  58109. self.stored_attributes[store_attribute] ||= []
  58110. self.stored_attributes[store_attribute] |= keys
  58111. end
  58112. def _store_accessors_module
  58113. @_store_accessors_module ||= begin
  58114. mod = Module.new
  58115. include mod
  58116. mod
  58117. end
  58118. end
  58119. end
  58120. protected
  58121. def read_store_attribute(store_attribute, key)
  58122. attribute = initialize_store_attribute(store_attribute)
  58123. attribute[key]
  58124. end
  58125. def write_store_attribute(store_attribute, key, value)
  58126. attribute = initialize_store_attribute(store_attribute)
  58127. if value != attribute[key]
  58128. send :"#{store_attribute}_will_change!"
  58129. attribute[key] = value
  58130. end
  58131. end
  58132. private
  58133. def initialize_store_attribute(store_attribute)
  58134. attribute = send(store_attribute)
  58135. unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
  58136. attribute = IndifferentCoder.as_indifferent_hash(attribute)
  58137. send :"#{store_attribute}=", attribute
  58138. end
  58139. attribute
  58140. end
  58141. class IndifferentCoder # :nodoc:
  58142. def initialize(coder_or_class_name)
  58143. @coder =
  58144. if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
  58145. coder_or_class_name
  58146. else
  58147. ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
  58148. end
  58149. end
  58150. def dump(obj)
  58151. @coder.dump self.class.as_indifferent_hash(obj)
  58152. end
  58153. def load(yaml)
  58154. self.class.as_indifferent_hash @coder.load(yaml)
  58155. end
  58156. def self.as_indifferent_hash(obj)
  58157. case obj
  58158. when ActiveSupport::HashWithIndifferentAccess
  58159. obj
  58160. when Hash
  58161. obj.with_indifferent_access
  58162. else
  58163. ActiveSupport::HashWithIndifferentAccess.new
  58164. end
  58165. end
  58166. end
  58167. end
  58168. end
  58169. module ActiveRecord
  58170. module Tasks # :nodoc:
  58171. class DatabaseAlreadyExists < StandardError; end # :nodoc:
  58172. class DatabaseNotSupported < StandardError; end # :nodoc:
  58173. module DatabaseTasks # :nodoc:
  58174. extend self
  58175. attr_writer :current_config
  58176. LOCAL_HOSTS = ['127.0.0.1', 'localhost']
  58177. def register_task(pattern, task)
  58178. @tasks ||= {}
  58179. @tasks[pattern] = task
  58180. end
  58181. register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks)
  58182. register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
  58183. register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
  58184. def current_config(options = {})
  58185. options.reverse_merge! :env => Rails.env
  58186. if options.has_key?(:config)
  58187. @current_config = options[:config]
  58188. else
  58189. @current_config ||= if ENV['DATABASE_URL']
  58190. database_url_config
  58191. else
  58192. ActiveRecord::Base.configurations[options[:env]]
  58193. end
  58194. end
  58195. end
  58196. def create(*arguments)
  58197. configuration = arguments.first
  58198. class_for_adapter(configuration['adapter']).new(*arguments).create
  58199. rescue DatabaseAlreadyExists
  58200. $stderr.puts "#{configuration['database']} already exists"
  58201. rescue Exception => error
  58202. $stderr.puts error, *(error.backtrace)
  58203. $stderr.puts "Couldn't create database for #{configuration.inspect}"
  58204. end
  58205. def create_all
  58206. each_local_configuration { |configuration| create configuration }
  58207. end
  58208. def create_current(environment = Rails.env)
  58209. each_current_configuration(environment) { |configuration|
  58210. create configuration
  58211. }
  58212. ActiveRecord::Base.establish_connection environment
  58213. end
  58214. def create_database_url
  58215. create database_url_config
  58216. end
  58217. def drop(*arguments)
  58218. configuration = arguments.first
  58219. class_for_adapter(configuration['adapter']).new(*arguments).drop
  58220. rescue Exception => error
  58221. $stderr.puts error, *(error.backtrace)
  58222. $stderr.puts "Couldn't drop #{configuration['database']}"
  58223. end
  58224. def drop_all
  58225. each_local_configuration { |configuration| drop configuration }
  58226. end
  58227. def drop_current(environment = Rails.env)
  58228. each_current_configuration(environment) { |configuration|
  58229. drop configuration
  58230. }
  58231. end
  58232. def drop_database_url
  58233. drop database_url_config
  58234. end
  58235. def charset_current(environment = Rails.env)
  58236. charset ActiveRecord::Base.configurations[environment]
  58237. end
  58238. def charset(*arguments)
  58239. configuration = arguments.first
  58240. class_for_adapter(configuration['adapter']).new(*arguments).charset
  58241. end
  58242. def collation_current(environment = Rails.env)
  58243. collation ActiveRecord::Base.configurations[environment]
  58244. end
  58245. def collation(*arguments)
  58246. configuration = arguments.first
  58247. class_for_adapter(configuration['adapter']).new(*arguments).collation
  58248. end
  58249. def purge(configuration)
  58250. class_for_adapter(configuration['adapter']).new(configuration).purge
  58251. end
  58252. def structure_dump(*arguments)
  58253. configuration = arguments.first
  58254. filename = arguments.delete_at 1
  58255. class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
  58256. end
  58257. def structure_load(*arguments)
  58258. configuration = arguments.first
  58259. filename = arguments.delete_at 1
  58260. class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
  58261. end
  58262. private
  58263. def database_url_config
  58264. @database_url_config ||=
  58265. ConnectionAdapters::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys
  58266. end
  58267. def class_for_adapter(adapter)
  58268. key = @tasks.keys.detect { |pattern| adapter[pattern] }
  58269. unless key
  58270. raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
  58271. end
  58272. @tasks[key]
  58273. end
  58274. def each_current_configuration(environment)
  58275. environments = [environment]
  58276. environments << 'test' if environment.development?
  58277. configurations = ActiveRecord::Base.configurations.values_at(*environments)
  58278. configurations.compact.each do |configuration|
  58279. yield configuration unless configuration['database'].blank?
  58280. end
  58281. end
  58282. def each_local_configuration
  58283. ActiveRecord::Base.configurations.each_value do |configuration|
  58284. next unless configuration['database']
  58285. if local_database?(configuration)
  58286. yield configuration
  58287. else
  58288. $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
  58289. end
  58290. end
  58291. end
  58292. def local_database?(configuration)
  58293. configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
  58294. end
  58295. end
  58296. end
  58297. end
  58298. module ActiveRecord
  58299. module Tasks # :nodoc:
  58300. class MySQLDatabaseTasks # :nodoc:
  58301. DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8'
  58302. DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci'
  58303. ACCESS_DENIED_ERROR = 1045
  58304. delegate :connection, :establish_connection, to: ActiveRecord::Base
  58305. def initialize(configuration)
  58306. @configuration = configuration
  58307. end
  58308. def create
  58309. establish_connection configuration_without_database
  58310. connection.create_database configuration['database'], creation_options
  58311. establish_connection configuration
  58312. rescue ActiveRecord::StatementInvalid => error
  58313. if /database exists/ === error.message
  58314. raise DatabaseAlreadyExists
  58315. else
  58316. raise
  58317. end
  58318. rescue error_class => error
  58319. if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
  58320. $stdout.print error.error
  58321. establish_connection root_configuration_without_database
  58322. connection.create_database configuration['database'], creation_options
  58323. connection.execute grant_statement.gsub(/\s+/, ' ').strip
  58324. establish_connection configuration
  58325. else
  58326. $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
  58327. $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
  58328. end
  58329. end
  58330. def drop
  58331. establish_connection configuration
  58332. connection.drop_database configuration['database']
  58333. end
  58334. def purge
  58335. establish_connection :test
  58336. connection.recreate_database configuration['database'], creation_options
  58337. end
  58338. def charset
  58339. connection.charset
  58340. end
  58341. def collation
  58342. connection.collation
  58343. end
  58344. def structure_dump(filename)
  58345. args = prepare_command_options('mysqldump')
  58346. args.concat(["--result-file", "#{filename}"])
  58347. args.concat(["--no-data"])
  58348. args.concat(["#{configuration['database']}"])
  58349. Kernel.system(*args)
  58350. end
  58351. def structure_load(filename)
  58352. args = prepare_command_options('mysql')
  58353. args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
  58354. args.concat(["--database", "#{configuration['database']}"])
  58355. Kernel.system(*args)
  58356. end
  58357. private
  58358. def configuration
  58359. @configuration
  58360. end
  58361. def configuration_without_database
  58362. configuration.merge('database' => nil)
  58363. end
  58364. def creation_options
  58365. Hash.new.tap do |options|
  58366. options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
  58367. options[:collation] = configuration['collation'] if configuration.include? 'collation'
  58368. # Set default charset only when collation isn't set.
  58369. options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
  58370. # Set default collation only when charset is also default.
  58371. options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
  58372. end
  58373. end
  58374. def error_class
  58375. if configuration['adapter'] =~ /jdbc/
  58376. require 'active_record/railties/jdbcmysql_error'
  58377. ArJdbcMySQL::Error
  58378. elsif defined?(Mysql2)
  58379. Mysql2::Error
  58380. elsif defined?(Mysql)
  58381. Mysql::Error
  58382. else
  58383. StandardError
  58384. end
  58385. end
  58386. def grant_statement
  58387. <<-SQL
  58388. GRANT ALL PRIVILEGES ON #{configuration['database']}.*
  58389. TO '#{configuration['username']}'@'localhost'
  58390. IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
  58391. SQL
  58392. end
  58393. def root_configuration_without_database
  58394. configuration_without_database.merge(
  58395. 'username' => 'root',
  58396. 'password' => root_password
  58397. )
  58398. end
  58399. def root_password
  58400. $stdout.print "Please provide the root password for your mysql installation\n>"
  58401. $stdin.gets.strip
  58402. end
  58403. def prepare_command_options(command)
  58404. args = [command]
  58405. args.concat(['--user', configuration['username']]) if configuration['username']
  58406. args << "--password=#{configuration['password']}" if configuration['password']
  58407. args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
  58408. configuration.slice('host', 'port', 'socket').each do |k, v|
  58409. args.concat([ "--#{k}", v ]) if v
  58410. end
  58411. args
  58412. end
  58413. end
  58414. end
  58415. end
  58416. require 'shellwords'
  58417. module ActiveRecord
  58418. module Tasks # :nodoc:
  58419. class PostgreSQLDatabaseTasks # :nodoc:
  58420. DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8'
  58421. delegate :connection, :establish_connection, :clear_active_connections!,
  58422. to: ActiveRecord::Base
  58423. def initialize(configuration)
  58424. @configuration = configuration
  58425. end
  58426. def create(master_established = false)
  58427. establish_master_connection unless master_established
  58428. connection.create_database configuration['database'],
  58429. configuration.merge('encoding' => encoding)
  58430. establish_connection configuration
  58431. rescue ActiveRecord::StatementInvalid => error
  58432. if /database .* already exists/ === error.message
  58433. raise DatabaseAlreadyExists
  58434. else
  58435. raise
  58436. end
  58437. end
  58438. def drop
  58439. establish_master_connection
  58440. connection.drop_database configuration['database']
  58441. end
  58442. def charset
  58443. connection.encoding
  58444. end
  58445. def collation
  58446. connection.collation
  58447. end
  58448. def purge
  58449. clear_active_connections!
  58450. drop
  58451. create true
  58452. end
  58453. def structure_dump(filename)
  58454. set_psql_env
  58455. search_path = configuration['schema_search_path']
  58456. unless search_path.blank?
  58457. search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
  58458. end
  58459. command = "pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(configuration['database'])}"
  58460. raise 'Error dumping database' unless Kernel.system(command)
  58461. File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
  58462. end
  58463. def structure_load(filename)
  58464. set_psql_env
  58465. Kernel.system("psql -f #{filename} #{configuration['database']}")
  58466. end
  58467. private
  58468. def configuration
  58469. @configuration
  58470. end
  58471. def encoding
  58472. configuration['encoding'] || DEFAULT_ENCODING
  58473. end
  58474. def establish_master_connection
  58475. establish_connection configuration.merge(
  58476. 'database' => 'postgres',
  58477. 'schema_search_path' => 'public'
  58478. )
  58479. end
  58480. def set_psql_env
  58481. ENV['PGHOST'] = configuration['host'] if configuration['host']
  58482. ENV['PGPORT'] = configuration['port'].to_s if configuration['port']
  58483. ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password']
  58484. ENV['PGUSER'] = configuration['username'].to_s if configuration['username']
  58485. end
  58486. end
  58487. end
  58488. end
  58489. module ActiveRecord
  58490. module Tasks # :nodoc:
  58491. class SQLiteDatabaseTasks # :nodoc:
  58492. delegate :connection, :establish_connection, to: ActiveRecord::Base
  58493. def initialize(configuration, root = Rails.root)
  58494. @configuration, @root = configuration, root
  58495. end
  58496. def create
  58497. raise DatabaseAlreadyExists if File.exist?(configuration['database'])
  58498. establish_connection configuration
  58499. connection
  58500. end
  58501. def drop
  58502. require 'pathname'
  58503. path = Pathname.new configuration['database']
  58504. file = path.absolute? ? path.to_s : File.join(root, path)
  58505. FileUtils.rm(file) if File.exist?(file)
  58506. end
  58507. alias :purge :drop
  58508. def charset
  58509. connection.encoding
  58510. end
  58511. def structure_dump(filename)
  58512. dbfile = configuration['database']
  58513. `sqlite3 #{dbfile} .schema > #{filename}`
  58514. end
  58515. def structure_load(filename)
  58516. dbfile = configuration['database']
  58517. `sqlite3 #{dbfile} < "#{filename}"`
  58518. end
  58519. private
  58520. def configuration
  58521. @configuration
  58522. end
  58523. def root
  58524. @root
  58525. end
  58526. end
  58527. end
  58528. end
  58529. require 'active_support/test_case'
  58530. ActiveSupport::Deprecation.warn('ActiveRecord::TestCase is deprecated, please use ActiveSupport::TestCase')
  58531. module ActiveRecord
  58532. # = Active Record Test Case
  58533. #
  58534. # Defines some test assertions to test against SQL queries.
  58535. class TestCase < ActiveSupport::TestCase #:nodoc:
  58536. def teardown
  58537. SQLCounter.clear_log
  58538. end
  58539. def assert_date_from_db(expected, actual, message = nil)
  58540. # SybaseAdapter doesn't have a separate column type just for dates,
  58541. # so the time is in the string and incorrectly formatted
  58542. if current_adapter?(:SybaseAdapter)
  58543. assert_equal expected.to_s, actual.to_date.to_s, message
  58544. else
  58545. assert_equal expected.to_s, actual.to_s, message
  58546. end
  58547. end
  58548. def assert_sql(*patterns_to_match)
  58549. SQLCounter.clear_log
  58550. yield
  58551. SQLCounter.log_all
  58552. ensure
  58553. failed_patterns = []
  58554. patterns_to_match.each do |pattern|
  58555. failed_patterns << pattern unless SQLCounter.log_all.any?{ |sql| pattern === sql }
  58556. end
  58557. assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
  58558. end
  58559. def assert_queries(num = 1, options = {})
  58560. ignore_none = options.fetch(:ignore_none) { num == :any }
  58561. SQLCounter.clear_log
  58562. yield
  58563. ensure
  58564. the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
  58565. if num == :any
  58566. assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed."
  58567. else
  58568. mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}"
  58569. assert_equal num, the_log.size, mesg
  58570. end
  58571. end
  58572. def assert_no_queries(&block)
  58573. assert_queries(0, :ignore_none => true, &block)
  58574. end
  58575. end
  58576. class SQLCounter
  58577. class << self
  58578. attr_accessor :ignored_sql, :log, :log_all
  58579. def clear_log; self.log = []; self.log_all = []; end
  58580. end
  58581. self.clear_log
  58582. self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
  58583. # FIXME: this needs to be refactored so specific database can add their own
  58584. # ignored SQL, or better yet, use a different notification for the queries
  58585. # instead examining the SQL content.
  58586. oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
  58587. mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/]
  58588. postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
  58589. sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im]
  58590. [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
  58591. ignored_sql.concat db_ignored_sql
  58592. end
  58593. attr_reader :ignore
  58594. def initialize(ignore = Regexp.union(self.class.ignored_sql))
  58595. @ignore = ignore
  58596. end
  58597. def call(name, start, finish, message_id, values)
  58598. sql = values[:sql]
  58599. # FIXME: this seems bad. we should probably have a better way to indicate
  58600. # the query was cached
  58601. return if 'CACHE' == values[:name]
  58602. self.class.log_all << sql
  58603. self.class.log << sql unless ignore =~ sql
  58604. end
  58605. end
  58606. ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
  58607. end
  58608. module ActiveRecord
  58609. # = Active Record Timestamp
  58610. #
  58611. # Active Record automatically timestamps create and update operations if the
  58612. # table has fields named <tt>created_at/created_on</tt> or
  58613. # <tt>updated_at/updated_on</tt>.
  58614. #
  58615. # Timestamping can be turned off by setting:
  58616. #
  58617. # config.active_record.record_timestamps = false
  58618. #
  58619. # Timestamps are in the local timezone by default but you can use UTC by setting:
  58620. #
  58621. # config.active_record.default_timezone = :utc
  58622. #
  58623. # == Time Zone aware attributes
  58624. #
  58625. # By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code.
  58626. #
  58627. # config.active_record.time_zone_aware_attributes = true
  58628. #
  58629. # This feature can easily be turned off by assigning value <tt>false</tt> .
  58630. #
  58631. # If your attributes are time zone aware and you desire to skip time zone conversion to the current Time.zone
  58632. # when reading certain attributes then you can do following:
  58633. #
  58634. # class Topic < ActiveRecord::Base
  58635. # self.skip_time_zone_conversion_for_attributes = [:written_on]
  58636. # end
  58637. module Timestamp
  58638. extend ActiveSupport::Concern
  58639. included do
  58640. class_attribute :record_timestamps
  58641. self.record_timestamps = true
  58642. end
  58643. def initialize_dup(other) # :nodoc:
  58644. clear_timestamp_attributes
  58645. super
  58646. end
  58647. private
  58648. def create_record
  58649. if self.record_timestamps
  58650. current_time = current_time_from_proper_timezone
  58651. all_timestamp_attributes.each do |column|
  58652. if respond_to?(column) && respond_to?("#{column}=") && self.send(column).nil?
  58653. write_attribute(column.to_s, current_time)
  58654. end
  58655. end
  58656. end
  58657. super
  58658. end
  58659. def update_record(*args)
  58660. if should_record_timestamps?
  58661. current_time = current_time_from_proper_timezone
  58662. timestamp_attributes_for_update_in_model.each do |column|
  58663. column = column.to_s
  58664. next if attribute_changed?(column)
  58665. write_attribute(column, current_time)
  58666. end
  58667. end
  58668. super
  58669. end
  58670. def should_record_timestamps?
  58671. self.record_timestamps && (!partial_writes? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
  58672. end
  58673. def timestamp_attributes_for_create_in_model
  58674. timestamp_attributes_for_create.select { |c| self.class.column_names.include?(c.to_s) }
  58675. end
  58676. def timestamp_attributes_for_update_in_model
  58677. timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c.to_s) }
  58678. end
  58679. def all_timestamp_attributes_in_model
  58680. timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
  58681. end
  58682. def timestamp_attributes_for_update
  58683. [:updated_at, :updated_on]
  58684. end
  58685. def timestamp_attributes_for_create
  58686. [:created_at, :created_on]
  58687. end
  58688. def all_timestamp_attributes
  58689. timestamp_attributes_for_create + timestamp_attributes_for_update
  58690. end
  58691. def current_time_from_proper_timezone
  58692. self.class.default_timezone == :utc ? Time.now.utc : Time.now
  58693. end
  58694. # Clear attributes and changed_attributes
  58695. def clear_timestamp_attributes
  58696. all_timestamp_attributes_in_model.each do |attribute_name|
  58697. self[attribute_name] = nil
  58698. changed_attributes.delete(attribute_name)
  58699. end
  58700. end
  58701. end
  58702. end
  58703. require 'thread'
  58704. module ActiveRecord
  58705. # See ActiveRecord::Transactions::ClassMethods for documentation.
  58706. module Transactions
  58707. extend ActiveSupport::Concern
  58708. ACTIONS = [:create, :destroy, :update]
  58709. class TransactionError < ActiveRecordError # :nodoc:
  58710. end
  58711. included do
  58712. define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name]
  58713. end
  58714. # = Active Record Transactions
  58715. #
  58716. # Transactions are protective blocks where SQL statements are only permanent
  58717. # if they can all succeed as one atomic action. The classic example is a
  58718. # transfer between two accounts where you can only have a deposit if the
  58719. # withdrawal succeeded and vice versa. Transactions enforce the integrity of
  58720. # the database and guard the data against program errors or database
  58721. # break-downs. So basically you should use transaction blocks whenever you
  58722. # have a number of statements that must be executed together or not at all.
  58723. #
  58724. # For example:
  58725. #
  58726. # ActiveRecord::Base.transaction do
  58727. # david.withdrawal(100)
  58728. # mary.deposit(100)
  58729. # end
  58730. #
  58731. # This example will only take money from David and give it to Mary if neither
  58732. # +withdrawal+ nor +deposit+ raise an exception. Exceptions will force a
  58733. # ROLLBACK that returns the database to the state before the transaction
  58734. # began. Be aware, though, that the objects will _not_ have their instance
  58735. # data returned to their pre-transactional state.
  58736. #
  58737. # == Different Active Record classes in a single transaction
  58738. #
  58739. # Though the transaction class method is called on some Active Record class,
  58740. # the objects within the transaction block need not all be instances of
  58741. # that class. This is because transactions are per-database connection, not
  58742. # per-model.
  58743. #
  58744. # In this example a +balance+ record is transactionally saved even
  58745. # though +transaction+ is called on the +Account+ class:
  58746. #
  58747. # Account.transaction do
  58748. # balance.save!
  58749. # account.save!
  58750. # end
  58751. #
  58752. # The +transaction+ method is also available as a model instance method.
  58753. # For example, you can also do this:
  58754. #
  58755. # balance.transaction do
  58756. # balance.save!
  58757. # account.save!
  58758. # end
  58759. #
  58760. # == Transactions are not distributed across database connections
  58761. #
  58762. # A transaction acts on a single database connection. If you have
  58763. # multiple class-specific databases, the transaction will not protect
  58764. # interaction among them. One workaround is to begin a transaction
  58765. # on each class whose models you alter:
  58766. #
  58767. # Student.transaction do
  58768. # Course.transaction do
  58769. # course.enroll(student)
  58770. # student.units += course.units
  58771. # end
  58772. # end
  58773. #
  58774. # This is a poor solution, but fully distributed transactions are beyond
  58775. # the scope of Active Record.
  58776. #
  58777. # == +save+ and +destroy+ are automatically wrapped in a transaction
  58778. #
  58779. # Both +save+ and +destroy+ come wrapped in a transaction that ensures
  58780. # that whatever you do in validations or callbacks will happen under its
  58781. # protected cover. So you can use validations to check for values that
  58782. # the transaction depends on or you can raise exceptions in the callbacks
  58783. # to rollback, including <tt>after_*</tt> callbacks.
  58784. #
  58785. # As a consequence changes to the database are not seen outside your connection
  58786. # until the operation is complete. For example, if you try to update the index
  58787. # of a search engine in +after_save+ the indexer won't see the updated record.
  58788. # The +after_commit+ callback is the only one that is triggered once the update
  58789. # is committed. See below.
  58790. #
  58791. # == Exception handling and rolling back
  58792. #
  58793. # Also have in mind that exceptions thrown within a transaction block will
  58794. # be propagated (after triggering the ROLLBACK), so you should be ready to
  58795. # catch those in your application code.
  58796. #
  58797. # One exception is the <tt>ActiveRecord::Rollback</tt> exception, which will trigger
  58798. # a ROLLBACK when raised, but not be re-raised by the transaction block.
  58799. #
  58800. # *Warning*: one should not catch <tt>ActiveRecord::StatementInvalid</tt> exceptions
  58801. # inside a transaction block. <tt>ActiveRecord::StatementInvalid</tt> exceptions indicate that an
  58802. # error occurred at the database level, for example when a unique constraint
  58803. # is violated. On some database systems, such as PostgreSQL, database errors
  58804. # inside a transaction cause the entire transaction to become unusable
  58805. # until it's restarted from the beginning. Here is an example which
  58806. # demonstrates the problem:
  58807. #
  58808. # # Suppose that we have a Number model with a unique column called 'i'.
  58809. # Number.transaction do
  58810. # Number.create(i: 0)
  58811. # begin
  58812. # # This will raise a unique constraint error...
  58813. # Number.create(i: 0)
  58814. # rescue ActiveRecord::StatementInvalid
  58815. # # ...which we ignore.
  58816. # end
  58817. #
  58818. # # On PostgreSQL, the transaction is now unusable. The following
  58819. # # statement will cause a PostgreSQL error, even though the unique
  58820. # # constraint is no longer violated:
  58821. # Number.create(i: 1)
  58822. # # => "PGError: ERROR: current transaction is aborted, commands
  58823. # # ignored until end of transaction block"
  58824. # end
  58825. #
  58826. # One should restart the entire transaction if an
  58827. # <tt>ActiveRecord::StatementInvalid</tt> occurred.
  58828. #
  58829. # == Nested transactions
  58830. #
  58831. # +transaction+ calls can be nested. By default, this makes all database
  58832. # statements in the nested transaction block become part of the parent
  58833. # transaction. For example, the following behavior may be surprising:
  58834. #
  58835. # User.transaction do
  58836. # User.create(username: 'Kotori')
  58837. # User.transaction do
  58838. # User.create(username: 'Nemu')
  58839. # raise ActiveRecord::Rollback
  58840. # end
  58841. # end
  58842. #
  58843. # creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
  58844. # exception in the nested block does not issue a ROLLBACK. Since these exceptions
  58845. # are captured in transaction blocks, the parent block does not see it and the
  58846. # real transaction is committed.
  58847. #
  58848. # In order to get a ROLLBACK for the nested transaction you may ask for a real
  58849. # sub-transaction by passing <tt>requires_new: true</tt>. If anything goes wrong,
  58850. # the database rolls back to the beginning of the sub-transaction without rolling
  58851. # back the parent transaction. If we add it to the previous example:
  58852. #
  58853. # User.transaction do
  58854. # User.create(username: 'Kotori')
  58855. # User.transaction(requires_new: true) do
  58856. # User.create(username: 'Nemu')
  58857. # raise ActiveRecord::Rollback
  58858. # end
  58859. # end
  58860. #
  58861. # only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.)
  58862. #
  58863. # Most databases don't support true nested transactions. At the time of
  58864. # writing, the only database that we're aware of that supports true nested
  58865. # transactions, is MS-SQL. Because of this, Active Record emulates nested
  58866. # transactions by using savepoints on MySQL and PostgreSQL. See
  58867. # http://dev.mysql.com/doc/refman/5.6/en/savepoint.html
  58868. # for more information about savepoints.
  58869. #
  58870. # === Callbacks
  58871. #
  58872. # There are two types of callbacks associated with committing and rolling back transactions:
  58873. # +after_commit+ and +after_rollback+.
  58874. #
  58875. # +after_commit+ callbacks are called on every record saved or destroyed within a
  58876. # transaction immediately after the transaction is committed. +after_rollback+ callbacks
  58877. # are called on every record saved or destroyed within a transaction immediately after the
  58878. # transaction or savepoint is rolled back.
  58879. #
  58880. # These callbacks are useful for interacting with other systems since you will be guaranteed
  58881. # that the callback is only executed when the database is in a permanent state. For example,
  58882. # +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from
  58883. # within a transaction could trigger the cache to be regenerated before the database is updated.
  58884. #
  58885. # === Caveats
  58886. #
  58887. # If you're on MySQL, then do not use DDL operations in nested transactions
  58888. # blocks that are emulated with savepoints. That is, do not execute statements
  58889. # like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
  58890. # releases all savepoints upon executing a DDL operation. When +transaction+
  58891. # is finished and tries to release the savepoint it created earlier, a
  58892. # database error will occur because the savepoint has already been
  58893. # automatically released. The following example demonstrates the problem:
  58894. #
  58895. # Model.connection.transaction do # BEGIN
  58896. # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
  58897. # Model.connection.create_table(...) # active_record_1 now automatically released
  58898. # end # RELEASE savepoint active_record_1
  58899. # # ^^^^ BOOM! database error!
  58900. # end
  58901. #
  58902. # Note that "TRUNCATE" is also a MySQL DDL statement!
  58903. module ClassMethods
  58904. # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
  58905. def transaction(options = {}, &block)
  58906. # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
  58907. connection.transaction(options, &block)
  58908. end
  58909. # This callback is called after a record has been created, updated, or destroyed.
  58910. #
  58911. # You can specify that the callback should only be fired by a certain action with
  58912. # the +:on+ option:
  58913. #
  58914. # after_commit :do_foo, on: :create
  58915. # after_commit :do_bar, on: :update
  58916. # after_commit :do_baz, on: :destroy
  58917. #
  58918. # Also, to have the callback fired on create and update, but not on destroy:
  58919. #
  58920. # after_commit :do_zoo, if: :persisted?
  58921. #
  58922. # Note that transactional fixtures do not play well with this feature. Please
  58923. # use the +test_after_commit+ gem to have these hooks fired in tests.
  58924. def after_commit(*args, &block)
  58925. set_options_for_callbacks!(args)
  58926. set_callback(:commit, :after, *args, &block)
  58927. end
  58928. # This callback is called after a create, update, or destroy are rolled back.
  58929. #
  58930. # Please check the documentation of +after_commit+ for options.
  58931. def after_rollback(*args, &block)
  58932. set_options_for_callbacks!(args)
  58933. set_callback(:rollback, :after, *args, &block)
  58934. end
  58935. private
  58936. def set_options_for_callbacks!(args)
  58937. options = args.last
  58938. if options.is_a?(Hash) && options[:on]
  58939. assert_valid_transaction_action(options[:on])
  58940. options[:if] = Array(options[:if])
  58941. options[:if] << "transaction_include_action?(:#{options[:on]})"
  58942. end
  58943. end
  58944. def assert_valid_transaction_action(action)
  58945. unless ACTIONS.include?(action.to_sym)
  58946. raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS.join(",")}"
  58947. end
  58948. end
  58949. end
  58950. # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
  58951. def transaction(options = {}, &block)
  58952. self.class.transaction(options, &block)
  58953. end
  58954. def destroy #:nodoc:
  58955. with_transaction_returning_status { super }
  58956. end
  58957. def save(*) #:nodoc:
  58958. rollback_active_record_state! do
  58959. with_transaction_returning_status { super }
  58960. end
  58961. end
  58962. def save!(*) #:nodoc:
  58963. with_transaction_returning_status { super }
  58964. end
  58965. # Reset id and @new_record if the transaction rolls back.
  58966. def rollback_active_record_state!
  58967. remember_transaction_record_state
  58968. yield
  58969. rescue Exception
  58970. restore_transaction_record_state
  58971. raise
  58972. ensure
  58973. clear_transaction_record_state
  58974. end
  58975. # Call the after_commit callbacks
  58976. #
  58977. # Ensure that it is not called if the object was never persisted (failed create),
  58978. # but call it after the commit of a destroyed object
  58979. def committed! #:nodoc:
  58980. run_callbacks :commit if destroyed? || persisted?
  58981. ensure
  58982. clear_transaction_record_state
  58983. end
  58984. # Call the after rollback callbacks. The restore_state argument indicates if the record
  58985. # state should be rolled back to the beginning or just to the last savepoint.
  58986. def rolledback!(force_restore_state = false) #:nodoc:
  58987. run_callbacks :rollback
  58988. ensure
  58989. restore_transaction_record_state(force_restore_state)
  58990. end
  58991. # Add the record to the current transaction so that the :after_rollback and :after_commit callbacks
  58992. # can be called.
  58993. def add_to_transaction
  58994. if self.class.connection.add_transaction_record(self)
  58995. remember_transaction_record_state
  58996. end
  58997. end
  58998. # Executes +method+ within a transaction and captures its return value as a
  58999. # status flag. If the status is true the transaction is committed, otherwise
  59000. # a ROLLBACK is issued. In any case the status flag is returned.
  59001. #
  59002. # This method is available within the context of an ActiveRecord::Base
  59003. # instance.
  59004. def with_transaction_returning_status
  59005. status = nil
  59006. self.class.transaction do
  59007. add_to_transaction
  59008. begin
  59009. status = yield
  59010. rescue ActiveRecord::Rollback
  59011. @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
  59012. status = nil
  59013. end
  59014. raise ActiveRecord::Rollback unless status
  59015. end
  59016. status
  59017. end
  59018. protected
  59019. # Save the new record state and id of a record so it can be restored later if a transaction fails.
  59020. def remember_transaction_record_state #:nodoc:
  59021. @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
  59022. @_start_transaction_state[:new_record] = @new_record
  59023. @_start_transaction_state[:destroyed] = @destroyed
  59024. @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
  59025. @_start_transaction_state[:frozen?] = @attributes.frozen?
  59026. end
  59027. # Clear the new record state and id of a record.
  59028. def clear_transaction_record_state #:nodoc:
  59029. @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
  59030. @_start_transaction_state.clear if @_start_transaction_state[:level] < 1
  59031. end
  59032. # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
  59033. def restore_transaction_record_state(force = false) #:nodoc:
  59034. unless @_start_transaction_state.empty?
  59035. @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
  59036. if @_start_transaction_state[:level] < 1 || force
  59037. restore_state = @_start_transaction_state
  59038. was_frozen = restore_state[:frozen?]
  59039. @attributes = @attributes.dup if @attributes.frozen?
  59040. @new_record = restore_state[:new_record]
  59041. @destroyed = restore_state[:destroyed]
  59042. if restore_state.has_key?(:id)
  59043. self.id = restore_state[:id]
  59044. else
  59045. @attributes.delete(self.class.primary_key)
  59046. @attributes_cache.delete(self.class.primary_key)
  59047. end
  59048. @attributes.freeze if was_frozen
  59049. @_start_transaction_state.clear
  59050. end
  59051. end
  59052. end
  59053. # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
  59054. def transaction_record_state(state) #:nodoc:
  59055. @_start_transaction_state[state]
  59056. end
  59057. # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
  59058. def transaction_include_action?(action) #:nodoc:
  59059. case action
  59060. when :create
  59061. transaction_record_state(:new_record)
  59062. when :destroy
  59063. destroyed?
  59064. when :update
  59065. !(transaction_record_state(:new_record) || destroyed?)
  59066. end
  59067. end
  59068. end
  59069. end
  59070. module ActiveRecord
  59071. module Translation
  59072. include ActiveModel::Translation
  59073. # Set the lookup ancestors for ActiveModel.
  59074. def lookup_ancestors #:nodoc:
  59075. klass = self
  59076. classes = [klass]
  59077. return classes if klass == ActiveRecord::Base
  59078. while klass != klass.base_class
  59079. classes << klass = klass.superclass
  59080. end
  59081. classes
  59082. end
  59083. # Set the i18n scope to overwrite ActiveModel.
  59084. def i18n_scope #:nodoc:
  59085. :activerecord
  59086. end
  59087. end
  59088. end
  59089. module ActiveRecord
  59090. module Validations
  59091. class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
  59092. def validate_each(record, attribute, value)
  59093. if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?(record.validation_context) }.any?
  59094. record.errors.add(attribute, :invalid, options.merge(:value => value))
  59095. end
  59096. end
  59097. end
  59098. module ClassMethods
  59099. # Validates whether the associated object or objects are all valid
  59100. # themselves. Works with any kind of association.
  59101. #
  59102. # class Book < ActiveRecord::Base
  59103. # has_many :pages
  59104. # belongs_to :library
  59105. #
  59106. # validates_associated :pages, :library
  59107. # end
  59108. #
  59109. # WARNING: This validation must not be used on both ends of an association.
  59110. # Doing so will lead to a circular dependency and cause infinite recursion.
  59111. #
  59112. # NOTE: This validation will not fail if the association hasn't been
  59113. # assigned. If you want to ensure that the association is both present and
  59114. # guaranteed to be valid, you also need to use +validates_presence_of+.
  59115. #
  59116. # Configuration options:
  59117. #
  59118. # * <tt>:message</tt> - A custom error message (default is: "is invalid").
  59119. # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
  59120. # validation contexts by default (+nil+), other options are <tt>:create</tt>
  59121. # and <tt>:update</tt>.
  59122. # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
  59123. # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
  59124. # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
  59125. # proc or string should return or evaluate to a +true+ or +false+ value.
  59126. # * <tt>:unless</tt> - Specifies a method, proc or string to call to
  59127. # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
  59128. # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
  59129. # method, proc or string should return or evaluate to a +true+ or +false+
  59130. # value.
  59131. def validates_associated(*attr_names)
  59132. validates_with AssociatedValidator, _merge_attributes(attr_names)
  59133. end
  59134. end
  59135. end
  59136. end
  59137. module ActiveRecord
  59138. module Validations
  59139. class PresenceValidator < ActiveModel::Validations::PresenceValidator # :nodoc:
  59140. def validate(record)
  59141. super
  59142. attributes.each do |attribute|
  59143. next unless record.class.reflect_on_association(attribute)
  59144. associated_records = Array(record.send(attribute))
  59145. # Superclass validates presence. Ensure present records aren't about to be destroyed.
  59146. if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? }
  59147. record.errors.add(attribute, :blank, options)
  59148. end
  59149. end
  59150. end
  59151. end
  59152. module ClassMethods
  59153. # Validates that the specified attributes are not blank (as defined by
  59154. # Object#blank?), and, if the attribute is an association, that the
  59155. # associated object is not marked for destruction. Happens by default
  59156. # on save.
  59157. #
  59158. # class Person < ActiveRecord::Base
  59159. # has_one :face
  59160. # validates_presence_of :face
  59161. # end
  59162. #
  59163. # The face attribute must be in the object and it cannot be blank or marked
  59164. # for destruction.
  59165. #
  59166. # If you want to validate the presence of a boolean field (where the real values
  59167. # are true and false), you will want to use
  59168. # <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
  59169. #
  59170. # This is due to the way Object#blank? handles boolean values:
  59171. # <tt>false.blank? # => true</tt>.
  59172. #
  59173. # This validator defers to the ActiveModel validation for presence, adding the
  59174. # check to see that an associated object is not marked for destruction. This
  59175. # prevents the parent object from validating successfully and saving, which then
  59176. # deletes the associated object, thus putting the parent object into an invalid
  59177. # state.
  59178. #
  59179. # Configuration options:
  59180. # * <tt>:message</tt> - A custom error message (default is: "can't be blank").
  59181. # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
  59182. # validation contexts by default (+nil+), other options are <tt>:create</tt>
  59183. # and <tt>:update</tt>.
  59184. # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
  59185. # the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
  59186. # <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
  59187. # or string should return or evaluate to a +true+ or +false+ value.
  59188. # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
  59189. # if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
  59190. # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
  59191. # proc or string should return or evaluate to a +true+ or +false+ value.
  59192. # * <tt>:strict</tt> - Specifies whether validation should be strict.
  59193. # See <tt>ActiveModel::Validation#validates!</tt> for more information.
  59194. def validates_presence_of(*attr_names)
  59195. validates_with PresenceValidator, _merge_attributes(attr_names)
  59196. end
  59197. end
  59198. end
  59199. end
  59200. module ActiveRecord
  59201. module Validations
  59202. class UniquenessValidator < ActiveModel::EachValidator # :nodoc:
  59203. def initialize(options)
  59204. super({ case_sensitive: true }.merge!(options))
  59205. end
  59206. # Unfortunately, we have to tie Uniqueness validators to a class.
  59207. def setup(klass)
  59208. @klass = klass
  59209. end
  59210. def validate_each(record, attribute, value)
  59211. finder_class = find_finder_class_for(record)
  59212. table = finder_class.arel_table
  59213. value = deserialize_attribute(record, attribute, value)
  59214. relation = build_relation(finder_class, table, attribute, value)
  59215. relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
  59216. relation = scope_relation(record, table, relation)
  59217. relation = finder_class.unscoped.where(relation)
  59218. relation.merge!(options[:conditions]) if options[:conditions]
  59219. if relation.exists?
  59220. error_options = options.except(:case_sensitive, :scope, :conditions)
  59221. error_options[:value] = value
  59222. record.errors.add(attribute, :taken, error_options)
  59223. end
  59224. end
  59225. protected
  59226. # The check for an existing value should be run from a class that
  59227. # isn't abstract. This means working down from the current class
  59228. # (self), to the first non-abstract class. Since classes don't know
  59229. # their subclasses, we have to build the hierarchy between self and
  59230. # the record's class.
  59231. def find_finder_class_for(record) #:nodoc:
  59232. class_hierarchy = [record.class]
  59233. while class_hierarchy.first != @klass
  59234. class_hierarchy.unshift(class_hierarchy.first.superclass)
  59235. end
  59236. class_hierarchy.detect { |klass| !klass.abstract_class? }
  59237. end
  59238. def build_relation(klass, table, attribute, value) #:nodoc:
  59239. if reflection = klass.reflect_on_association(attribute)
  59240. attribute = reflection.foreign_key
  59241. value = value.attributes[reflection.primary_key_column.name]
  59242. end
  59243. column = klass.columns_hash[attribute.to_s]
  59244. value = klass.connection.type_cast(value, column)
  59245. value = value.to_s[0, column.limit] if value && column.limit && column.text?
  59246. if !options[:case_sensitive] && value && column.text?
  59247. # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
  59248. klass.connection.case_insensitive_comparison(table, attribute, column, value)
  59249. else
  59250. value = klass.connection.case_sensitive_modifier(value) unless value.nil?
  59251. table[attribute].eq(value)
  59252. end
  59253. end
  59254. def scope_relation(record, table, relation)
  59255. Array(options[:scope]).each do |scope_item|
  59256. if reflection = record.class.reflect_on_association(scope_item)
  59257. scope_value = record.send(reflection.foreign_key)
  59258. scope_item = reflection.foreign_key
  59259. else
  59260. scope_value = record.read_attribute(scope_item)
  59261. end
  59262. relation = relation.and(table[scope_item].eq(scope_value))
  59263. end
  59264. relation
  59265. end
  59266. def deserialize_attribute(record, attribute, value)
  59267. coder = record.class.serialized_attributes[attribute.to_s]
  59268. value = coder.dump value if value && coder
  59269. value
  59270. end
  59271. end
  59272. module ClassMethods
  59273. # Validates whether the value of the specified attributes are unique
  59274. # across the system. Useful for making sure that only one user
  59275. # can be named "davidhh".
  59276. #
  59277. # class Person < ActiveRecord::Base
  59278. # validates_uniqueness_of :user_name
  59279. # end
  59280. #
  59281. # It can also validate whether the value of the specified attributes are
  59282. # unique based on a <tt>:scope</tt> parameter:
  59283. #
  59284. # class Person < ActiveRecord::Base
  59285. # validates_uniqueness_of :user_name, scope: :account_id
  59286. # end
  59287. #
  59288. # Or even multiple scope parameters. For example, making sure that a
  59289. # teacher can only be on the schedule once per semester for a particular
  59290. # class.
  59291. #
  59292. # class TeacherSchedule < ActiveRecord::Base
  59293. # validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
  59294. # end
  59295. #
  59296. # It is also possible to limit the uniqueness constraint to a set of
  59297. # records matching certain conditions. In this example archived articles
  59298. # are not being taken into consideration when validating uniqueness
  59299. # of the title attribute:
  59300. #
  59301. # class Article < ActiveRecord::Base
  59302. # validates_uniqueness_of :title, conditions: where('status != ?', 'archived')
  59303. # end
  59304. #
  59305. # When the record is created, a check is performed to make sure that no
  59306. # record exists in the database with the given value for the specified
  59307. # attribute (that maps to a column). When the record is updated,
  59308. # the same check is made but disregarding the record itself.
  59309. #
  59310. # Configuration options:
  59311. #
  59312. # * <tt>:message</tt> - Specifies a custom error message (default is:
  59313. # "has already been taken").
  59314. # * <tt>:scope</tt> - One or more columns by which to limit the scope of
  59315. # the uniqueness constraint.
  59316. # * <tt>:conditions</tt> - Specify the conditions to be included as a
  59317. # <tt>WHERE</tt> SQL fragment to limit the uniqueness constraint lookup
  59318. # (e.g. <tt>conditions: where('status = ?', 'active')</tt>).
  59319. # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by
  59320. # non-text columns (+true+ by default).
  59321. # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
  59322. # attribute is +nil+ (default is +false+).
  59323. # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
  59324. # attribute is blank (default is +false+).
  59325. # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
  59326. # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
  59327. # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
  59328. # proc or string should return or evaluate to a +true+ or +false+ value.
  59329. # * <tt>:unless</tt> - Specifies a method, proc or string to call to
  59330. # determine if the validation should ot occur (e.g. <tt>unless: :skip_validation</tt>,
  59331. # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
  59332. # method, proc or string should return or evaluate to a +true+ or +false+
  59333. # value.
  59334. #
  59335. # === Concurrency and integrity
  59336. #
  59337. # Using this validation method in conjunction with ActiveRecord::Base#save
  59338. # does not guarantee the absence of duplicate record insertions, because
  59339. # uniqueness checks on the application level are inherently prone to race
  59340. # conditions. For example, suppose that two users try to post a Comment at
  59341. # the same time, and a Comment's title must be unique. At the database-level,
  59342. # the actions performed by these users could be interleaved in the following manner:
  59343. #
  59344. # User 1 | User 2
  59345. # ------------------------------------+--------------------------------------
  59346. # # User 1 checks whether there's |
  59347. # # already a comment with the title |
  59348. # # 'My Post'. This is not the case. |
  59349. # SELECT * FROM comments |
  59350. # WHERE title = 'My Post' |
  59351. # |
  59352. # | # User 2 does the same thing and also
  59353. # | # infers that his title is unique.
  59354. # | SELECT * FROM comments
  59355. # | WHERE title = 'My Post'
  59356. # |
  59357. # # User 1 inserts his comment. |
  59358. # INSERT INTO comments |
  59359. # (title, content) VALUES |
  59360. # ('My Post', 'hi!') |
  59361. # |
  59362. # | # User 2 does the same thing.
  59363. # | INSERT INTO comments
  59364. # | (title, content) VALUES
  59365. # | ('My Post', 'hello!')
  59366. # |
  59367. # | # ^^^^^^
  59368. # | # Boom! We now have a duplicate
  59369. # | # title!
  59370. #
  59371. # This could even happen if you use transactions with the 'serializable'
  59372. # isolation level. The best way to work around this problem is to add a unique
  59373. # index to the database table using
  59374. # ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
  59375. # rare case that a race condition occurs, the database will guarantee
  59376. # the field's uniqueness.
  59377. #
  59378. # When the database catches such a duplicate insertion,
  59379. # ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
  59380. # exception. You can either choose to let this error propagate (which
  59381. # will result in the default Rails exception page being shown), or you
  59382. # can catch it and restart the transaction (e.g. by telling the user
  59383. # that the title already exists, and asking him to re-enter the title).
  59384. # This technique is also known as optimistic concurrency control:
  59385. # http://en.wikipedia.org/wiki/Optimistic_concurrency_control.
  59386. #
  59387. # The bundled ActiveRecord::ConnectionAdapters distinguish unique index
  59388. # constraint errors from other types of database errors by throwing an
  59389. # ActiveRecord::RecordNotUnique exception. For other adapters you will
  59390. # have to parse the (database-specific) exception message to detect such
  59391. # a case.
  59392. #
  59393. # The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
  59394. #
  59395. # * ActiveRecord::ConnectionAdapters::MysqlAdapter.
  59396. # * ActiveRecord::ConnectionAdapters::Mysql2Adapter.
  59397. # * ActiveRecord::ConnectionAdapters::SQLite3Adapter.
  59398. # * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
  59399. def validates_uniqueness_of(*attr_names)
  59400. validates_with UniquenessValidator, _merge_attributes(attr_names)
  59401. end
  59402. end
  59403. end
  59404. end
  59405. module ActiveRecord
  59406. # = Active Record RecordInvalid
  59407. #
  59408. # Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
  59409. # +record+ method to retrieve the record which did not validate.
  59410. #
  59411. # begin
  59412. # complex_operation_that_calls_save!_internally
  59413. # rescue ActiveRecord::RecordInvalid => invalid
  59414. # puts invalid.record.errors
  59415. # end
  59416. class RecordInvalid < ActiveRecordError
  59417. attr_reader :record # :nodoc:
  59418. def initialize(record) # :nodoc:
  59419. @record = record
  59420. errors = @record.errors.full_messages.join(", ")
  59421. super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
  59422. end
  59423. end
  59424. # = Active Record Validations
  59425. #
  59426. # Active Record includes the majority of its validations from <tt>ActiveModel::Validations</tt>
  59427. # all of which accept the <tt>:on</tt> argument to define the context where the
  59428. # validations are active. Active Record will always supply either the context of
  59429. # <tt>:create</tt> or <tt>:update</tt> dependent on whether the model is a
  59430. # <tt>new_record?</tt>.
  59431. module Validations
  59432. extend ActiveSupport::Concern
  59433. include ActiveModel::Validations
  59434. module ClassMethods
  59435. # Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
  59436. # so an exception is raised if the record is invalid.
  59437. def create!(attributes = nil, &block)
  59438. if attributes.is_a?(Array)
  59439. attributes.collect { |attr| create!(attr, &block) }
  59440. else
  59441. object = new(attributes)
  59442. yield(object) if block_given?
  59443. object.save!
  59444. object
  59445. end
  59446. end
  59447. end
  59448. # The validation process on save can be skipped by passing <tt>validate: false</tt>.
  59449. # The regular Base#save method is replaced with this when the validations
  59450. # module is mixed in, which it is by default.
  59451. def save(options={})
  59452. perform_validations(options) ? super : false
  59453. end
  59454. # Attempts to save the record just like Base#save but will raise a +RecordInvalid+
  59455. # exception instead of returning +false+ if the record is not valid.
  59456. def save!(options={})
  59457. perform_validations(options) ? super : raise(RecordInvalid.new(self))
  59458. end
  59459. # Runs all the validations within the specified context. Returns +true+ if
  59460. # no errors are found, +false+ otherwise.
  59461. #
  59462. # If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
  59463. # <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
  59464. #
  59465. # Validations with no <tt>:on</tt> option will run no matter the context. Validations with
  59466. # some <tt>:on</tt> option will only run in the specified context.
  59467. def valid?(context = nil)
  59468. context ||= (new_record? ? :create : :update)
  59469. output = super(context)
  59470. errors.empty? && output
  59471. end
  59472. protected
  59473. def perform_validations(options={}) # :nodoc:
  59474. perform_validation = options[:validate] != false
  59475. perform_validation ? valid?(options[:context]) : true
  59476. end
  59477. end
  59478. end
  59479. require "active_record/validations/associated"
  59480. require "active_record/validations/uniqueness"
  59481. require "active_record/validations/presence"
  59482. module ActiveRecord
  59483. module VERSION #:nodoc:
  59484. MAJOR = 4
  59485. MINOR = 0
  59486. TINY = 0
  59487. PRE = "beta"
  59488. STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
  59489. end
  59490. end
  59491. #--
  59492. # Copyright (c) 2004-2013 David Heinemeier Hansson
  59493. #
  59494. # Permission is hereby granted, free of charge, to any person obtaining
  59495. # a copy of this software and associated documentation files (the
  59496. # "Software"), to deal in the Software without restriction, including
  59497. # without limitation the rights to use, copy, modify, merge, publish,
  59498. # distribute, sublicense, and/or sell copies of the Software, and to
  59499. # permit persons to whom the Software is furnished to do so, subject to
  59500. # the following conditions:
  59501. #
  59502. # The above copyright notice and this permission notice shall be
  59503. # included in all copies or substantial portions of the Software.
  59504. #
  59505. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  59506. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  59507. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  59508. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  59509. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  59510. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  59511. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  59512. #++
  59513. require 'active_support'
  59514. require 'active_support/rails'
  59515. require 'active_model'
  59516. require 'arel'
  59517. require 'active_record/deprecated_finders'
  59518. require 'active_record/version'
  59519. module ActiveRecord
  59520. extend ActiveSupport::Autoload
  59521. autoload :Base
  59522. autoload :Callbacks
  59523. autoload :Core
  59524. autoload :CounterCache
  59525. autoload :ConnectionHandling
  59526. autoload :DynamicMatchers
  59527. autoload :Explain
  59528. autoload :Inheritance
  59529. autoload :Integration
  59530. autoload :Migration
  59531. autoload :Migrator, 'active_record/migration'
  59532. autoload :ModelSchema
  59533. autoload :NestedAttributes
  59534. autoload :Persistence
  59535. autoload :QueryCache
  59536. autoload :Querying
  59537. autoload :ReadonlyAttributes
  59538. autoload :Reflection
  59539. autoload :Sanitization
  59540. autoload :Schema
  59541. autoload :SchemaDumper
  59542. autoload :SchemaMigration
  59543. autoload :Scoping
  59544. autoload :Serialization
  59545. autoload :Store
  59546. autoload :Timestamp
  59547. autoload :Transactions
  59548. autoload :Translation
  59549. autoload :Validations
  59550. eager_autoload do
  59551. autoload :ActiveRecordError, 'active_record/errors'
  59552. autoload :ConnectionNotEstablished, 'active_record/errors'
  59553. autoload :ConnectionAdapters, 'active_record/connection_adapters/abstract_adapter'
  59554. autoload :Aggregations
  59555. autoload :Associations
  59556. autoload :AttributeMethods
  59557. autoload :AttributeAssignment
  59558. autoload :AutosaveAssociation
  59559. autoload :Relation
  59560. autoload :NullRelation
  59561. autoload_under 'relation' do
  59562. autoload :QueryMethods
  59563. autoload :FinderMethods
  59564. autoload :Calculations
  59565. autoload :PredicateBuilder
  59566. autoload :SpawnMethods
  59567. autoload :Batches
  59568. autoload :Delegation
  59569. end
  59570. autoload :Result
  59571. end
  59572. module Coders
  59573. autoload :YAMLColumn, 'active_record/coders/yaml_column'
  59574. end
  59575. module AttributeMethods
  59576. extend ActiveSupport::Autoload
  59577. eager_autoload do
  59578. autoload :BeforeTypeCast
  59579. autoload :Dirty
  59580. autoload :PrimaryKey
  59581. autoload :Query
  59582. autoload :Read
  59583. autoload :TimeZoneConversion
  59584. autoload :Write
  59585. autoload :Serialization
  59586. end
  59587. end
  59588. module Locking
  59589. extend ActiveSupport::Autoload
  59590. eager_autoload do
  59591. autoload :Optimistic
  59592. autoload :Pessimistic
  59593. end
  59594. end
  59595. module ConnectionAdapters
  59596. extend ActiveSupport::Autoload
  59597. eager_autoload do
  59598. autoload :AbstractAdapter
  59599. autoload :ConnectionManagement, "active_record/connection_adapters/abstract/connection_pool"
  59600. end
  59601. end
  59602. module Scoping
  59603. extend ActiveSupport::Autoload
  59604. eager_autoload do
  59605. autoload :Named
  59606. autoload :Default
  59607. end
  59608. end
  59609. module Tasks
  59610. extend ActiveSupport::Autoload
  59611. autoload :DatabaseTasks
  59612. autoload :SQLiteDatabaseTasks, 'active_record/tasks/sqlite_database_tasks'
  59613. autoload :MySQLDatabaseTasks, 'active_record/tasks/mysql_database_tasks'
  59614. autoload :PostgreSQLDatabaseTasks,
  59615. 'active_record/tasks/postgresql_database_tasks'
  59616. end
  59617. autoload :TestCase
  59618. autoload :TestFixtures, 'active_record/fixtures'
  59619. def self.eager_load!
  59620. super
  59621. ActiveRecord::Locking.eager_load!
  59622. ActiveRecord::Scoping.eager_load!
  59623. ActiveRecord::Associations.eager_load!
  59624. ActiveRecord::AttributeMethods.eager_load!
  59625. ActiveRecord::ConnectionAdapters.eager_load!
  59626. end
  59627. end
  59628. ActiveSupport.on_load(:active_record) do
  59629. Arel::Table.engine = self
  59630. end
  59631. ActiveSupport.on_load(:i18n) do
  59632. I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
  59633. end
  59634. require 'rails/generators/active_record'
  59635. module ActiveRecord
  59636. module Generators # :nodoc:
  59637. class MigrationGenerator < Base # :nodoc:
  59638. argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
  59639. def create_migration_file
  59640. set_local_assigns!
  59641. validate_file_name!
  59642. migration_template "migration.rb", "db/migrate/#{file_name}.rb"
  59643. end
  59644. protected
  59645. attr_reader :migration_action, :join_tables
  59646. def set_local_assigns!
  59647. case file_name
  59648. when /^(add|remove)_.*_(?:to|from)_(.*)/
  59649. @migration_action = $1
  59650. @table_name = $2.pluralize
  59651. when /join_table/
  59652. if attributes.length == 2
  59653. @migration_action = 'join'
  59654. @join_tables = attributes.map(&:plural_name)
  59655. set_index_names
  59656. end
  59657. end
  59658. end
  59659. def set_index_names
  59660. attributes.each_with_index do |attr, i|
  59661. attr.index_name = [attr, attributes[i - 1]].map{ |a| index_name_for(a) }
  59662. end
  59663. end
  59664. def index_name_for(attribute)
  59665. if attribute.foreign_key?
  59666. attribute.name
  59667. else
  59668. attribute.name.singularize.foreign_key
  59669. end.to_sym
  59670. end
  59671. private
  59672. def validate_file_name!
  59673. unless file_name =~ /^[_a-z0-9]+$/
  59674. raise IllegalMigrationNameError.new(file_name)
  59675. end
  59676. end
  59677. end
  59678. end
  59679. end
  59680. class <%= migration_class_name %> < ActiveRecord::Migration
  59681. <%- if migration_action == 'add' -%>
  59682. def change
  59683. <% attributes.each do |attribute| -%>
  59684. <%- if attribute.reference? -%>
  59685. add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
  59686. <%- else -%>
  59687. add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
  59688. <%- if attribute.has_index? -%>
  59689. add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
  59690. <%- end -%>
  59691. <%- end -%>
  59692. <%- end -%>
  59693. end
  59694. <%- elsif migration_action == 'join' -%>
  59695. def change
  59696. create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t|
  59697. <%- attributes.each do |attribute| -%>
  59698. <%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %>
  59699. <%- end -%>
  59700. end
  59701. end
  59702. <%- else -%>
  59703. def change
  59704. <% attributes.each do |attribute| -%>
  59705. <%- if migration_action -%>
  59706. <%- if attribute.reference? -%>
  59707. remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
  59708. <%- else -%>
  59709. <%- if attribute.has_index? -%>
  59710. remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
  59711. <%- end -%>
  59712. remove_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
  59713. <%- end -%>
  59714. <%- end -%>
  59715. <%- end -%>
  59716. end
  59717. <%- end -%>
  59718. end
  59719. require 'rails/generators/active_record'
  59720. module ActiveRecord
  59721. module Generators # :nodoc:
  59722. class ModelGenerator < Base # :nodoc:
  59723. argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
  59724. check_class_collision
  59725. class_option :migration, :type => :boolean
  59726. class_option :timestamps, :type => :boolean
  59727. class_option :parent, :type => :string, :desc => "The parent class for the generated model"
  59728. class_option :indexes, :type => :boolean, :default => true, :desc => "Add indexes for references and belongs_to columns"
  59729. def create_migration_file
  59730. return unless options[:migration] && options[:parent].nil?
  59731. attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false
  59732. migration_template "migration.rb", "db/migrate/create_#{table_name}.rb"
  59733. end
  59734. def create_model_file
  59735. template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
  59736. end
  59737. def create_module_file
  59738. return if regular_class_path.empty?
  59739. template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb") if behavior == :invoke
  59740. end
  59741. def attributes_with_index
  59742. attributes.select { |a| !a.reference? && a.has_index? }
  59743. end
  59744. def accessible_attributes
  59745. attributes.reject(&:reference?)
  59746. end
  59747. hook_for :test_framework
  59748. protected
  59749. def parent_class_name
  59750. options[:parent] || "ActiveRecord::Base"
  59751. end
  59752. end
  59753. end
  59754. end
  59755. class <%= migration_class_name %> < ActiveRecord::Migration
  59756. def change
  59757. create_table :<%= table_name %> do |t|
  59758. <% attributes.each do |attribute| -%>
  59759. t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
  59760. <% end -%>
  59761. <% if options[:timestamps] %>
  59762. t.timestamps
  59763. <% end -%>
  59764. end
  59765. <% attributes_with_index.each do |attribute| -%>
  59766. add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
  59767. <% end -%>
  59768. end
  59769. end
  59770. <% module_namespacing do -%>
  59771. class <%= class_name %> < <%= parent_class_name.classify %>
  59772. <% attributes.select {|attr| attr.reference? }.each do |attribute| -%>
  59773. belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %>
  59774. <% end -%>
  59775. end
  59776. <% end -%>
  59777. <% module_namespacing do -%>
  59778. module <%= class_path.map(&:camelize).join('::') %>
  59779. def self.table_name_prefix
  59780. '<%= namespaced? ? namespaced_class_path.join('_') : class_path.join('_') %>_'
  59781. end
  59782. end
  59783. <% end -%>
  59784. require 'rails/generators/named_base'
  59785. require 'rails/generators/migration'
  59786. require 'rails/generators/active_model'
  59787. require 'active_record'
  59788. module ActiveRecord
  59789. module Generators # :nodoc:
  59790. class Base < Rails::Generators::NamedBase # :nodoc:
  59791. include Rails::Generators::Migration
  59792. # Set the current directory as base for the inherited generators.
  59793. def self.base_root
  59794. File.dirname(__FILE__)
  59795. end
  59796. # Implement the required interface for Rails::Generators::Migration.
  59797. def self.next_migration_number(dirname)
  59798. next_migration_number = current_migration_number(dirname) + 1
  59799. ActiveRecord::Migration.next_migration_number(next_migration_number)
  59800. end
  59801. end
  59802. end
  59803. end
  59804. require 'active_support'
  59805. require 'active_support/time'
  59806. require 'active_support/core_ext'
  59807. module ActiveSupport
  59808. # Backtraces often include many lines that are not relevant for the context
  59809. # under review. This makes it hard to find the signal amongst the backtrace
  59810. # noise, and adds debugging time. With a BacktraceCleaner, filters and
  59811. # silencers are used to remove the noisy lines, so that only the most relevant
  59812. # lines remain.
  59813. #
  59814. # Filters are used to modify lines of data, while silencers are used to remove
  59815. # lines entirely. The typical filter use case is to remove lengthy path
  59816. # information from the start of each line, and view file paths relevant to the
  59817. # app directory instead of the file system root. The typical silencer use case
  59818. # is to exclude the output of a noisy library from the backtrace, so that you
  59819. # can focus on the rest.
  59820. #
  59821. # bc = BacktraceCleaner.new
  59822. # bc.add_filter { |line| line.gsub(Rails.root, '') }
  59823. # bc.add_silencer { |line| line =~ /mongrel|rubygems/ }
  59824. # bc.clean(exception.backtrace) # will strip the Rails.root prefix and skip any lines from mongrel or rubygems
  59825. #
  59826. # To reconfigure an existing BacktraceCleaner (like the default one in Rails)
  59827. # and show as much data as possible, you can always call
  59828. # <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
  59829. # backtrace to a pristine state. If you need to reconfigure an existing
  59830. # BacktraceCleaner so that it does not filter or modify the paths of any lines
  59831. # of the backtrace, you can call BacktraceCleaner#remove_filters! These two
  59832. # methods will give you a completely untouched backtrace.
  59833. #
  59834. # Inspired by the Quiet Backtrace gem by Thoughtbot.
  59835. class BacktraceCleaner
  59836. def initialize
  59837. @filters, @silencers = [], []
  59838. end
  59839. # Returns the backtrace after all filters and silencers have been run
  59840. # against it. Filters run first, then silencers.
  59841. def clean(backtrace, kind = :silent)
  59842. filtered = filter_backtrace(backtrace)
  59843. case kind
  59844. when :silent
  59845. silence(filtered)
  59846. when :noise
  59847. noise(filtered)
  59848. else
  59849. filtered
  59850. end
  59851. end
  59852. alias :filter :clean
  59853. # Adds a filter from the block provided. Each line in the backtrace will be
  59854. # mapped against this filter.
  59855. #
  59856. # # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
  59857. # backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') }
  59858. def add_filter(&block)
  59859. @filters << block
  59860. end
  59861. # Adds a silencer from the block provided. If the silencer returns +true+
  59862. # for a given line, it will be excluded from the clean backtrace.
  59863. #
  59864. # # Will reject all lines that include the word "mongrel", like "/gems/mongrel/server.rb" or "/app/my_mongrel_server/rb"
  59865. # backtrace_cleaner.add_silencer { |line| line =~ /mongrel/ }
  59866. def add_silencer(&block)
  59867. @silencers << block
  59868. end
  59869. # Will remove all silencers, but leave in the filters. This is useful if
  59870. # your context of debugging suddenly expands as you suspect a bug in one of
  59871. # the libraries you use.
  59872. def remove_silencers!
  59873. @silencers = []
  59874. end
  59875. # Removes all filters, but leaves in silencers. Useful if you suddenly
  59876. # need to see entire filepaths in the backtrace that you had already
  59877. # filtered out.
  59878. def remove_filters!
  59879. @filters = []
  59880. end
  59881. private
  59882. def filter_backtrace(backtrace)
  59883. @filters.each do |f|
  59884. backtrace = backtrace.map { |line| f.call(line) }
  59885. end
  59886. backtrace
  59887. end
  59888. def silence(backtrace)
  59889. @silencers.each do |s|
  59890. backtrace = backtrace.reject { |line| s.call(line) }
  59891. end
  59892. backtrace
  59893. end
  59894. def noise(backtrace)
  59895. @silencers.each do |s|
  59896. backtrace = backtrace.select { |line| s.call(line) }
  59897. end
  59898. backtrace
  59899. end
  59900. end
  59901. end
  59902. require 'active_support/deprecation'
  59903. require 'active_support/proxy_object'
  59904. module ActiveSupport
  59905. class BasicObject < ProxyObject # :nodoc:
  59906. def self.inherited(*)
  59907. ::ActiveSupport::Deprecation.warn 'ActiveSupport::BasicObject is deprecated! Use ActiveSupport::ProxyObject instead.'
  59908. super
  59909. end
  59910. end
  59911. end
  59912. require 'active_support/core_ext/benchmark'
  59913. require 'active_support/core_ext/hash/keys'
  59914. module ActiveSupport
  59915. module Benchmarkable
  59916. # Allows you to measure the execution time of a block in a template and
  59917. # records the result to the log. Wrap this block around expensive operations
  59918. # or possible bottlenecks to get a time reading for the operation. For
  59919. # example, let's say you thought your file processing method was taking too
  59920. # long; you could wrap it in a benchmark block.
  59921. #
  59922. # <% benchmark 'Process data files' do %>
  59923. # <%= expensive_files_operation %>
  59924. # <% end %>
  59925. #
  59926. # That would add something like "Process data files (345.2ms)" to the log,
  59927. # which you can then use to compare timings when optimizing your code.
  59928. #
  59929. # You may give an optional logger level (<tt>:debug</tt>, <tt>:info</tt>,
  59930. # <tt>:warn</tt>, <tt>:error</tt>) as the <tt>:level</tt> option. The
  59931. # default logger level value is <tt>:info</tt>.
  59932. #
  59933. # <% benchmark 'Low-level files', level: :debug do %>
  59934. # <%= lowlevel_files_operation %>
  59935. # <% end %>
  59936. #
  59937. # Finally, you can pass true as the third argument to silence all log
  59938. # activity (other than the timing information) from inside the block. This
  59939. # is great for boiling down a noisy block to just a single statement that
  59940. # produces one log line:
  59941. #
  59942. # <% benchmark 'Process data files', level: :info, silence: true do %>
  59943. # <%= expensive_and_chatty_files_operation %>
  59944. # <% end %>
  59945. def benchmark(message = "Benchmarking", options = {})
  59946. if logger
  59947. options.assert_valid_keys(:level, :silence)
  59948. options[:level] ||= :info
  59949. result = nil
  59950. ms = Benchmark.ms { result = options[:silence] ? silence { yield } : yield }
  59951. logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
  59952. result
  59953. else
  59954. yield
  59955. end
  59956. end
  59957. # Silence the logger during the execution of the block.
  59958. def silence
  59959. message = "ActiveSupport::Benchmarkable#silence is deprecated. It will be removed from Rails 4.1."
  59960. ActiveSupport::Deprecation.warn message
  59961. old_logger_level, logger.level = logger.level, ::Logger::ERROR if logger
  59962. yield
  59963. ensure
  59964. logger.level = old_logger_level if logger
  59965. end
  59966. end
  59967. end
  59968. require 'active_support/deprecation'
  59969. require 'active_support/logger'
  59970. module ActiveSupport
  59971. class BufferedLogger < Logger
  59972. def initialize(*args)
  59973. self.class._deprecation_warning
  59974. super
  59975. end
  59976. def self.inherited(*)
  59977. _deprecation_warning
  59978. super
  59979. end
  59980. def self._deprecation_warning
  59981. ::ActiveSupport::Deprecation.warn 'ActiveSupport::BufferedLogger is deprecated! Use ActiveSupport::Logger instead.'
  59982. end
  59983. end
  59984. end
  59985. begin
  59986. require 'builder'
  59987. rescue LoadError => e
  59988. $stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install"
  59989. raise e
  59990. end
  59991. require 'active_support/core_ext/marshal'
  59992. require 'active_support/core_ext/file/atomic'
  59993. require 'active_support/core_ext/string/conversions'
  59994. require 'uri/common'
  59995. module ActiveSupport
  59996. module Cache
  59997. # A cache store implementation which stores everything on the filesystem.
  59998. #
  59999. # FileStore implements the Strategy::LocalCache strategy which implements
  60000. # an in-memory cache inside of a block.
  60001. class FileStore < Store
  60002. attr_reader :cache_path
  60003. DIR_FORMATTER = "%03X"
  60004. FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
  60005. EXCLUDED_DIRS = ['.', '..'].freeze
  60006. def initialize(cache_path, options = nil)
  60007. super(options)
  60008. @cache_path = cache_path.to_s
  60009. extend Strategy::LocalCache
  60010. end
  60011. def clear(options = nil)
  60012. root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
  60013. FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
  60014. end
  60015. def cleanup(options = nil)
  60016. options = merged_options(options)
  60017. each_key(options) do |key|
  60018. entry = read_entry(key, options)
  60019. delete_entry(key, options) if entry && entry.expired?
  60020. end
  60021. end
  60022. def increment(name, amount = 1, options = nil)
  60023. file_name = key_file_path(namespaced_key(name, options))
  60024. lock_file(file_name) do
  60025. options = merged_options(options)
  60026. if num = read(name, options)
  60027. num = num.to_i + amount
  60028. write(name, num, options)
  60029. num
  60030. else
  60031. nil
  60032. end
  60033. end
  60034. end
  60035. def decrement(name, amount = 1, options = nil)
  60036. file_name = key_file_path(namespaced_key(name, options))
  60037. lock_file(file_name) do
  60038. options = merged_options(options)
  60039. if num = read(name, options)
  60040. num = num.to_i - amount
  60041. write(name, num, options)
  60042. num
  60043. else
  60044. nil
  60045. end
  60046. end
  60047. end
  60048. def delete_matched(matcher, options = nil)
  60049. options = merged_options(options)
  60050. instrument(:delete_matched, matcher.inspect) do
  60051. matcher = key_matcher(matcher, options)
  60052. search_dir(cache_path) do |path|
  60053. key = file_path_key(path)
  60054. delete_entry(key, options) if key.match(matcher)
  60055. end
  60056. end
  60057. end
  60058. protected
  60059. def read_entry(key, options)
  60060. file_name = key_file_path(key)
  60061. if File.exist?(file_name)
  60062. File.open(file_name) { |f| Marshal.load(f) }
  60063. end
  60064. rescue => e
  60065. logger.error("FileStoreError (#{e}): #{e.message}") if logger
  60066. nil
  60067. end
  60068. def write_entry(key, entry, options)
  60069. file_name = key_file_path(key)
  60070. ensure_cache_path(File.dirname(file_name))
  60071. File.atomic_write(file_name, cache_path) {|f| Marshal.dump(entry, f)}
  60072. true
  60073. end
  60074. def delete_entry(key, options)
  60075. file_name = key_file_path(key)
  60076. if File.exist?(file_name)
  60077. begin
  60078. File.delete(file_name)
  60079. delete_empty_directories(File.dirname(file_name))
  60080. true
  60081. rescue => e
  60082. # Just in case the error was caused by another process deleting the file first.
  60083. raise e if File.exist?(file_name)
  60084. false
  60085. end
  60086. end
  60087. end
  60088. private
  60089. # Lock a file for a block so only one process can modify it at a time.
  60090. def lock_file(file_name, &block) # :nodoc:
  60091. if File.exist?(file_name)
  60092. File.open(file_name, 'r+') do |f|
  60093. begin
  60094. f.flock File::LOCK_EX
  60095. yield
  60096. ensure
  60097. f.flock File::LOCK_UN
  60098. end
  60099. end
  60100. else
  60101. yield
  60102. end
  60103. end
  60104. # Translate a key into a file path.
  60105. def key_file_path(key)
  60106. fname = URI.encode_www_form_component(key)
  60107. hash = Zlib.adler32(fname)
  60108. hash, dir_1 = hash.divmod(0x1000)
  60109. dir_2 = hash.modulo(0x1000)
  60110. fname_paths = []
  60111. # Make sure file name doesn't exceed file system limits.
  60112. begin
  60113. fname_paths << fname[0, FILENAME_MAX_SIZE]
  60114. fname = fname[FILENAME_MAX_SIZE..-1]
  60115. end until fname.blank?
  60116. File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
  60117. end
  60118. # Translate a file path into a key.
  60119. def file_path_key(path)
  60120. fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
  60121. URI.decode_www_form_component(fname, Encoding::UTF_8)
  60122. end
  60123. # Delete empty directories in the cache.
  60124. def delete_empty_directories(dir)
  60125. return if dir == cache_path
  60126. if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
  60127. File.delete(dir) rescue nil
  60128. delete_empty_directories(File.dirname(dir))
  60129. end
  60130. end
  60131. # Make sure a file path's directories exist.
  60132. def ensure_cache_path(path)
  60133. FileUtils.makedirs(path) unless File.exist?(path)
  60134. end
  60135. def search_dir(dir, &callback)
  60136. return if !File.exist?(dir)
  60137. Dir.foreach(dir) do |d|
  60138. next if EXCLUDED_DIRS.include?(d)
  60139. name = File.join(dir, d)
  60140. if File.directory?(name)
  60141. search_dir(name, &callback)
  60142. else
  60143. callback.call name
  60144. end
  60145. end
  60146. end
  60147. end
  60148. end
  60149. end
  60150. begin
  60151. require 'dalli'
  60152. rescue LoadError => e
  60153. $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
  60154. raise e
  60155. end
  60156. require 'digest/md5'
  60157. require 'active_support/core_ext/marshal'
  60158. require 'active_support/core_ext/array/extract_options'
  60159. module ActiveSupport
  60160. module Cache
  60161. # A cache store implementation which stores data in Memcached:
  60162. # http://memcached.org/
  60163. #
  60164. # This is currently the most popular cache store for production websites.
  60165. #
  60166. # Special features:
  60167. # - Clustering and load balancing. One can specify multiple memcached servers,
  60168. # and MemCacheStore will load balance between all available servers. If a
  60169. # server goes down, then MemCacheStore will ignore it until it comes back up.
  60170. #
  60171. # MemCacheStore implements the Strategy::LocalCache strategy which implements
  60172. # an in-memory cache inside of a block.
  60173. class MemCacheStore < Store
  60174. ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
  60175. def self.build_mem_cache(*addresses)
  60176. addresses = addresses.flatten
  60177. options = addresses.extract_options!
  60178. addresses = ["localhost:11211"] if addresses.empty?
  60179. Dalli::Client.new(addresses, options)
  60180. end
  60181. # Creates a new MemCacheStore object, with the given memcached server
  60182. # addresses. Each address is either a host name, or a host-with-port string
  60183. # in the form of "host_name:port". For example:
  60184. #
  60185. # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
  60186. #
  60187. # If no addresses are specified, then MemCacheStore will connect to
  60188. # localhost port 11211 (the default memcached port).
  60189. #
  60190. # Instead of addresses one can pass in a MemCache-like object. For example:
  60191. #
  60192. # require 'memcached' # gem install memcached; uses C bindings to libmemcached
  60193. # ActiveSupport::Cache::MemCacheStore.new(Memcached::Rails.new("localhost:11211"))
  60194. def initialize(*addresses)
  60195. addresses = addresses.flatten
  60196. options = addresses.extract_options!
  60197. super(options)
  60198. if addresses.first.respond_to?(:get)
  60199. @data = addresses.first
  60200. else
  60201. mem_cache_options = options.dup
  60202. UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
  60203. @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
  60204. end
  60205. extend Strategy::LocalCache
  60206. extend LocalCacheWithRaw
  60207. end
  60208. # Reads multiple values from the cache using a single call to the
  60209. # servers for all keys. Options can be passed in the last argument.
  60210. def read_multi(*names)
  60211. options = names.extract_options!
  60212. options = merged_options(options)
  60213. keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
  60214. raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
  60215. values = {}
  60216. raw_values.each do |key, value|
  60217. entry = deserialize_entry(value)
  60218. values[keys_to_names[key]] = entry.value unless entry.expired?
  60219. end
  60220. values
  60221. end
  60222. # Increment a cached value. This method uses the memcached incr atomic
  60223. # operator and can only be used on values written with the :raw option.
  60224. # Calling it on a value not stored with :raw will initialize that value
  60225. # to zero.
  60226. def increment(name, amount = 1, options = nil) # :nodoc:
  60227. options = merged_options(options)
  60228. instrument(:increment, name, :amount => amount) do
  60229. @data.incr(escape_key(namespaced_key(name, options)), amount)
  60230. end
  60231. rescue Dalli::DalliError
  60232. logger.error("DalliError (#{e}): #{e.message}") if logger
  60233. nil
  60234. end
  60235. # Decrement a cached value. This method uses the memcached decr atomic
  60236. # operator and can only be used on values written with the :raw option.
  60237. # Calling it on a value not stored with :raw will initialize that value
  60238. # to zero.
  60239. def decrement(name, amount = 1, options = nil) # :nodoc:
  60240. options = merged_options(options)
  60241. instrument(:decrement, name, :amount => amount) do
  60242. @data.decr(escape_key(namespaced_key(name, options)), amount)
  60243. end
  60244. rescue Dalli::DalliError
  60245. logger.error("DalliError (#{e}): #{e.message}") if logger
  60246. nil
  60247. end
  60248. # Clear the entire cache on all memcached servers. This method should
  60249. # be used with care when shared cache is being used.
  60250. def clear(options = nil)
  60251. @data.flush_all
  60252. rescue Dalli::DalliError => e
  60253. logger.error("DalliError (#{e}): #{e.message}") if logger
  60254. nil
  60255. end
  60256. # Get the statistics from the memcached servers.
  60257. def stats
  60258. @data.stats
  60259. end
  60260. protected
  60261. # Read an entry from the cache.
  60262. def read_entry(key, options) # :nodoc:
  60263. deserialize_entry(@data.get(escape_key(key), options))
  60264. rescue Dalli::DalliError => e
  60265. logger.error("DalliError (#{e}): #{e.message}") if logger
  60266. nil
  60267. end
  60268. # Write an entry to the cache.
  60269. def write_entry(key, entry, options) # :nodoc:
  60270. method = options && options[:unless_exist] ? :add : :set
  60271. value = options[:raw] ? entry.value.to_s : entry
  60272. expires_in = options[:expires_in].to_i
  60273. if expires_in > 0 && !options[:raw]
  60274. # Set the memcache expire a few minutes in the future to support race condition ttls on read
  60275. expires_in += 5.minutes
  60276. end
  60277. @data.send(method, escape_key(key), value, expires_in, options)
  60278. rescue Dalli::DalliError => e
  60279. logger.error("DalliError (#{e}): #{e.message}") if logger
  60280. false
  60281. end
  60282. # Delete an entry from the cache.
  60283. def delete_entry(key, options) # :nodoc:
  60284. @data.delete(escape_key(key))
  60285. rescue Dalli::DalliError => e
  60286. logger.error("DalliError (#{e}): #{e.message}") if logger
  60287. false
  60288. end
  60289. private
  60290. # Memcache keys are binaries. So we need to force their encoding to binary
  60291. # before applying the regular expression to ensure we are escaping all
  60292. # characters properly.
  60293. def escape_key(key)
  60294. key = key.to_s.dup
  60295. key = key.force_encoding(Encoding::ASCII_8BIT)
  60296. key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
  60297. key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
  60298. key
  60299. end
  60300. def deserialize_entry(raw_value)
  60301. if raw_value
  60302. entry = Marshal.load(raw_value) rescue raw_value
  60303. entry.is_a?(Entry) ? entry : Entry.new(entry)
  60304. else
  60305. nil
  60306. end
  60307. end
  60308. # Provide support for raw values in the local cache strategy.
  60309. module LocalCacheWithRaw # :nodoc:
  60310. protected
  60311. def read_entry(key, options)
  60312. entry = super
  60313. if options[:raw] && local_cache && entry
  60314. entry = deserialize_entry(entry.value)
  60315. end
  60316. entry
  60317. end
  60318. def write_entry(key, entry, options) # :nodoc:
  60319. retval = super
  60320. if options[:raw] && local_cache && retval
  60321. raw_entry = Entry.new(entry.value.to_s)
  60322. raw_entry.expires_at = entry.expires_at
  60323. local_cache.write_entry(key, raw_entry, options)
  60324. end
  60325. retval
  60326. end
  60327. end
  60328. end
  60329. end
  60330. end
  60331. require 'monitor'
  60332. module ActiveSupport
  60333. module Cache
  60334. # A cache store implementation which stores everything into memory in the
  60335. # same process. If you're running multiple Ruby on Rails server processes
  60336. # (which is the case if you're using mongrel_cluster or Phusion Passenger),
  60337. # then this means that Rails server process instances won't be able
  60338. # to share cache data with each other and this may not be the most
  60339. # appropriate cache in that scenario.
  60340. #
  60341. # This cache has a bounded size specified by the :size options to the
  60342. # initializer (default is 32Mb). When the cache exceeds the allotted size,
  60343. # a cleanup will occur which tries to prune the cache down to three quarters
  60344. # of the maximum size by removing the least recently used entries.
  60345. #
  60346. # MemoryStore is thread-safe.
  60347. class MemoryStore < Store
  60348. def initialize(options = nil)
  60349. options ||= {}
  60350. super(options)
  60351. @data = {}
  60352. @key_access = {}
  60353. @max_size = options[:size] || 32.megabytes
  60354. @max_prune_time = options[:max_prune_time] || 2
  60355. @cache_size = 0
  60356. @monitor = Monitor.new
  60357. @pruning = false
  60358. end
  60359. def clear(options = nil)
  60360. synchronize do
  60361. @data.clear
  60362. @key_access.clear
  60363. @cache_size = 0
  60364. end
  60365. end
  60366. def cleanup(options = nil)
  60367. options = merged_options(options)
  60368. instrument(:cleanup, :size => @data.size) do
  60369. keys = synchronize{ @data.keys }
  60370. keys.each do |key|
  60371. entry = @data[key]
  60372. delete_entry(key, options) if entry && entry.expired?
  60373. end
  60374. end
  60375. end
  60376. # To ensure entries fit within the specified memory prune the cache by removing the least
  60377. # recently accessed entries.
  60378. def prune(target_size, max_time = nil)
  60379. return if pruning?
  60380. @pruning = true
  60381. begin
  60382. start_time = Time.now
  60383. cleanup
  60384. instrument(:prune, target_size, :from => @cache_size) do
  60385. keys = synchronize{ @key_access.keys.sort{|a,b| @key_access[a].to_f <=> @key_access[b].to_f} }
  60386. keys.each do |key|
  60387. delete_entry(key, options)
  60388. return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
  60389. end
  60390. end
  60391. ensure
  60392. @pruning = false
  60393. end
  60394. end
  60395. # Returns true if the cache is currently being pruned.
  60396. def pruning?
  60397. @pruning
  60398. end
  60399. # Increment an integer value in the cache.
  60400. def increment(name, amount = 1, options = nil)
  60401. synchronize do
  60402. options = merged_options(options)
  60403. if num = read(name, options)
  60404. num = num.to_i + amount
  60405. write(name, num, options)
  60406. num
  60407. else
  60408. nil
  60409. end
  60410. end
  60411. end
  60412. # Decrement an integer value in the cache.
  60413. def decrement(name, amount = 1, options = nil)
  60414. synchronize do
  60415. options = merged_options(options)
  60416. if num = read(name, options)
  60417. num = num.to_i - amount
  60418. write(name, num, options)
  60419. num
  60420. else
  60421. nil
  60422. end
  60423. end
  60424. end
  60425. def delete_matched(matcher, options = nil)
  60426. options = merged_options(options)
  60427. instrument(:delete_matched, matcher.inspect) do
  60428. matcher = key_matcher(matcher, options)
  60429. keys = synchronize { @data.keys }
  60430. keys.each do |key|
  60431. delete_entry(key, options) if key.match(matcher)
  60432. end
  60433. end
  60434. end
  60435. def inspect # :nodoc:
  60436. "<##{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
  60437. end
  60438. # Synchronize calls to the cache. This should be called wherever the underlying cache implementation
  60439. # is not thread safe.
  60440. def synchronize(&block) # :nodoc:
  60441. @monitor.synchronize(&block)
  60442. end
  60443. protected
  60444. def read_entry(key, options) # :nodoc:
  60445. entry = @data[key]
  60446. synchronize do
  60447. if entry
  60448. @key_access[key] = Time.now.to_f
  60449. else
  60450. @key_access.delete(key)
  60451. end
  60452. end
  60453. entry
  60454. end
  60455. def write_entry(key, entry, options) # :nodoc:
  60456. entry.dup_value!
  60457. synchronize do
  60458. old_entry = @data[key]
  60459. return false if @data.key?(key) && options[:unless_exist]
  60460. @cache_size -= old_entry.size if old_entry
  60461. @cache_size += entry.size
  60462. @key_access[key] = Time.now.to_f
  60463. @data[key] = entry
  60464. prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
  60465. true
  60466. end
  60467. end
  60468. def delete_entry(key, options) # :nodoc:
  60469. synchronize do
  60470. @key_access.delete(key)
  60471. entry = @data.delete(key)
  60472. @cache_size -= entry.size if entry
  60473. !!entry
  60474. end
  60475. end
  60476. end
  60477. end
  60478. end
  60479. module ActiveSupport
  60480. module Cache
  60481. # A cache store implementation which doesn't actually store anything. Useful in
  60482. # development and test environments where you don't want caching turned on but
  60483. # need to go through the caching interface.
  60484. #
  60485. # This cache does implement the local cache strategy, so values will actually
  60486. # be cached inside blocks that utilize this strategy. See
  60487. # ActiveSupport::Cache::Strategy::LocalCache for more details.
  60488. class NullStore < Store
  60489. def initialize(options = nil)
  60490. super(options)
  60491. extend Strategy::LocalCache
  60492. end
  60493. def clear(options = nil)
  60494. end
  60495. def cleanup(options = nil)
  60496. end
  60497. def increment(name, amount = 1, options = nil)
  60498. end
  60499. def decrement(name, amount = 1, options = nil)
  60500. end
  60501. def delete_matched(matcher, options = nil)
  60502. end
  60503. protected
  60504. def read_entry(key, options) # :nodoc:
  60505. end
  60506. def write_entry(key, entry, options) # :nodoc:
  60507. true
  60508. end
  60509. def delete_entry(key, options) # :nodoc:
  60510. false
  60511. end
  60512. end
  60513. end
  60514. end
  60515. require 'active_support/core_ext/object/duplicable'
  60516. require 'active_support/core_ext/string/inflections'
  60517. module ActiveSupport
  60518. module Cache
  60519. module Strategy
  60520. # Caches that implement LocalCache will be backed by an in-memory cache for the
  60521. # duration of a block. Repeated calls to the cache for the same key will hit the
  60522. # in-memory cache for faster access.
  60523. module LocalCache
  60524. # Simple memory backed cache. This cache is not thread safe and is intended only
  60525. # for serving as a temporary memory cache for a single thread.
  60526. class LocalStore < Store
  60527. def initialize
  60528. super
  60529. @data = {}
  60530. end
  60531. # Don't allow synchronizing since it isn't thread safe,
  60532. def synchronize # :nodoc:
  60533. yield
  60534. end
  60535. def clear(options = nil)
  60536. @data.clear
  60537. end
  60538. def read_entry(key, options)
  60539. @data[key]
  60540. end
  60541. def write_entry(key, value, options)
  60542. @data[key] = value
  60543. true
  60544. end
  60545. def delete_entry(key, options)
  60546. !!@data.delete(key)
  60547. end
  60548. end
  60549. # Use a local cache for the duration of block.
  60550. def with_local_cache
  60551. save_val = Thread.current[thread_local_key]
  60552. begin
  60553. Thread.current[thread_local_key] = LocalStore.new
  60554. yield
  60555. ensure
  60556. Thread.current[thread_local_key] = save_val
  60557. end
  60558. end
  60559. #--
  60560. # This class wraps up local storage for middlewares. Only the middleware method should
  60561. # construct them.
  60562. class Middleware # :nodoc:
  60563. attr_reader :name, :thread_local_key
  60564. def initialize(name, thread_local_key)
  60565. @name = name
  60566. @thread_local_key = thread_local_key
  60567. @app = nil
  60568. end
  60569. def new(app)
  60570. @app = app
  60571. self
  60572. end
  60573. def call(env)
  60574. Thread.current[thread_local_key] = LocalStore.new
  60575. @app.call(env)
  60576. ensure
  60577. Thread.current[thread_local_key] = nil
  60578. end
  60579. end
  60580. # Middleware class can be inserted as a Rack handler to be local cache for the
  60581. # duration of request.
  60582. def middleware
  60583. @middleware ||= Middleware.new(
  60584. "ActiveSupport::Cache::Strategy::LocalCache",
  60585. thread_local_key)
  60586. end
  60587. def clear(options = nil) # :nodoc:
  60588. local_cache.clear(options) if local_cache
  60589. super
  60590. end
  60591. def cleanup(options = nil) # :nodoc:
  60592. local_cache.clear(options) if local_cache
  60593. super
  60594. end
  60595. def increment(name, amount = 1, options = nil) # :nodoc:
  60596. value = bypass_local_cache{super}
  60597. if local_cache
  60598. local_cache.mute do
  60599. if value
  60600. local_cache.write(name, value, options)
  60601. else
  60602. local_cache.delete(name, options)
  60603. end
  60604. end
  60605. end
  60606. value
  60607. end
  60608. def decrement(name, amount = 1, options = nil) # :nodoc:
  60609. value = bypass_local_cache{super}
  60610. if local_cache
  60611. local_cache.mute do
  60612. if value
  60613. local_cache.write(name, value, options)
  60614. else
  60615. local_cache.delete(name, options)
  60616. end
  60617. end
  60618. end
  60619. value
  60620. end
  60621. protected
  60622. def read_entry(key, options) # :nodoc:
  60623. if local_cache
  60624. entry = local_cache.read_entry(key, options)
  60625. unless entry
  60626. entry = super
  60627. local_cache.write_entry(key, entry, options)
  60628. end
  60629. entry
  60630. else
  60631. super
  60632. end
  60633. end
  60634. def write_entry(key, entry, options) # :nodoc:
  60635. local_cache.write_entry(key, entry, options) if local_cache
  60636. super
  60637. end
  60638. def delete_entry(key, options) # :nodoc:
  60639. local_cache.delete_entry(key, options) if local_cache
  60640. super
  60641. end
  60642. private
  60643. def thread_local_key
  60644. @thread_local_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym
  60645. end
  60646. def local_cache
  60647. Thread.current[thread_local_key]
  60648. end
  60649. def bypass_local_cache
  60650. save_cache = Thread.current[thread_local_key]
  60651. begin
  60652. Thread.current[thread_local_key] = nil
  60653. yield
  60654. ensure
  60655. Thread.current[thread_local_key] = save_cache
  60656. end
  60657. end
  60658. end
  60659. end
  60660. end
  60661. end
  60662. require 'benchmark'
  60663. require 'zlib'
  60664. require 'active_support/core_ext/array/extract_options'
  60665. require 'active_support/core_ext/array/wrap'
  60666. require 'active_support/core_ext/benchmark'
  60667. require 'active_support/core_ext/class/attribute_accessors'
  60668. require 'active_support/core_ext/numeric/bytes'
  60669. require 'active_support/core_ext/numeric/time'
  60670. require 'active_support/core_ext/object/to_param'
  60671. require 'active_support/core_ext/string/inflections'
  60672. module ActiveSupport
  60673. # See ActiveSupport::Cache::Store for documentation.
  60674. module Cache
  60675. autoload :FileStore, 'active_support/cache/file_store'
  60676. autoload :MemoryStore, 'active_support/cache/memory_store'
  60677. autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
  60678. autoload :NullStore, 'active_support/cache/null_store'
  60679. # These options mean something to all cache implementations. Individual cache
  60680. # implementations may support additional options.
  60681. UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
  60682. module Strategy
  60683. autoload :LocalCache, 'active_support/cache/strategy/local_cache'
  60684. end
  60685. class << self
  60686. # Creates a new CacheStore object according to the given options.
  60687. #
  60688. # If no arguments are passed to this method, then a new
  60689. # ActiveSupport::Cache::MemoryStore object will be returned.
  60690. #
  60691. # If you pass a Symbol as the first argument, then a corresponding cache
  60692. # store class under the ActiveSupport::Cache namespace will be created.
  60693. # For example:
  60694. #
  60695. # ActiveSupport::Cache.lookup_store(:memory_store)
  60696. # # => returns a new ActiveSupport::Cache::MemoryStore object
  60697. #
  60698. # ActiveSupport::Cache.lookup_store(:mem_cache_store)
  60699. # # => returns a new ActiveSupport::Cache::MemCacheStore object
  60700. #
  60701. # Any additional arguments will be passed to the corresponding cache store
  60702. # class's constructor:
  60703. #
  60704. # ActiveSupport::Cache.lookup_store(:file_store, '/tmp/cache')
  60705. # # => same as: ActiveSupport::Cache::FileStore.new('/tmp/cache')
  60706. #
  60707. # If the first argument is not a Symbol, then it will simply be returned:
  60708. #
  60709. # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
  60710. # # => returns MyOwnCacheStore.new
  60711. def lookup_store(*store_option)
  60712. store, *parameters = *Array.wrap(store_option).flatten
  60713. case store
  60714. when Symbol
  60715. store_class_name = store.to_s.camelize
  60716. store_class =
  60717. begin
  60718. require "active_support/cache/#{store}"
  60719. rescue LoadError => e
  60720. raise "Could not find cache store adapter for #{store} (#{e})"
  60721. else
  60722. ActiveSupport::Cache.const_get(store_class_name)
  60723. end
  60724. store_class.new(*parameters)
  60725. when nil
  60726. ActiveSupport::Cache::MemoryStore.new
  60727. else
  60728. store
  60729. end
  60730. end
  60731. def expand_cache_key(key, namespace = nil)
  60732. expanded_cache_key = namespace ? "#{namespace}/" : ""
  60733. if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
  60734. expanded_cache_key << "#{prefix}/"
  60735. end
  60736. expanded_cache_key << retrieve_cache_key(key)
  60737. expanded_cache_key
  60738. end
  60739. private
  60740. def retrieve_cache_key(key)
  60741. case
  60742. when key.respond_to?(:cache_key) then key.cache_key
  60743. when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
  60744. when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
  60745. else key.to_param
  60746. end.to_s
  60747. end
  60748. end
  60749. # An abstract cache store class. There are multiple cache store
  60750. # implementations, each having its own additional features. See the classes
  60751. # under the ActiveSupport::Cache module, e.g.
  60752. # ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most
  60753. # popular cache store for large production websites.
  60754. #
  60755. # Some implementations may not support all methods beyond the basic cache
  60756. # methods of +fetch+, +write+, +read+, +exist?+, and +delete+.
  60757. #
  60758. # ActiveSupport::Cache::Store can store any serializable Ruby object.
  60759. #
  60760. # cache = ActiveSupport::Cache::MemoryStore.new
  60761. #
  60762. # cache.read('city') # => nil
  60763. # cache.write('city', "Duckburgh")
  60764. # cache.read('city') # => "Duckburgh"
  60765. #
  60766. # Keys are always translated into Strings and are case sensitive. When an
  60767. # object is specified as a key and has a +cache_key+ method defined, this
  60768. # method will be called to define the key. Otherwise, the +to_param+
  60769. # method will be called. Hashes and Arrays can also be used as keys. The
  60770. # elements will be delimited by slashes, and the elements within a Hash
  60771. # will be sorted by key so they are consistent.
  60772. #
  60773. # cache.read('city') == cache.read(:city) # => true
  60774. #
  60775. # Nil values can be cached.
  60776. #
  60777. # If your cache is on a shared infrastructure, you can define a namespace
  60778. # for your cache entries. If a namespace is defined, it will be prefixed on
  60779. # to every key. The namespace can be either a static value or a Proc. If it
  60780. # is a Proc, it will be invoked when each key is evaluated so that you can
  60781. # use application logic to invalidate keys.
  60782. #
  60783. # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
  60784. # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
  60785. #
  60786. # Caches can also store values in a compressed format to save space and
  60787. # reduce time spent sending data. Since there is overhead, values must be
  60788. # large enough to warrant compression. To turn on compression either pass
  60789. # <tt>compress: true</tt> in the initializer or as an option to +fetch+
  60790. # or +write+. To specify the threshold at which to compress values, set the
  60791. # <tt>:compress_threshold</tt> option. The default threshold is 16K.
  60792. class Store
  60793. cattr_accessor :logger, :instance_writer => true
  60794. attr_reader :silence, :options
  60795. alias :silence? :silence
  60796. # Create a new cache. The options will be passed to any write method calls
  60797. # except for <tt>:namespace</tt> which can be used to set the global
  60798. # namespace for the cache.
  60799. def initialize(options = nil)
  60800. @options = options ? options.dup : {}
  60801. end
  60802. # Silence the logger.
  60803. def silence!
  60804. @silence = true
  60805. self
  60806. end
  60807. # Silence the logger within a block.
  60808. def mute
  60809. previous_silence, @silence = defined?(@silence) && @silence, true
  60810. yield
  60811. ensure
  60812. @silence = previous_silence
  60813. end
  60814. # Set to +true+ if cache stores should be instrumented.
  60815. # Default is +false+.
  60816. def self.instrument=(boolean)
  60817. Thread.current[:instrument_cache_store] = boolean
  60818. end
  60819. def self.instrument
  60820. Thread.current[:instrument_cache_store] || false
  60821. end
  60822. # Fetches data from the cache, using the given key. If there is data in
  60823. # the cache with the given key, then that data is returned.
  60824. #
  60825. # If there is no such data in the cache (a cache miss), then +nil+ will be
  60826. # returned. However, if a block has been passed, that block will be passed
  60827. # the key and executed in the event of a cache miss. The return value of the
  60828. # block will be written to the cache under the given cache key, and that
  60829. # return value will be returned.
  60830. #
  60831. # cache.write('today', 'Monday')
  60832. # cache.fetch('today') # => "Monday"
  60833. #
  60834. # cache.fetch('city') # => nil
  60835. # cache.fetch('city') do
  60836. # 'Duckburgh'
  60837. # end
  60838. # cache.fetch('city') # => "Duckburgh"
  60839. #
  60840. # You may also specify additional options via the +options+ argument.
  60841. # Setting <tt>force: true</tt> will force a cache miss:
  60842. #
  60843. # cache.write('today', 'Monday')
  60844. # cache.fetch('today', force: true) # => nil
  60845. #
  60846. # Setting <tt>:compress</tt> will store a large cache entry set by the call
  60847. # in a compressed format.
  60848. #
  60849. # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
  60850. # All caches support auto-expiring content after a specified number of
  60851. # seconds. This value can be specified as an option to the constructor
  60852. # (in which case all entries will be affected), or it can be supplied to
  60853. # the +fetch+ or +write+ method to effect just one entry.
  60854. #
  60855. # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
  60856. # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
  60857. #
  60858. # Setting <tt>:race_condition_ttl</tt> is very useful in situations where
  60859. # a cache entry is used very frequently and is under heavy load. If a
  60860. # cache expires and due to heavy load seven different processes will try
  60861. # to read data natively and then they all will try to write to cache. To
  60862. # avoid that case the first process to find an expired cache entry will
  60863. # bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
  60864. # Yes, this process is extending the time for a stale value by another few
  60865. # seconds. Because of extended life of the previous cache, other processes
  60866. # will continue to use slightly stale data for a just a big longer. In the
  60867. # meantime that first process will go ahead and will write into cache the
  60868. # new value. After that all the processes will start getting new value.
  60869. # The key is to keep <tt>:race_condition_ttl</tt> small.
  60870. #
  60871. # If the process regenerating the entry errors out, the entry will be
  60872. # regenerated after the specified number of seconds. Also note that the
  60873. # life of stale cache is extended only if it expired recently. Otherwise
  60874. # a new value is generated and <tt>:race_condition_ttl</tt> does not play
  60875. # any role.
  60876. #
  60877. # # Set all values to expire after one minute.
  60878. # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
  60879. #
  60880. # cache.write('foo', 'original value')
  60881. # val_1 = nil
  60882. # val_2 = nil
  60883. # sleep 60
  60884. #
  60885. # Thread.new do
  60886. # val_1 = cache.fetch('foo', race_condition_ttl: 10) do
  60887. # sleep 1
  60888. # 'new value 1'
  60889. # end
  60890. # end
  60891. #
  60892. # Thread.new do
  60893. # val_2 = cache.fetch('foo', race_condition_ttl: 10) do
  60894. # 'new value 2'
  60895. # end
  60896. # end
  60897. #
  60898. # # val_1 => "new value 1"
  60899. # # val_2 => "original value"
  60900. # # sleep 10 # First thread extend the life of cache by another 10 seconds
  60901. # # cache.fetch('foo') => "new value 1"
  60902. #
  60903. # Other options will be handled by the specific cache store implementation.
  60904. # Internally, #fetch calls #read_entry, and calls #write_entry on a cache
  60905. # miss. +options+ will be passed to the #read and #write calls.
  60906. #
  60907. # For example, MemCacheStore's #write method supports the +:raw+
  60908. # option, which tells the memcached server to store all values as strings.
  60909. # We can use this option with #fetch too:
  60910. #
  60911. # cache = ActiveSupport::Cache::MemCacheStore.new
  60912. # cache.fetch("foo", force: true, raw: true) do
  60913. # :bar
  60914. # end
  60915. # cache.fetch('foo') # => "bar"
  60916. def fetch(name, options = nil)
  60917. if block_given?
  60918. options = merged_options(options)
  60919. key = namespaced_key(name, options)
  60920. cached_entry = find_cached_entry(key, name, options) unless options[:force]
  60921. entry = handle_expired_entry(cached_entry, key, options)
  60922. if entry
  60923. get_entry_value(entry, name, options)
  60924. else
  60925. save_block_result_to_cache(name, options) { |_name| yield _name }
  60926. end
  60927. else
  60928. read(name, options)
  60929. end
  60930. end
  60931. # Fetches data from the cache, using the given key. If there is data in
  60932. # the cache with the given key, then that data is returned. Otherwise,
  60933. # +nil+ is returned.
  60934. #
  60935. # Options are passed to the underlying cache implementation.
  60936. def read(name, options = nil)
  60937. options = merged_options(options)
  60938. key = namespaced_key(name, options)
  60939. instrument(:read, name, options) do |payload|
  60940. entry = read_entry(key, options)
  60941. if entry
  60942. if entry.expired?
  60943. delete_entry(key, options)
  60944. payload[:hit] = false if payload
  60945. nil
  60946. else
  60947. payload[:hit] = true if payload
  60948. entry.value
  60949. end
  60950. else
  60951. payload[:hit] = false if payload
  60952. nil
  60953. end
  60954. end
  60955. end
  60956. # Read multiple values at once from the cache. Options can be passed
  60957. # in the last argument.
  60958. #
  60959. # Some cache implementation may optimize this method.
  60960. #
  60961. # Returns a hash mapping the names provided to the values found.
  60962. def read_multi(*names)
  60963. options = names.extract_options!
  60964. options = merged_options(options)
  60965. results = {}
  60966. names.each do |name|
  60967. key = namespaced_key(name, options)
  60968. entry = read_entry(key, options)
  60969. if entry
  60970. if entry.expired?
  60971. delete_entry(key, options)
  60972. else
  60973. results[name] = entry.value
  60974. end
  60975. end
  60976. end
  60977. results
  60978. end
  60979. # Writes the value to the cache, with the key.
  60980. #
  60981. # Options are passed to the underlying cache implementation.
  60982. def write(name, value, options = nil)
  60983. options = merged_options(options)
  60984. instrument(:write, name, options) do |payload|
  60985. entry = Entry.new(value, options)
  60986. write_entry(namespaced_key(name, options), entry, options)
  60987. end
  60988. end
  60989. # Deletes an entry in the cache. Returns +true+ if an entry is deleted.
  60990. #
  60991. # Options are passed to the underlying cache implementation.
  60992. def delete(name, options = nil)
  60993. options = merged_options(options)
  60994. instrument(:delete, name) do |payload|
  60995. delete_entry(namespaced_key(name, options), options)
  60996. end
  60997. end
  60998. # Return +true+ if the cache contains an entry for the given key.
  60999. #
  61000. # Options are passed to the underlying cache implementation.
  61001. def exist?(name, options = nil)
  61002. options = merged_options(options)
  61003. instrument(:exist?, name) do |payload|
  61004. entry = read_entry(namespaced_key(name, options), options)
  61005. entry && !entry.expired?
  61006. end
  61007. end
  61008. # Delete all entries with keys matching the pattern.
  61009. #
  61010. # Options are passed to the underlying cache implementation.
  61011. #
  61012. # All implementations may not support this method.
  61013. def delete_matched(matcher, options = nil)
  61014. raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
  61015. end
  61016. # Increment an integer value in the cache.
  61017. #
  61018. # Options are passed to the underlying cache implementation.
  61019. #
  61020. # All implementations may not support this method.
  61021. def increment(name, amount = 1, options = nil)
  61022. raise NotImplementedError.new("#{self.class.name} does not support increment")
  61023. end
  61024. # Decrement an integer value in the cache.
  61025. #
  61026. # Options are passed to the underlying cache implementation.
  61027. #
  61028. # All implementations may not support this method.
  61029. def decrement(name, amount = 1, options = nil)
  61030. raise NotImplementedError.new("#{self.class.name} does not support decrement")
  61031. end
  61032. # Cleanup the cache by removing expired entries.
  61033. #
  61034. # Options are passed to the underlying cache implementation.
  61035. #
  61036. # All implementations may not support this method.
  61037. def cleanup(options = nil)
  61038. raise NotImplementedError.new("#{self.class.name} does not support cleanup")
  61039. end
  61040. # Clear the entire cache. Be careful with this method since it could
  61041. # affect other processes if shared cache is being used.
  61042. #
  61043. # Options are passed to the underlying cache implementation.
  61044. #
  61045. # All implementations may not support this method.
  61046. def clear(options = nil)
  61047. raise NotImplementedError.new("#{self.class.name} does not support clear")
  61048. end
  61049. protected
  61050. # Add the namespace defined in the options to a pattern designed to
  61051. # match keys. Implementations that support delete_matched should call
  61052. # this method to translate a pattern that matches names into one that
  61053. # matches namespaced keys.
  61054. def key_matcher(pattern, options)
  61055. prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
  61056. if prefix
  61057. source = pattern.source
  61058. if source.start_with?('^')
  61059. source = source[1, source.length]
  61060. else
  61061. source = ".*#{source[0, source.length]}"
  61062. end
  61063. Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
  61064. else
  61065. pattern
  61066. end
  61067. end
  61068. # Read an entry from the cache implementation. Subclasses must implement
  61069. # this method.
  61070. def read_entry(key, options) # :nodoc:
  61071. raise NotImplementedError.new
  61072. end
  61073. # Write an entry to the cache implementation. Subclasses must implement
  61074. # this method.
  61075. def write_entry(key, entry, options) # :nodoc:
  61076. raise NotImplementedError.new
  61077. end
  61078. # Delete an entry from the cache implementation. Subclasses must
  61079. # implement this method.
  61080. def delete_entry(key, options) # :nodoc:
  61081. raise NotImplementedError.new
  61082. end
  61083. private
  61084. # Merge the default options with ones specific to a method call.
  61085. def merged_options(call_options) # :nodoc:
  61086. if call_options
  61087. options.merge(call_options)
  61088. else
  61089. options.dup
  61090. end
  61091. end
  61092. # Expand key to be a consistent string value. Invoke +cache_key+ if
  61093. # object responds to +cache_key+. Otherwise, +to_param+ method will be
  61094. # called. If the key is a Hash, then keys will be sorted alphabetically.
  61095. def expanded_key(key) # :nodoc:
  61096. return key.cache_key.to_s if key.respond_to?(:cache_key)
  61097. case key
  61098. when Array
  61099. if key.size > 1
  61100. key = key.collect{|element| expanded_key(element)}
  61101. else
  61102. key = key.first
  61103. end
  61104. when Hash
  61105. key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
  61106. end
  61107. key.to_param
  61108. end
  61109. # Prefix a key with the namespace. Namespace and key will be delimited
  61110. # with a colon.
  61111. def namespaced_key(key, options)
  61112. key = expanded_key(key)
  61113. namespace = options[:namespace] if options
  61114. prefix = namespace.is_a?(Proc) ? namespace.call : namespace
  61115. key = "#{prefix}:#{key}" if prefix
  61116. key
  61117. end
  61118. def instrument(operation, key, options = nil)
  61119. log(operation, key, options)
  61120. if self.class.instrument
  61121. payload = { :key => key }
  61122. payload.merge!(options) if options.is_a?(Hash)
  61123. ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
  61124. else
  61125. yield(nil)
  61126. end
  61127. end
  61128. def log(operation, key, options = nil)
  61129. return unless logger && logger.debug? && !silence?
  61130. logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
  61131. end
  61132. def find_cached_entry(key, name, options)
  61133. instrument(:read, name, options) do |payload|
  61134. payload[:super_operation] = :fetch if payload
  61135. read_entry(key, options)
  61136. end
  61137. end
  61138. def handle_expired_entry(entry, key, options)
  61139. if entry && entry.expired?
  61140. race_ttl = options[:race_condition_ttl].to_i
  61141. if race_ttl && (Time.now - entry.expires_at <= race_ttl)
  61142. # When an entry has :race_condition_ttl defined, put the stale entry back into the cache
  61143. # for a brief period while the entry is begin recalculated.
  61144. entry.expires_at = Time.now + race_ttl
  61145. write_entry(key, entry, :expires_in => race_ttl * 2)
  61146. else
  61147. delete_entry(key, options)
  61148. end
  61149. entry = nil
  61150. end
  61151. entry
  61152. end
  61153. def get_entry_value(entry, name, options)
  61154. instrument(:fetch_hit, name, options) { |payload| }
  61155. entry.value
  61156. end
  61157. def save_block_result_to_cache(name, options)
  61158. result = instrument(:generate, name, options) do |payload|
  61159. yield(name)
  61160. end
  61161. write(name, result, options)
  61162. result
  61163. end
  61164. end
  61165. # This class is used to represent cache entries. Cache entries have a value and an optional
  61166. # expiration time. The expiration time is used to support the :race_condition_ttl option
  61167. # on the cache.
  61168. #
  61169. # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
  61170. # using short instance variable names that are lazily defined.
  61171. class Entry # :nodoc:
  61172. DEFAULT_COMPRESS_LIMIT = 16.kilobytes
  61173. # Create a new cache entry for the specified value. Options supported are
  61174. # +:compress+, +:compress_threshold+, and +:expires_in+.
  61175. def initialize(value, options = {})
  61176. if should_compress?(value, options)
  61177. @v = compress(value)
  61178. @c = true
  61179. else
  61180. @v = value
  61181. end
  61182. if expires_in = options[:expires_in]
  61183. @x = (Time.now + expires_in).to_i
  61184. end
  61185. end
  61186. def value
  61187. convert_version_3_entry! if defined?(@value)
  61188. compressed? ? uncompress(@v) : @v
  61189. end
  61190. # Check if the entry is expired. The +expires_in+ parameter can override
  61191. # the value set when the entry was created.
  61192. def expired?
  61193. convert_version_3_entry! if defined?(@value)
  61194. if defined?(@x)
  61195. @x && @x < Time.now.to_i
  61196. else
  61197. false
  61198. end
  61199. end
  61200. def expires_at
  61201. Time.at(@x) if defined?(@x)
  61202. end
  61203. def expires_at=(value)
  61204. @x = value.to_i
  61205. end
  61206. # Returns the size of the cached value. This could be less than
  61207. # <tt>value.size</tt> if the data is compressed.
  61208. def size
  61209. if defined?(@s)
  61210. @s
  61211. else
  61212. case value
  61213. when NilClass
  61214. 0
  61215. when String
  61216. @v.bytesize
  61217. else
  61218. @s = Marshal.dump(@v).bytesize
  61219. end
  61220. end
  61221. end
  61222. # Duplicate the value in a class. This is used by cache implementations that don't natively
  61223. # serialize entries to protect against accidental cache modifications.
  61224. def dup_value!
  61225. convert_version_3_entry! if defined?(@value)
  61226. if @v && !compressed? && !(@v.is_a?(Numeric) || @v == true || @v == false)
  61227. if @v.is_a?(String)
  61228. @v = @v.dup
  61229. else
  61230. @v = Marshal.load(Marshal.dump(@v))
  61231. end
  61232. end
  61233. end
  61234. private
  61235. def should_compress?(value, options)
  61236. if value && options[:compress]
  61237. compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
  61238. serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
  61239. return true if serialized_value_size >= compress_threshold
  61240. end
  61241. false
  61242. end
  61243. def compressed?
  61244. defined?(@c) ? @c : false
  61245. end
  61246. def compress(value)
  61247. Zlib::Deflate.deflate(Marshal.dump(value))
  61248. end
  61249. def uncompress(value)
  61250. Marshal.load(Zlib::Inflate.inflate(value))
  61251. end
  61252. # The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
  61253. # to ensure that cache entries created under the old version still work with the new class definition.
  61254. def convert_version_3_entry!
  61255. if defined?(@value)
  61256. @v = @value
  61257. remove_instance_variable(:@value)
  61258. end
  61259. if defined?(@compressed)
  61260. @c = @compressed
  61261. remove_instance_variable(:@compressed)
  61262. end
  61263. if defined?(@expires_in) && defined?(@created_at) && @expires_in && @created_at
  61264. @x = (@created_at + @expires_in).to_i
  61265. remove_instance_variable(:@created_at)
  61266. remove_instance_variable(:@expires_in)
  61267. end
  61268. end
  61269. end
  61270. end
  61271. end
  61272. require 'thread_safe'
  61273. require 'active_support/concern'
  61274. require 'active_support/descendants_tracker'
  61275. require 'active_support/core_ext/class/attribute'
  61276. require 'active_support/core_ext/kernel/reporting'
  61277. require 'active_support/core_ext/kernel/singleton_class'
  61278. module ActiveSupport
  61279. # Callbacks are code hooks that are run at key points in an object's lifecycle.
  61280. # The typical use case is to have a base class define a set of callbacks
  61281. # relevant to the other functionality it supplies, so that subclasses can
  61282. # install callbacks that enhance or modify the base functionality without
  61283. # needing to override or redefine methods of the base class.
  61284. #
  61285. # Mixing in this module allows you to define the events in the object's
  61286. # lifecycle that will support callbacks (via +ClassMethods.define_callbacks+),
  61287. # set the instance methods, procs, or callback objects to be called (via
  61288. # +ClassMethods.set_callback+), and run the installed callbacks at the
  61289. # appropriate times (via +run_callbacks+).
  61290. #
  61291. # Three kinds of callbacks are supported: before callbacks, run before a
  61292. # certain event; after callbacks, run after the event; and around callbacks,
  61293. # blocks that surround the event, triggering it when they yield. Callback code
  61294. # can be contained in instance methods, procs or lambdas, or callback objects
  61295. # that respond to certain predetermined methods. See +ClassMethods.set_callback+
  61296. # for details.
  61297. #
  61298. # class Record
  61299. # include ActiveSupport::Callbacks
  61300. # define_callbacks :save
  61301. #
  61302. # def save
  61303. # run_callbacks :save do
  61304. # puts "- save"
  61305. # end
  61306. # end
  61307. # end
  61308. #
  61309. # class PersonRecord < Record
  61310. # set_callback :save, :before, :saving_message
  61311. # def saving_message
  61312. # puts "saving..."
  61313. # end
  61314. #
  61315. # set_callback :save, :after do |object|
  61316. # puts "saved"
  61317. # end
  61318. # end
  61319. #
  61320. # person = PersonRecord.new
  61321. # person.save
  61322. #
  61323. # Output:
  61324. # saving...
  61325. # - save
  61326. # saved
  61327. module Callbacks
  61328. extend Concern
  61329. included do
  61330. extend ActiveSupport::DescendantsTracker
  61331. end
  61332. # Runs the callbacks for the given event.
  61333. #
  61334. # Calls the before and around callbacks in the order they were set, yields
  61335. # the block (if given one), and then runs the after callbacks in reverse
  61336. # order.
  61337. #
  61338. # If the callback chain was halted, returns +false+. Otherwise returns the
  61339. # result of the block, or +true+ if no block is given.
  61340. #
  61341. # run_callbacks :save do
  61342. # save
  61343. # end
  61344. def run_callbacks(kind, &block)
  61345. runner_name = self.class.__define_callbacks(kind, self)
  61346. send(runner_name, &block)
  61347. end
  61348. private
  61349. # A hook invoked everytime a before callback is halted.
  61350. # This can be overridden in AS::Callback implementors in order
  61351. # to provide better debugging/logging.
  61352. def halted_callback_hook(filter)
  61353. end
  61354. class Callback #:nodoc:#
  61355. @@_callback_sequence = 0
  61356. attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter
  61357. def initialize(chain, filter, kind, options, klass)
  61358. @chain, @kind, @klass = chain, kind, klass
  61359. deprecate_per_key_option(options)
  61360. normalize_options!(options)
  61361. @raw_filter, @options = filter, options
  61362. @filter = _compile_filter(filter)
  61363. recompile_options!
  61364. end
  61365. def deprecate_per_key_option(options)
  61366. if options[:per_key]
  61367. raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead."
  61368. end
  61369. end
  61370. def clone(chain, klass)
  61371. obj = super()
  61372. obj.chain = chain
  61373. obj.klass = klass
  61374. obj.options = @options.dup
  61375. obj.options[:if] = @options[:if].dup
  61376. obj.options[:unless] = @options[:unless].dup
  61377. obj
  61378. end
  61379. def normalize_options!(options)
  61380. options[:if] = Array(options[:if])
  61381. options[:unless] = Array(options[:unless])
  61382. end
  61383. def name
  61384. chain.name
  61385. end
  61386. def next_id
  61387. @@_callback_sequence += 1
  61388. end
  61389. def matches?(_kind, _filter)
  61390. @kind == _kind && @filter == _filter
  61391. end
  61392. def duplicates?(other)
  61393. matches?(other.kind, other.filter)
  61394. end
  61395. def _update_filter(filter_options, new_options)
  61396. filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless)
  61397. filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if)
  61398. end
  61399. def recompile!(_options)
  61400. deprecate_per_key_option(_options)
  61401. _update_filter(self.options, _options)
  61402. recompile_options!
  61403. end
  61404. # Wraps code with filter
  61405. def apply(code)
  61406. case @kind
  61407. when :before
  61408. <<-RUBY_EVAL
  61409. if !halted && #{@compiled_options}
  61410. # This double assignment is to prevent warnings in 1.9.3 as
  61411. # the `result` variable is not always used except if the
  61412. # terminator code refers to it.
  61413. result = result = #{@filter}
  61414. halted = (#{chain.config[:terminator]})
  61415. if halted
  61416. halted_callback_hook(#{@raw_filter.inspect.inspect})
  61417. end
  61418. end
  61419. #{code}
  61420. RUBY_EVAL
  61421. when :after
  61422. <<-RUBY_EVAL
  61423. #{code}
  61424. if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
  61425. #{@filter}
  61426. end
  61427. RUBY_EVAL
  61428. when :around
  61429. name = define_conditional_callback
  61430. <<-RUBY_EVAL
  61431. #{name}(halted) do
  61432. #{code}
  61433. value
  61434. end
  61435. RUBY_EVAL
  61436. end
  61437. end
  61438. private
  61439. # Compile around filters with conditions into proxy methods
  61440. # that contain the conditions.
  61441. #
  61442. # For `set_callback :save, :around, :filter_name, if: :condition':
  61443. #
  61444. # def _conditional_callback_save_17
  61445. # if condition
  61446. # filter_name do
  61447. # yield self
  61448. # end
  61449. # else
  61450. # yield self
  61451. # end
  61452. # end
  61453. def define_conditional_callback
  61454. name = "_conditional_callback_#{@kind}_#{next_id}"
  61455. @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  61456. def #{name}(halted)
  61457. if #{@compiled_options} && !halted
  61458. #{@filter} do
  61459. yield self
  61460. end
  61461. else
  61462. yield self
  61463. end
  61464. end
  61465. RUBY_EVAL
  61466. name
  61467. end
  61468. # Options support the same options as filters themselves (and support
  61469. # symbols, string, procs, and objects), so compile a conditional
  61470. # expression based on the options.
  61471. def recompile_options!
  61472. conditions = ["true"]
  61473. unless options[:if].empty?
  61474. conditions << Array(_compile_filter(options[:if]))
  61475. end
  61476. unless options[:unless].empty?
  61477. conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
  61478. end
  61479. @compiled_options = conditions.flatten.join(" && ")
  61480. end
  61481. # Filters support:
  61482. #
  61483. # Arrays:: Used in conditions. This is used to specify
  61484. # multiple conditions. Used internally to
  61485. # merge conditions from skip_* filters.
  61486. # Symbols:: A method to call.
  61487. # Strings:: Some content to evaluate.
  61488. # Procs:: A proc to call with the object.
  61489. # Objects:: An object with a <tt>before_foo</tt> method on it to call.
  61490. #
  61491. # All of these objects are compiled into methods and handled
  61492. # the same after this point:
  61493. #
  61494. # Arrays:: Merged together into a single filter.
  61495. # Symbols:: Already methods.
  61496. # Strings:: class_eval'ed into methods.
  61497. # Procs:: define_method'ed into methods.
  61498. # Objects::
  61499. # a method is created that calls the before_foo method
  61500. # on the object.
  61501. def _compile_filter(filter)
  61502. method_name = "_callback_#{@kind}_#{next_id}"
  61503. case filter
  61504. when Array
  61505. filter.map {|f| _compile_filter(f)}
  61506. when Symbol
  61507. filter
  61508. when String
  61509. "(#{filter})"
  61510. when Proc
  61511. @klass.send(:define_method, method_name, &filter)
  61512. return method_name if filter.arity <= 0
  61513. method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
  61514. else
  61515. @klass.send(:define_method, "#{method_name}_object") { filter }
  61516. _normalize_legacy_filter(kind, filter)
  61517. scopes = Array(chain.config[:scope])
  61518. method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
  61519. @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  61520. def #{method_name}(&blk)
  61521. #{method_name}_object.send(:#{method_to_call}, self, &blk)
  61522. end
  61523. RUBY_EVAL
  61524. method_name
  61525. end
  61526. end
  61527. def _normalize_legacy_filter(kind, filter)
  61528. if !filter.respond_to?(kind) && filter.respond_to?(:filter)
  61529. message = "Filter object with #filter method is deprecated. Define method corresponding " \
  61530. "to filter type (#before, #after or #around)."
  61531. ActiveSupport::Deprecation.warn message
  61532. filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  61533. def #{kind}(context, &block) filter(context, &block) end
  61534. RUBY_EVAL
  61535. elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around)
  61536. message = "Filter object with #before and #after methods is deprecated. Define #around method instead."
  61537. ActiveSupport::Deprecation.warn message
  61538. def filter.around(context)
  61539. should_continue = before(context)
  61540. yield if should_continue
  61541. after(context)
  61542. end
  61543. end
  61544. end
  61545. end
  61546. # An Array with a compile method.
  61547. class CallbackChain < Array #:nodoc:#
  61548. attr_reader :name, :config
  61549. def initialize(name, config)
  61550. @name = name
  61551. @config = {
  61552. :terminator => "false",
  61553. :scope => [ :kind ]
  61554. }.merge(config)
  61555. end
  61556. def compile
  61557. method = []
  61558. method << "value = nil"
  61559. method << "halted = false"
  61560. callbacks = "value = !halted && (!block_given? || yield)"
  61561. reverse_each do |callback|
  61562. callbacks = callback.apply(callbacks)
  61563. end
  61564. method << callbacks
  61565. method << "value"
  61566. method.join("\n")
  61567. end
  61568. def append(*callbacks)
  61569. callbacks.each { |c| append_one(c) }
  61570. end
  61571. def prepend(*callbacks)
  61572. callbacks.each { |c| prepend_one(c) }
  61573. end
  61574. private
  61575. def append_one(callback)
  61576. remove_duplicates(callback)
  61577. push(callback)
  61578. end
  61579. def prepend_one(callback)
  61580. remove_duplicates(callback)
  61581. unshift(callback)
  61582. end
  61583. def remove_duplicates(callback)
  61584. delete_if { |c| callback.duplicates?(c) }
  61585. end
  61586. end
  61587. module ClassMethods
  61588. # This method defines callback chain method for the given kind
  61589. # if it was not yet defined.
  61590. # This generated method plays caching role.
  61591. def __define_callbacks(kind, object) #:nodoc:
  61592. name = __callback_runner_name(kind)
  61593. unless object.respond_to?(name, true)
  61594. str = object.send("_#{kind}_callbacks").compile
  61595. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  61596. def #{name}() #{str} end
  61597. protected :#{name}
  61598. RUBY_EVAL
  61599. end
  61600. name
  61601. end
  61602. def __reset_runner(symbol)
  61603. name = __callback_runner_name(symbol)
  61604. undef_method(name) if method_defined?(name)
  61605. end
  61606. def __callback_runner_name_cache
  61607. @__callback_runner_name_cache ||= ThreadSafe::Cache.new {|cache, kind| cache[kind] = __generate_callback_runner_name(kind) }
  61608. end
  61609. def __generate_callback_runner_name(kind)
  61610. "_run__#{self.name.hash.abs}__#{kind}__callbacks"
  61611. end
  61612. def __callback_runner_name(kind)
  61613. __callback_runner_name_cache[kind]
  61614. end
  61615. # This is used internally to append, prepend and skip callbacks to the
  61616. # CallbackChain.
  61617. def __update_callbacks(name, filters = [], block = nil) #:nodoc:
  61618. type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
  61619. options = filters.last.is_a?(Hash) ? filters.pop : {}
  61620. filters.unshift(block) if block
  61621. ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
  61622. chain = target.send("_#{name}_callbacks")
  61623. yield target, chain.dup, type, filters, options
  61624. target.__reset_runner(name)
  61625. end
  61626. end
  61627. # Install a callback for the given event.
  61628. #
  61629. # set_callback :save, :before, :before_meth
  61630. # set_callback :save, :after, :after_meth, if: :condition
  61631. # set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }
  61632. #
  61633. # The second arguments indicates whether the callback is to be run +:before+,
  61634. # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
  61635. # means the first example above can also be written as:
  61636. #
  61637. # set_callback :save, :before_meth
  61638. #
  61639. # The callback can specified as a symbol naming an instance method; as a
  61640. # proc, lambda, or block; as a string to be instance evaluated; or as an
  61641. # object that responds to a certain method determined by the <tt>:scope</tt>
  61642. # argument to +define_callback+.
  61643. #
  61644. # If a proc, lambda, or block is given, its body is evaluated in the context
  61645. # of the current object. It can also optionally accept the current object as
  61646. # an argument.
  61647. #
  61648. # Before and around callbacks are called in the order that they are set;
  61649. # after callbacks are called in the reverse order.
  61650. #
  61651. # Around callbacks can access the return value from the event, if it
  61652. # wasn't halted, from the +yield+ call.
  61653. #
  61654. # ===== Options
  61655. #
  61656. # * <tt>:if</tt> - A symbol naming an instance method or a proc; the
  61657. # callback will be called only when it returns a +true+ value.
  61658. # * <tt>:unless</tt> - A symbol naming an instance method or a proc; the
  61659. # callback will be called only when it returns a +false+ value.
  61660. # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
  61661. # existing chain rather than appended.
  61662. def set_callback(name, *filter_list, &block)
  61663. mapped = nil
  61664. __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
  61665. mapped ||= filters.map do |filter|
  61666. Callback.new(chain, filter, type, options.dup, self)
  61667. end
  61668. options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
  61669. target.send("_#{name}_callbacks=", chain)
  61670. end
  61671. end
  61672. # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
  61673. # <tt>:unless</tt> options may be passed in order to control when the
  61674. # callback is skipped.
  61675. #
  61676. # class Writer < Person
  61677. # skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
  61678. # end
  61679. def skip_callback(name, *filter_list, &block)
  61680. __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
  61681. filters.each do |filter|
  61682. filter = chain.find {|c| c.matches?(type, filter) }
  61683. if filter && options.any?
  61684. new_filter = filter.clone(chain, self)
  61685. chain.insert(chain.index(filter), new_filter)
  61686. new_filter.recompile!(options)
  61687. end
  61688. chain.delete(filter)
  61689. end
  61690. target.send("_#{name}_callbacks=", chain)
  61691. end
  61692. end
  61693. # Remove all set callbacks for the given event.
  61694. def reset_callbacks(symbol)
  61695. callbacks = send("_#{symbol}_callbacks")
  61696. ActiveSupport::DescendantsTracker.descendants(self).each do |target|
  61697. chain = target.send("_#{symbol}_callbacks").dup
  61698. callbacks.each { |c| chain.delete(c) }
  61699. target.send("_#{symbol}_callbacks=", chain)
  61700. target.__reset_runner(symbol)
  61701. end
  61702. self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
  61703. __reset_runner(symbol)
  61704. end
  61705. # Define sets of events in the object lifecycle that support callbacks.
  61706. #
  61707. # define_callbacks :validate
  61708. # define_callbacks :initialize, :save, :destroy
  61709. #
  61710. # ===== Options
  61711. #
  61712. # * <tt>:terminator</tt> - Determines when a before filter will halt the
  61713. # callback chain, preventing following callbacks from being called and
  61714. # the event from being triggered. This is a string to be eval'ed. The
  61715. # result of the callback is available in the +result+ variable.
  61716. #
  61717. # define_callbacks :validate, terminator: 'result == false'
  61718. #
  61719. # In this example, if any before validate callbacks returns +false+,
  61720. # other callbacks are not executed. Defaults to +false+, meaning no value
  61721. # halts the chain.
  61722. #
  61723. # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
  61724. # callbacks should be terminated by the <tt>:terminator</tt> option. By
  61725. # default after callbacks executed no matter if callback chain was
  61726. # terminated or not. Option makes sense only when <tt>:terminator</tt>
  61727. # option is specified.
  61728. #
  61729. # * <tt>:scope</tt> - Indicates which methods should be executed when an
  61730. # object is used as a callback.
  61731. #
  61732. # class Audit
  61733. # def before(caller)
  61734. # puts 'Audit: before is called'
  61735. # end
  61736. #
  61737. # def before_save(caller)
  61738. # puts 'Audit: before_save is called'
  61739. # end
  61740. # end
  61741. #
  61742. # class Account
  61743. # include ActiveSupport::Callbacks
  61744. #
  61745. # define_callbacks :save
  61746. # set_callback :save, :before, Audit.new
  61747. #
  61748. # def save
  61749. # run_callbacks :save do
  61750. # puts 'save in main'
  61751. # end
  61752. # end
  61753. # end
  61754. #
  61755. # In the above case whenever you save an account the method
  61756. # <tt>Audit#before</tt> will be called. On the other hand
  61757. #
  61758. # define_callbacks :save, scope: [:kind, :name]
  61759. #
  61760. # would trigger <tt>Audit#before_save</tt> instead. That's constructed
  61761. # by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
  61762. # case "kind" is "before" and "name" is "save". In this context +:kind+
  61763. # and +:name+ have special meanings: +:kind+ refers to the kind of
  61764. # callback (before/after/around) and +:name+ refers to the method on
  61765. # which callbacks are being defined.
  61766. #
  61767. # A declaration like
  61768. #
  61769. # define_callbacks :save, scope: [:name]
  61770. #
  61771. # would call <tt>Audit#save</tt>.
  61772. def define_callbacks(*callbacks)
  61773. config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
  61774. callbacks.each do |callback|
  61775. class_attribute "_#{callback}_callbacks"
  61776. send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
  61777. end
  61778. end
  61779. end
  61780. end
  61781. end
  61782. module ActiveSupport
  61783. # A typical module looks like this:
  61784. #
  61785. # module M
  61786. # def self.included(base)
  61787. # base.extend ClassMethods
  61788. # base.class_eval do
  61789. # scope :disabled, -> { where(disabled: true) }
  61790. # end
  61791. # end
  61792. #
  61793. # module ClassMethods
  61794. # ...
  61795. # end
  61796. # end
  61797. #
  61798. # By using <tt>ActiveSupport::Concern</tt> the above module could instead be
  61799. # written as:
  61800. #
  61801. # require 'active_support/concern'
  61802. #
  61803. # module M
  61804. # extend ActiveSupport::Concern
  61805. #
  61806. # included do
  61807. # scope :disabled, -> { where(disabled: true) }
  61808. # end
  61809. #
  61810. # module ClassMethods
  61811. # ...
  61812. # end
  61813. # end
  61814. #
  61815. # Moreover, it gracefully handles module dependencies. Given a +Foo+ module
  61816. # and a +Bar+ module which depends on the former, we would typically write the
  61817. # following:
  61818. #
  61819. # module Foo
  61820. # def self.included(base)
  61821. # base.class_eval do
  61822. # def self.method_injected_by_foo
  61823. # ...
  61824. # end
  61825. # end
  61826. # end
  61827. # end
  61828. #
  61829. # module Bar
  61830. # def self.included(base)
  61831. # base.method_injected_by_foo
  61832. # end
  61833. # end
  61834. #
  61835. # class Host
  61836. # include Foo # We need to include this dependency for Bar
  61837. # include Bar # Bar is the module that Host really needs
  61838. # end
  61839. #
  61840. # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
  61841. # could try to hide these from +Host+ directly including +Foo+ in +Bar+:
  61842. #
  61843. # module Bar
  61844. # include Foo
  61845. # def self.included(base)
  61846. # base.method_injected_by_foo
  61847. # end
  61848. # end
  61849. #
  61850. # class Host
  61851. # include Bar
  61852. # end
  61853. #
  61854. # Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
  61855. # is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
  61856. # module dependencies are properly resolved:
  61857. #
  61858. # require 'active_support/concern'
  61859. #
  61860. # module Foo
  61861. # extend ActiveSupport::Concern
  61862. # included do
  61863. # def self.method_injected_by_foo
  61864. # ...
  61865. # end
  61866. # end
  61867. # end
  61868. #
  61869. # module Bar
  61870. # extend ActiveSupport::Concern
  61871. # include Foo
  61872. #
  61873. # included do
  61874. # self.method_injected_by_foo
  61875. # end
  61876. # end
  61877. #
  61878. # class Host
  61879. # include Bar # works, Bar takes care now of its dependencies
  61880. # end
  61881. module Concern
  61882. def self.extended(base) #:nodoc:
  61883. base.instance_variable_set("@_dependencies", [])
  61884. end
  61885. def append_features(base)
  61886. if base.instance_variable_defined?("@_dependencies")
  61887. base.instance_variable_get("@_dependencies") << self
  61888. return false
  61889. else
  61890. return false if base < self
  61891. @_dependencies.each { |dep| base.send(:include, dep) }
  61892. super
  61893. base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
  61894. base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
  61895. end
  61896. end
  61897. def included(base = nil, &block)
  61898. if base.nil?
  61899. @_included_block = block
  61900. else
  61901. super
  61902. end
  61903. end
  61904. end
  61905. end
  61906. require 'thread'
  61907. require 'monitor'
  61908. module ActiveSupport
  61909. module Concurrency
  61910. class Latch
  61911. def initialize(count = 1)
  61912. @count = count
  61913. @lock = Monitor.new
  61914. @cv = @lock.new_cond
  61915. end
  61916. def release
  61917. @lock.synchronize do
  61918. @count -= 1 if @count > 0
  61919. @cv.broadcast if @count.zero?
  61920. end
  61921. end
  61922. def await
  61923. @lock.synchronize do
  61924. @cv.wait_while { @count > 0 }
  61925. end
  61926. end
  61927. end
  61928. end
  61929. end
  61930. require 'active_support/concern'
  61931. require 'active_support/ordered_options'
  61932. require 'active_support/core_ext/array/extract_options'
  61933. module ActiveSupport
  61934. # Configurable provides a <tt>config</tt> method to store and retrieve
  61935. # configuration options as an <tt>OrderedHash</tt>.
  61936. module Configurable
  61937. extend ActiveSupport::Concern
  61938. class Configuration < ActiveSupport::InheritableOptions
  61939. def compile_methods!
  61940. self.class.compile_methods!(keys)
  61941. end
  61942. # Compiles reader methods so we don't have to go through method_missing.
  61943. def self.compile_methods!(keys)
  61944. keys.reject { |m| method_defined?(m) }.each do |key|
  61945. class_eval <<-RUBY, __FILE__, __LINE__ + 1
  61946. def #{key}; _get(#{key.inspect}); end
  61947. RUBY
  61948. end
  61949. end
  61950. end
  61951. module ClassMethods
  61952. def config
  61953. @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
  61954. superclass.config.inheritable_copy
  61955. else
  61956. # create a new "anonymous" class that will host the compiled reader methods
  61957. Class.new(Configuration).new
  61958. end
  61959. end
  61960. def configure
  61961. yield config
  61962. end
  61963. # Allows you to add shortcut so that you don't have to refer to attribute
  61964. # through config. Also look at the example for config to contrast.
  61965. #
  61966. # Defines both class and instance config accessors.
  61967. #
  61968. # class User
  61969. # include ActiveSupport::Configurable
  61970. # config_accessor :allowed_access
  61971. # end
  61972. #
  61973. # User.allowed_access # => nil
  61974. # User.allowed_access = false
  61975. # User.allowed_access # => false
  61976. #
  61977. # user = User.new
  61978. # user.allowed_access # => false
  61979. # user.allowed_access = true
  61980. # user.allowed_access # => true
  61981. #
  61982. # User.allowed_access # => false
  61983. #
  61984. # The attribute name must be a valid method name in Ruby.
  61985. #
  61986. # class User
  61987. # include ActiveSupport::Configurable
  61988. # config_accessor :"1_Badname"
  61989. # end
  61990. # # => NameError: invalid config attribute name
  61991. #
  61992. # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
  61993. # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
  61994. #
  61995. # class User
  61996. # include ActiveSupport::Configurable
  61997. # config_accessor :allowed_access, instance_reader: false, instance_writer: false
  61998. # end
  61999. #
  62000. # User.allowed_access = false
  62001. # User.allowed_access # => false
  62002. #
  62003. # User.new.allowed_access = true # => NoMethodError
  62004. # User.new.allowed_access # => NoMethodError
  62005. #
  62006. # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
  62007. #
  62008. # class User
  62009. # include ActiveSupport::Configurable
  62010. # config_accessor :allowed_access, instance_accessor: false
  62011. # end
  62012. #
  62013. # User.allowed_access = false
  62014. # User.allowed_access # => false
  62015. #
  62016. # User.new.allowed_access = true # => NoMethodError
  62017. # User.new.allowed_access # => NoMethodError
  62018. #
  62019. # Also you can pass a block to set up the attribute with a default value.
  62020. #
  62021. # class User
  62022. # include ActiveSupport::Configurable
  62023. # config_accessor :hair_colors do
  62024. # [:brown, :black, :blonde, :red]
  62025. # end
  62026. # end
  62027. #
  62028. # User.hair_colors # => [:brown, :black, :blonde, :red]
  62029. def config_accessor(*names)
  62030. options = names.extract_options!
  62031. names.each do |name|
  62032. raise NameError.new('invalid config attribute name') unless name =~ /^[_A-Za-z]\w*$/
  62033. reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
  62034. writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
  62035. singleton_class.class_eval reader, __FILE__, reader_line
  62036. singleton_class.class_eval writer, __FILE__, writer_line
  62037. unless options[:instance_accessor] == false
  62038. class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
  62039. class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
  62040. end
  62041. send("#{name}=", yield) if block_given?
  62042. end
  62043. end
  62044. end
  62045. # Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
  62046. #
  62047. # require 'active_support/configurable'
  62048. #
  62049. # class User
  62050. # include ActiveSupport::Configurable
  62051. # end
  62052. #
  62053. # user = User.new
  62054. #
  62055. # user.config.allowed_access = true
  62056. # user.config.level = 1
  62057. #
  62058. # user.config.allowed_access # => true
  62059. # user.config.level # => 1
  62060. def config
  62061. @_config ||= self.class.config.inheritable_copy
  62062. end
  62063. end
  62064. end
  62065. class Array
  62066. # Returns the tail of the array from +position+.
  62067. #
  62068. # %w( a b c d ).from(0) # => ["a", "b", "c", "d"]
  62069. # %w( a b c d ).from(2) # => ["c", "d"]
  62070. # %w( a b c d ).from(10) # => []
  62071. # %w().from(0) # => []
  62072. def from(position)
  62073. self[position, length] || []
  62074. end
  62075. # Returns the beginning of the array up to +position+.
  62076. #
  62077. # %w( a b c d ).to(0) # => ["a"]
  62078. # %w( a b c d ).to(2) # => ["a", "b", "c"]
  62079. # %w( a b c d ).to(10) # => ["a", "b", "c", "d"]
  62080. # %w().to(0) # => []
  62081. def to(position)
  62082. first position + 1
  62083. end
  62084. # Equal to <tt>self[1]</tt>.
  62085. #
  62086. # %w( a b c d e ).second # => "b"
  62087. def second
  62088. self[1]
  62089. end
  62090. # Equal to <tt>self[2]</tt>.
  62091. #
  62092. # %w( a b c d e ).third # => "c"
  62093. def third
  62094. self[2]
  62095. end
  62096. # Equal to <tt>self[3]</tt>.
  62097. #
  62098. # %w( a b c d e ).fourth # => "d"
  62099. def fourth
  62100. self[3]
  62101. end
  62102. # Equal to <tt>self[4]</tt>.
  62103. #
  62104. # %w( a b c d e ).fifth # => "e"
  62105. def fifth
  62106. self[4]
  62107. end
  62108. # Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
  62109. def forty_two
  62110. self[41]
  62111. end
  62112. end
  62113. require 'active_support/xml_mini'
  62114. require 'active_support/core_ext/hash/keys'
  62115. require 'active_support/core_ext/string/inflections'
  62116. require 'active_support/core_ext/object/to_param'
  62117. require 'active_support/core_ext/object/to_query'
  62118. class Array
  62119. # Converts the array to a comma-separated sentence where the last element is
  62120. # joined by the connector word.
  62121. #
  62122. # You can pass the following options to change the default behavior. If you
  62123. # pass an option key that doesn't exist in the list below, it will raise an
  62124. # <tt>ArgumentError</tt>.
  62125. #
  62126. # Options:
  62127. #
  62128. # * <tt>:words_connector</tt> - The sign or word used to join the elements
  62129. # in arrays with two or more elements (default: ", ").
  62130. # * <tt>:two_words_connector</tt> - The sign or word used to join the elements
  62131. # in arrays with two elements (default: " and ").
  62132. # * <tt>:last_word_connector</tt> - The sign or word used to join the last element
  62133. # in arrays with three or more elements (default: ", and ").
  62134. # * <tt>:locale</tt> - If +i18n+ is available, you can set a locale and use
  62135. # the connector options defined on the 'support.array' namespace in the
  62136. # corresponding dictionary file.
  62137. #
  62138. # [].to_sentence # => ""
  62139. # ['one'].to_sentence # => "one"
  62140. # ['one', 'two'].to_sentence # => "one and two"
  62141. # ['one', 'two', 'three'].to_sentence # => "one, two, and three"
  62142. #
  62143. # ['one', 'two'].to_sentence(passing: 'invalid option')
  62144. # # => ArgumentError: Unknown key :passing
  62145. #
  62146. # ['one', 'two'].to_sentence(two_words_connector: '-')
  62147. # # => "one-two"
  62148. #
  62149. # ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
  62150. # # => "one or two or at least three"
  62151. #
  62152. # Examples using <tt>:locale</tt> option:
  62153. #
  62154. # # Given this locale dictionary:
  62155. # #
  62156. # # es:
  62157. # # support:
  62158. # # array:
  62159. # # words_connector: " o "
  62160. # # two_words_connector: " y "
  62161. # # last_word_connector: " o al menos "
  62162. #
  62163. # ['uno', 'dos'].to_sentence(locale: :es)
  62164. # # => "uno y dos"
  62165. #
  62166. # ['uno', 'dos', 'tres'].to_sentence(locale: :es)
  62167. # # => "uno o dos o al menos tres"
  62168. def to_sentence(options = {})
  62169. options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
  62170. default_connectors = {
  62171. :words_connector => ', ',
  62172. :two_words_connector => ' and ',
  62173. :last_word_connector => ', and '
  62174. }
  62175. if defined?(I18n)
  62176. i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
  62177. default_connectors.merge!(i18n_connectors)
  62178. end
  62179. options = default_connectors.merge!(options)
  62180. case length
  62181. when 0
  62182. ''
  62183. when 1
  62184. self[0].to_s.dup
  62185. when 2
  62186. "#{self[0]}#{options[:two_words_connector]}#{self[1]}"
  62187. else
  62188. "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
  62189. end
  62190. end
  62191. # Converts a collection of elements into a formatted string by calling
  62192. # <tt>to_s</tt> on all elements and joining them. Having this model:
  62193. #
  62194. # class Blog < ActiveRecord::Base
  62195. # def to_s
  62196. # title
  62197. # end
  62198. # end
  62199. #
  62200. # Blog.all.map(&:title) #=> ["First Post", "Second Post", "Third post"]
  62201. #
  62202. # <tt>to_formatted_s</tt> shows us:
  62203. #
  62204. # Blog.all.to_formatted_s # => "First PostSecond PostThird Post"
  62205. #
  62206. # Adding in the <tt>:db</tt> argument as the format yields a comma separated
  62207. # id list:
  62208. #
  62209. # Blog.all.to_formatted_s(:db) # => "1,2,3"
  62210. def to_formatted_s(format = :default)
  62211. case format
  62212. when :db
  62213. if empty?
  62214. 'null'
  62215. else
  62216. collect { |element| element.id }.join(',')
  62217. end
  62218. else
  62219. to_default_s
  62220. end
  62221. end
  62222. alias_method :to_default_s, :to_s
  62223. alias_method :to_s, :to_formatted_s
  62224. # Returns a string that represents the array in XML by invoking +to_xml+
  62225. # on each element. Active Record collections delegate their representation
  62226. # in XML to this method.
  62227. #
  62228. # All elements are expected to respond to +to_xml+, if any of them does
  62229. # not then an exception is raised.
  62230. #
  62231. # The root node reflects the class name of the first element in plural
  62232. # if all elements belong to the same type and that's not Hash:
  62233. #
  62234. # customer.projects.to_xml
  62235. #
  62236. # <?xml version="1.0" encoding="UTF-8"?>
  62237. # <projects type="array">
  62238. # <project>
  62239. # <amount type="decimal">20000.0</amount>
  62240. # <customer-id type="integer">1567</customer-id>
  62241. # <deal-date type="date">2008-04-09</deal-date>
  62242. # ...
  62243. # </project>
  62244. # <project>
  62245. # <amount type="decimal">57230.0</amount>
  62246. # <customer-id type="integer">1567</customer-id>
  62247. # <deal-date type="date">2008-04-15</deal-date>
  62248. # ...
  62249. # </project>
  62250. # </projects>
  62251. #
  62252. # Otherwise the root element is "objects":
  62253. #
  62254. # [{ foo: 1, bar: 2}, { baz: 3}].to_xml
  62255. #
  62256. # <?xml version="1.0" encoding="UTF-8"?>
  62257. # <objects type="array">
  62258. # <object>
  62259. # <bar type="integer">2</bar>
  62260. # <foo type="integer">1</foo>
  62261. # </object>
  62262. # <object>
  62263. # <baz type="integer">3</baz>
  62264. # </object>
  62265. # </objects>
  62266. #
  62267. # If the collection is empty the root element is "nil-classes" by default:
  62268. #
  62269. # [].to_xml
  62270. #
  62271. # <?xml version="1.0" encoding="UTF-8"?>
  62272. # <nil-classes type="array"/>
  62273. #
  62274. # To ensure a meaningful root element use the <tt>:root</tt> option:
  62275. #
  62276. # customer_with_no_projects.projects.to_xml(root: 'projects')
  62277. #
  62278. # <?xml version="1.0" encoding="UTF-8"?>
  62279. # <projects type="array"/>
  62280. #
  62281. # By default name of the node for the children of root is <tt>root.singularize</tt>.
  62282. # You can change it with the <tt>:children</tt> option.
  62283. #
  62284. # The +options+ hash is passed downwards:
  62285. #
  62286. # Message.all.to_xml(skip_types: true)
  62287. #
  62288. # <?xml version="1.0" encoding="UTF-8"?>
  62289. # <messages>
  62290. # <message>
  62291. # <created-at>2008-03-07T09:58:18+01:00</created-at>
  62292. # <id>1</id>
  62293. # <name>1</name>
  62294. # <updated-at>2008-03-07T09:58:18+01:00</updated-at>
  62295. # <user-id>1</user-id>
  62296. # </message>
  62297. # </messages>
  62298. #
  62299. def to_xml(options = {})
  62300. require 'active_support/builder' unless defined?(Builder)
  62301. options = options.dup
  62302. options[:indent] ||= 2
  62303. options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
  62304. options[:root] ||= \
  62305. if first.class != Hash && all? { |e| e.is_a?(first.class) }
  62306. underscored = ActiveSupport::Inflector.underscore(first.class.name)
  62307. ActiveSupport::Inflector.pluralize(underscored).tr('/', '_')
  62308. else
  62309. 'objects'
  62310. end
  62311. builder = options[:builder]
  62312. builder.instruct! unless options.delete(:skip_instruct)
  62313. root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
  62314. children = options.delete(:children) || root.singularize
  62315. attributes = options[:skip_types] ? {} : { type: 'array' }
  62316. if empty?
  62317. builder.tag!(root, attributes)
  62318. else
  62319. builder.tag!(root, attributes) do
  62320. each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) }
  62321. yield builder if block_given?
  62322. end
  62323. end
  62324. end
  62325. end
  62326. class Hash
  62327. # By default, only instances of Hash itself are extractable.
  62328. # Subclasses of Hash may implement this method and return
  62329. # true to declare themselves as extractable. If a Hash
  62330. # is extractable, Array#extract_options! pops it from
  62331. # the Array when it is the last element of the Array.
  62332. def extractable_options?
  62333. instance_of?(Hash)
  62334. end
  62335. end
  62336. class Array
  62337. # Extracts options from a set of arguments. Removes and returns the last
  62338. # element in the array if it's a hash, otherwise returns a blank hash.
  62339. #
  62340. # def options(*args)
  62341. # args.extract_options!
  62342. # end
  62343. #
  62344. # options(1, 2) # => {}
  62345. # options(1, 2, a: :b) # => {:a=>:b}
  62346. def extract_options!
  62347. if last.is_a?(Hash) && last.extractable_options?
  62348. pop
  62349. else
  62350. {}
  62351. end
  62352. end
  62353. end
  62354. class Array
  62355. # Splits or iterates over the array in groups of size +number+,
  62356. # padding any remaining slots with +fill_with+ unless it is +false+.
  62357. #
  62358. # %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}
  62359. # ["1", "2", "3"]
  62360. # ["4", "5", "6"]
  62361. # ["7", "8", "9"]
  62362. # ["10", nil, nil]
  62363. #
  62364. # %w(1 2 3 4 5).in_groups_of(2, '&nbsp;') {|group| p group}
  62365. # ["1", "2"]
  62366. # ["3", "4"]
  62367. # ["5", "&nbsp;"]
  62368. #
  62369. # %w(1 2 3 4 5).in_groups_of(2, false) {|group| p group}
  62370. # ["1", "2"]
  62371. # ["3", "4"]
  62372. # ["5"]
  62373. def in_groups_of(number, fill_with = nil)
  62374. if fill_with == false
  62375. collection = self
  62376. else
  62377. # size % number gives how many extra we have;
  62378. # subtracting from number gives how many to add;
  62379. # modulo number ensures we don't add group of just fill.
  62380. padding = (number - size % number) % number
  62381. collection = dup.concat([fill_with] * padding)
  62382. end
  62383. if block_given?
  62384. collection.each_slice(number) { |slice| yield(slice) }
  62385. else
  62386. groups = []
  62387. collection.each_slice(number) { |group| groups << group }
  62388. groups
  62389. end
  62390. end
  62391. # Splits or iterates over the array in +number+ of groups, padding any
  62392. # remaining slots with +fill_with+ unless it is +false+.
  62393. #
  62394. # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
  62395. # ["1", "2", "3", "4"]
  62396. # ["5", "6", "7", nil]
  62397. # ["8", "9", "10", nil]
  62398. #
  62399. # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3, '&nbsp;') {|group| p group}
  62400. # ["1", "2", "3", "4"]
  62401. # ["5", "6", "7", "&nbsp;"]
  62402. # ["8", "9", "10", "&nbsp;"]
  62403. #
  62404. # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
  62405. # ["1", "2", "3"]
  62406. # ["4", "5"]
  62407. # ["6", "7"]
  62408. def in_groups(number, fill_with = nil)
  62409. # size / number gives minor group size;
  62410. # size % number gives how many objects need extra accommodation;
  62411. # each group hold either division or division + 1 items.
  62412. division = size.div number
  62413. modulo = size % number
  62414. # create a new array avoiding dup
  62415. groups = []
  62416. start = 0
  62417. number.times do |index|
  62418. length = division + (modulo > 0 && modulo > index ? 1 : 0)
  62419. groups << last_group = slice(start, length)
  62420. last_group << fill_with if fill_with != false &&
  62421. modulo > 0 && length == division
  62422. start += length
  62423. end
  62424. if block_given?
  62425. groups.each { |g| yield(g) }
  62426. else
  62427. groups
  62428. end
  62429. end
  62430. # Divides the array into one or more subarrays based on a delimiting +value+
  62431. # or the result of an optional block.
  62432. #
  62433. # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
  62434. # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
  62435. def split(value = nil, &block)
  62436. inject([[]]) do |results, element|
  62437. if block && block.call(element) || value == element
  62438. results << []
  62439. else
  62440. results.last << element
  62441. end
  62442. results
  62443. end
  62444. end
  62445. end
  62446. class Array
  62447. # The human way of thinking about adding stuff to the end of a list is with append
  62448. alias_method :append, :<<
  62449. # The human way of thinking about adding stuff to the beginning of a list is with prepend
  62450. alias_method :prepend, :unshift
  62451. endclass Array
  62452. # *DEPRECATED*: Use +Array#uniq+ instead.
  62453. #
  62454. # Returns a unique array based on the criteria in the block.
  62455. #
  62456. # [1, 2, 3, 4].uniq_by { |i| i.odd? } # => [1, 2]
  62457. def uniq_by(&block)
  62458. ActiveSupport::Deprecation.warn 'uniq_by is deprecated. Use Array#uniq instead'
  62459. uniq(&block)
  62460. end
  62461. # *DEPRECATED*: Use +Array#uniq!+ instead.
  62462. #
  62463. # Same as +uniq_by+, but modifies +self+.
  62464. def uniq_by!(&block)
  62465. ActiveSupport::Deprecation.warn 'uniq_by! is deprecated. Use Array#uniq! instead'
  62466. uniq!(&block)
  62467. end
  62468. end
  62469. class Array
  62470. # Wraps its argument in an array unless it is already an array (or array-like).
  62471. #
  62472. # Specifically:
  62473. #
  62474. # * If the argument is +nil+ an empty list is returned.
  62475. # * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
  62476. # * Otherwise, returns an array with the argument as its single element.
  62477. #
  62478. # Array.wrap(nil) # => []
  62479. # Array.wrap([1, 2, 3]) # => [1, 2, 3]
  62480. # Array.wrap(0) # => [0]
  62481. #
  62482. # This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences:
  62483. #
  62484. # * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
  62485. # moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns
  62486. # such a +nil+ right away.
  62487. # * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt>
  62488. # raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value.
  62489. # * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array.
  62490. #
  62491. # The last point is particularly worth comparing for some enumerables:
  62492. #
  62493. # Array(foo: :bar) # => [[:foo, :bar]]
  62494. # Array.wrap(foo: :bar) # => [{:foo=>:bar}]
  62495. #
  62496. # There's also a related idiom that uses the splat operator:
  62497. #
  62498. # [*object]
  62499. #
  62500. # which for +nil+ returns <tt>[]</tt>, and calls to <tt>Array(object)</tt> otherwise.
  62501. #
  62502. # Thus, in this case the behavior may be different for +nil+, and the differences with
  62503. # <tt>Kernel#Array</tt> explained above apply to the rest of <tt>object</tt>s.
  62504. def self.wrap(object)
  62505. if object.nil?
  62506. []
  62507. elsif object.respond_to?(:to_ary)
  62508. object.to_ary || [object]
  62509. else
  62510. [object]
  62511. end
  62512. end
  62513. end
  62514. require 'active_support/core_ext/array/wrap'
  62515. require 'active_support/core_ext/array/access'
  62516. require 'active_support/core_ext/array/uniq_by'
  62517. require 'active_support/core_ext/array/conversions'
  62518. require 'active_support/core_ext/array/extract_options'
  62519. require 'active_support/core_ext/array/grouping'
  62520. require 'active_support/core_ext/array/prepend_and_append'
  62521. require 'benchmark'
  62522. class << Benchmark
  62523. def ms
  62524. 1000 * realtime { yield }
  62525. end
  62526. end
  62527. require 'bigdecimal'
  62528. require 'yaml'
  62529. class BigDecimal
  62530. YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
  62531. def encode_with(coder)
  62532. string = to_s
  62533. coder.represent_scalar(nil, YAML_MAPPING[string] || string)
  62534. end
  62535. # Backport this method if it doesn't exist
  62536. unless method_defined?(:to_d)
  62537. def to_d
  62538. self
  62539. end
  62540. end
  62541. DEFAULT_STRING_FORMAT = 'F'
  62542. def to_formatted_s(*args)
  62543. if args[0].is_a?(Symbol)
  62544. super
  62545. else
  62546. format = args[0] || DEFAULT_STRING_FORMAT
  62547. _original_to_s(format)
  62548. end
  62549. end
  62550. alias_method :_original_to_s, :to_s
  62551. alias_method :to_s, :to_formatted_s
  62552. end
  62553. require 'active_support/core_ext/big_decimal/conversions'
  62554. require 'active_support/core_ext/kernel/singleton_class'
  62555. require 'active_support/core_ext/module/remove_method'
  62556. require 'active_support/core_ext/array/extract_options'
  62557. class Class
  62558. # Declare a class-level attribute whose value is inheritable by subclasses.
  62559. # Subclasses can change their own value and it will not impact parent class.
  62560. #
  62561. # class Base
  62562. # class_attribute :setting
  62563. # end
  62564. #
  62565. # class Subclass < Base
  62566. # end
  62567. #
  62568. # Base.setting = true
  62569. # Subclass.setting # => true
  62570. # Subclass.setting = false
  62571. # Subclass.setting # => false
  62572. # Base.setting # => true
  62573. #
  62574. # In the above case as long as Subclass does not assign a value to setting
  62575. # by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
  62576. # would read value assigned to parent class. Once Subclass assigns a value then
  62577. # the value assigned by Subclass would be returned.
  62578. #
  62579. # This matches normal Ruby method inheritance: think of writing an attribute
  62580. # on a subclass as overriding the reader method. However, you need to be aware
  62581. # when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
  62582. # In such cases, you don't want to do changes in places but use setters:
  62583. #
  62584. # Base.setting = []
  62585. # Base.setting # => []
  62586. # Subclass.setting # => []
  62587. #
  62588. # # Appending in child changes both parent and child because it is the same object:
  62589. # Subclass.setting << :foo
  62590. # Base.setting # => [:foo]
  62591. # Subclass.setting # => [:foo]
  62592. #
  62593. # # Use setters to not propagate changes:
  62594. # Base.setting = []
  62595. # Subclass.setting += [:foo]
  62596. # Base.setting # => []
  62597. # Subclass.setting # => [:foo]
  62598. #
  62599. # For convenience, a query method is defined as well:
  62600. #
  62601. # Subclass.setting? # => false
  62602. #
  62603. # Instances may overwrite the class value in the same way:
  62604. #
  62605. # Base.setting = true
  62606. # object = Base.new
  62607. # object.setting # => true
  62608. # object.setting = false
  62609. # object.setting # => false
  62610. # Base.setting # => true
  62611. #
  62612. # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
  62613. #
  62614. # object.setting # => NoMethodError
  62615. # object.setting? # => NoMethodError
  62616. #
  62617. # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
  62618. #
  62619. # object.setting = false # => NoMethodError
  62620. #
  62621. # To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
  62622. def class_attribute(*attrs)
  62623. options = attrs.extract_options!
  62624. # double assignment is used to avoid "assigned but unused variable" warning
  62625. instance_reader = instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
  62626. instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
  62627. # We use class_eval here rather than define_method because class_attribute
  62628. # may be used in a performance sensitive context therefore the overhead that
  62629. # define_method introduces may become significant.
  62630. attrs.each do |name|
  62631. class_eval <<-RUBY, __FILE__, __LINE__ + 1
  62632. def self.#{name}() nil end
  62633. def self.#{name}?() !!#{name} end
  62634. def self.#{name}=(val)
  62635. singleton_class.class_eval do
  62636. remove_possible_method(:#{name})
  62637. define_method(:#{name}) { val }
  62638. end
  62639. if singleton_class?
  62640. class_eval do
  62641. remove_possible_method(:#{name})
  62642. def #{name}
  62643. defined?(@#{name}) ? @#{name} : singleton_class.#{name}
  62644. end
  62645. end
  62646. end
  62647. val
  62648. end
  62649. if instance_reader
  62650. remove_possible_method :#{name}
  62651. def #{name}
  62652. defined?(@#{name}) ? @#{name} : self.class.#{name}
  62653. end
  62654. def #{name}?
  62655. !!#{name}
  62656. end
  62657. end
  62658. RUBY
  62659. attr_writer name if instance_writer
  62660. end
  62661. end
  62662. private
  62663. def singleton_class?
  62664. ancestors.first != self
  62665. end
  62666. end
  62667. require 'active_support/core_ext/array/extract_options'
  62668. # Extends the class object with class and instance accessors for class attributes,
  62669. # just like the native attr* accessors for instance attributes.
  62670. class Class
  62671. # Defines a class attribute if it's not defined and creates a reader method that
  62672. # returns the attribute value.
  62673. #
  62674. # class Person
  62675. # cattr_reader :hair_colors
  62676. # end
  62677. #
  62678. # Person.class_variable_set("@@hair_colors", [:brown, :black])
  62679. # Person.hair_colors # => [:brown, :black]
  62680. # Person.new.hair_colors # => [:brown, :black]
  62681. #
  62682. # The attribute name must be a valid method name in Ruby.
  62683. #
  62684. # class Person
  62685. # cattr_reader :"1_Badname "
  62686. # end
  62687. # # => NameError: invalid attribute name
  62688. #
  62689. # If you want to opt out the instance reader method, you can pass <tt>instance_reader: false</tt>
  62690. # or <tt>instance_accessor: false</tt>.
  62691. #
  62692. # class Person
  62693. # cattr_reader :hair_colors, instance_reader: false
  62694. # end
  62695. #
  62696. # Person.new.hair_colors # => NoMethodError
  62697. def cattr_reader(*syms)
  62698. options = syms.extract_options!
  62699. syms.each do |sym|
  62700. raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
  62701. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  62702. unless defined? @@#{sym}
  62703. @@#{sym} = nil
  62704. end
  62705. def self.#{sym}
  62706. @@#{sym}
  62707. end
  62708. EOS
  62709. unless options[:instance_reader] == false || options[:instance_accessor] == false
  62710. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  62711. def #{sym}
  62712. @@#{sym}
  62713. end
  62714. EOS
  62715. end
  62716. end
  62717. end
  62718. # Defines a class attribute if it's not defined and creates a writer method to allow
  62719. # assignment to the attribute.
  62720. #
  62721. # class Person
  62722. # cattr_writer :hair_colors
  62723. # end
  62724. #
  62725. # Person.hair_colors = [:brown, :black]
  62726. # Person.class_variable_get("@@hair_colors") # => [:brown, :black]
  62727. # Person.new.hair_colors = [:blonde, :red]
  62728. # Person.class_variable_get("@@hair_colors") # => [:blonde, :red]
  62729. #
  62730. # The attribute name must be a valid method name in Ruby.
  62731. #
  62732. # class Person
  62733. # cattr_writer :"1_Badname "
  62734. # end
  62735. # # => NameError: invalid attribute name
  62736. #
  62737. # If you want to opt out the instance writer method, pass <tt>instance_writer: false</tt>
  62738. # or <tt>instance_accessor: false</tt>.
  62739. #
  62740. # class Person
  62741. # cattr_writer :hair_colors, instance_writer: false
  62742. # end
  62743. #
  62744. # Person.new.hair_colors = [:blonde, :red] # => NoMethodError
  62745. #
  62746. # Also, you can pass a block to set up the attribute with a default value.
  62747. #
  62748. # class Person
  62749. # cattr_writer :hair_colors do
  62750. # [:brown, :black, :blonde, :red]
  62751. # end
  62752. # end
  62753. #
  62754. # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
  62755. def cattr_writer(*syms)
  62756. options = syms.extract_options!
  62757. syms.each do |sym|
  62758. raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
  62759. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  62760. unless defined? @@#{sym}
  62761. @@#{sym} = nil
  62762. end
  62763. def self.#{sym}=(obj)
  62764. @@#{sym} = obj
  62765. end
  62766. EOS
  62767. unless options[:instance_writer] == false || options[:instance_accessor] == false
  62768. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  62769. def #{sym}=(obj)
  62770. @@#{sym} = obj
  62771. end
  62772. EOS
  62773. end
  62774. send("#{sym}=", yield) if block_given?
  62775. end
  62776. end
  62777. # Defines both class and instance accessors for class attributes.
  62778. #
  62779. # class Person
  62780. # cattr_accessor :hair_colors
  62781. # end
  62782. #
  62783. # Person.hair_colors = [:brown, :black, :blonde, :red]
  62784. # Person.hair_colors # => [:brown, :black, :blonde, :red]
  62785. # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
  62786. #
  62787. # If a subclass changes the value then that would also change the value for
  62788. # parent class. Similarly if parent class changes the value then that would
  62789. # change the value of subclasses too.
  62790. #
  62791. # class Male < Person
  62792. # end
  62793. #
  62794. # Male.hair_colors << :blue
  62795. # Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
  62796. #
  62797. # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
  62798. # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
  62799. #
  62800. # class Person
  62801. # cattr_accessor :hair_colors, instance_writer: false, instance_reader: false
  62802. # end
  62803. #
  62804. # Person.new.hair_colors = [:brown] # => NoMethodError
  62805. # Person.new.hair_colors # => NoMethodError
  62806. #
  62807. # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
  62808. #
  62809. # class Person
  62810. # cattr_accessor :hair_colors, instance_accessor: false
  62811. # end
  62812. #
  62813. # Person.new.hair_colors = [:brown] # => NoMethodError
  62814. # Person.new.hair_colors # => NoMethodError
  62815. #
  62816. # Also you can pass a block to set up the attribute with a default value.
  62817. #
  62818. # class Person
  62819. # cattr_accessor :hair_colors do
  62820. # [:brown, :black, :blonde, :red]
  62821. # end
  62822. # end
  62823. #
  62824. # Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
  62825. def cattr_accessor(*syms, &blk)
  62826. cattr_reader(*syms)
  62827. cattr_writer(*syms, &blk)
  62828. end
  62829. end
  62830. require 'active_support/core_ext/kernel/singleton_class'
  62831. require 'active_support/core_ext/module/remove_method'
  62832. class Class
  62833. def superclass_delegating_accessor(name, options = {})
  62834. # Create private _name and _name= methods that can still be used if the public
  62835. # methods are overridden. This allows
  62836. _superclass_delegating_accessor("_#{name}")
  62837. # Generate the public methods name, name=, and name?
  62838. # These methods dispatch to the private _name, and _name= methods, making them
  62839. # overridable
  62840. singleton_class.send(:define_method, name) { send("_#{name}") }
  62841. singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
  62842. singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
  62843. # If an instance_reader is needed, generate methods for name and name= on the
  62844. # class itself, so instances will be able to see them
  62845. define_method(name) { send("_#{name}") } if options[:instance_reader] != false
  62846. define_method("#{name}?") { !!send("#{name}") } if options[:instance_reader] != false
  62847. end
  62848. private
  62849. # Take the object being set and store it in a method. This gives us automatic
  62850. # inheritance behavior, without having to store the object in an instance
  62851. # variable and look up the superclass chain manually.
  62852. def _stash_object_in_method(object, method, instance_reader = true)
  62853. singleton_class.remove_possible_method(method)
  62854. singleton_class.send(:define_method, method) { object }
  62855. remove_possible_method(method)
  62856. define_method(method) { object } if instance_reader
  62857. end
  62858. def _superclass_delegating_accessor(name, options = {})
  62859. singleton_class.send(:define_method, "#{name}=") do |value|
  62860. _stash_object_in_method(value, name, options[:instance_reader] != false)
  62861. end
  62862. send("#{name}=", nil)
  62863. end
  62864. end
  62865. require 'active_support/core_ext/module/anonymous'
  62866. require 'active_support/core_ext/module/reachable'
  62867. class Class
  62868. begin
  62869. ObjectSpace.each_object(Class.new) {}
  62870. def descendants # :nodoc:
  62871. descendants = []
  62872. ObjectSpace.each_object(singleton_class) do |k|
  62873. descendants.unshift k unless k == self
  62874. end
  62875. descendants
  62876. end
  62877. rescue StandardError # JRuby
  62878. def descendants # :nodoc:
  62879. descendants = []
  62880. ObjectSpace.each_object(Class) do |k|
  62881. descendants.unshift k if k < self
  62882. end
  62883. descendants.uniq!
  62884. descendants
  62885. end
  62886. end
  62887. # Returns an array with the direct children of +self+.
  62888. #
  62889. # Integer.subclasses # => [Fixnum, Bignum]
  62890. #
  62891. # class Foo; end
  62892. # class Bar < Foo; end
  62893. # class Baz < Bar; end
  62894. #
  62895. # Foo.subclasses # => [Bar]
  62896. def subclasses
  62897. subclasses, chain = [], descendants
  62898. chain.each do |k|
  62899. subclasses << k unless chain.any? { |c| c > k }
  62900. end
  62901. subclasses
  62902. end
  62903. end
  62904. require 'active_support/core_ext/class/attribute'
  62905. require 'active_support/core_ext/class/attribute_accessors'
  62906. require 'active_support/core_ext/class/delegating_attributes'
  62907. require 'active_support/core_ext/class/subclasses'
  62908. require 'active_support/core_ext/object/acts_like'
  62909. class Date
  62910. # Duck-types as a Date-like class. See Object#acts_like?.
  62911. def acts_like_date?
  62912. true
  62913. end
  62914. end
  62915. require 'date'
  62916. require 'active_support/duration'
  62917. require 'active_support/core_ext/object/acts_like'
  62918. require 'active_support/core_ext/date/zones'
  62919. require 'active_support/core_ext/time/zones'
  62920. require 'active_support/core_ext/date_and_time/calculations'
  62921. class Date
  62922. include DateAndTime::Calculations
  62923. class << self
  62924. attr_accessor :beginning_of_week_default
  62925. # Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
  62926. # If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
  62927. # If no config.beginning_of_week was specified, returns :monday.
  62928. def beginning_of_week
  62929. Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
  62930. end
  62931. # Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
  62932. #
  62933. # This method accepts any of the following day symbols:
  62934. # :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
  62935. def beginning_of_week=(week_start)
  62936. Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
  62937. end
  62938. # Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
  62939. def find_beginning_of_week!(week_start)
  62940. raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
  62941. week_start
  62942. end
  62943. # Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
  62944. def yesterday
  62945. ::Date.current.yesterday
  62946. end
  62947. # Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
  62948. def tomorrow
  62949. ::Date.current.tomorrow
  62950. end
  62951. # Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
  62952. def current
  62953. ::Time.zone ? ::Time.zone.today : ::Date.today
  62954. end
  62955. end
  62956. # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
  62957. # and then subtracts the specified number of seconds.
  62958. def ago(seconds)
  62959. in_time_zone.since(-seconds)
  62960. end
  62961. # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
  62962. # and then adds the specified number of seconds
  62963. def since(seconds)
  62964. in_time_zone.since(seconds)
  62965. end
  62966. alias :in :since
  62967. # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
  62968. def beginning_of_day
  62969. in_time_zone
  62970. end
  62971. alias :midnight :beginning_of_day
  62972. alias :at_midnight :beginning_of_day
  62973. alias :at_beginning_of_day :beginning_of_day
  62974. # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
  62975. def end_of_day
  62976. in_time_zone.end_of_day
  62977. end
  62978. alias :at_end_of_day :end_of_day
  62979. def plus_with_duration(other) #:nodoc:
  62980. if ActiveSupport::Duration === other
  62981. other.since(self)
  62982. else
  62983. plus_without_duration(other)
  62984. end
  62985. end
  62986. alias_method :plus_without_duration, :+
  62987. alias_method :+, :plus_with_duration
  62988. def minus_with_duration(other) #:nodoc:
  62989. if ActiveSupport::Duration === other
  62990. plus_with_duration(-other)
  62991. else
  62992. minus_without_duration(other)
  62993. end
  62994. end
  62995. alias_method :minus_without_duration, :-
  62996. alias_method :-, :minus_with_duration
  62997. # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
  62998. # any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
  62999. def advance(options)
  63000. options = options.dup
  63001. d = self
  63002. d = d >> options.delete(:years) * 12 if options[:years]
  63003. d = d >> options.delete(:months) if options[:months]
  63004. d = d + options.delete(:weeks) * 7 if options[:weeks]
  63005. d = d + options.delete(:days) if options[:days]
  63006. d
  63007. end
  63008. # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
  63009. # The +options+ parameter is a hash with a combination of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>.
  63010. #
  63011. # Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1)
  63012. # Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12)
  63013. def change(options)
  63014. ::Date.new(
  63015. options.fetch(:year, year),
  63016. options.fetch(:month, month),
  63017. options.fetch(:day, day)
  63018. )
  63019. end
  63020. end
  63021. require 'date'
  63022. require 'active_support/inflector/methods'
  63023. require 'active_support/core_ext/date/zones'
  63024. require 'active_support/core_ext/module/remove_method'
  63025. class Date
  63026. DATE_FORMATS = {
  63027. :short => '%e %b',
  63028. :long => '%B %e, %Y',
  63029. :db => '%Y-%m-%d',
  63030. :number => '%Y%m%d',
  63031. :long_ordinal => lambda { |date|
  63032. day_format = ActiveSupport::Inflector.ordinalize(date.day)
  63033. date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
  63034. },
  63035. :rfc822 => '%e %b %Y'
  63036. }
  63037. # Ruby 1.9 has Date#to_time which converts to localtime only.
  63038. remove_method :to_time
  63039. # Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
  63040. remove_method :xmlschema
  63041. # Convert to a formatted string. See DATE_FORMATS for predefined formats.
  63042. #
  63043. # This method is aliased to <tt>to_s</tt>.
  63044. #
  63045. # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
  63046. #
  63047. # date.to_formatted_s(:db) # => "2007-11-10"
  63048. # date.to_s(:db) # => "2007-11-10"
  63049. #
  63050. # date.to_formatted_s(:short) # => "10 Nov"
  63051. # date.to_formatted_s(:long) # => "November 10, 2007"
  63052. # date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
  63053. # date.to_formatted_s(:rfc822) # => "10 Nov 2007"
  63054. #
  63055. # == Adding your own time formats to to_formatted_s
  63056. # You can add your own formats to the Date::DATE_FORMATS hash.
  63057. # Use the format name as the hash key and either a strftime string
  63058. # or Proc instance that takes a date argument as the value.
  63059. #
  63060. # # config/initializers/time_formats.rb
  63061. # Date::DATE_FORMATS[:month_and_year] = '%B %Y'
  63062. # Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
  63063. def to_formatted_s(format = :default)
  63064. if formatter = DATE_FORMATS[format]
  63065. if formatter.respond_to?(:call)
  63066. formatter.call(self).to_s
  63067. else
  63068. strftime(formatter)
  63069. end
  63070. else
  63071. to_default_s
  63072. end
  63073. end
  63074. alias_method :to_default_s, :to_s
  63075. alias_method :to_s, :to_formatted_s
  63076. # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
  63077. def readable_inspect
  63078. strftime('%a, %d %b %Y')
  63079. end
  63080. alias_method :default_inspect, :inspect
  63081. alias_method :inspect, :readable_inspect
  63082. # Converts a Date instance to a Time, where the time is set to the beginning of the day.
  63083. # The timezone can be either :local or :utc (default :local).
  63084. #
  63085. # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
  63086. #
  63087. # date.to_time # => Sat Nov 10 00:00:00 0800 2007
  63088. # date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007
  63089. #
  63090. # date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007
  63091. def to_time(form = :local)
  63092. ::Time.send(form, year, month, day)
  63093. end
  63094. def xmlschema
  63095. in_time_zone.xmlschema
  63096. end
  63097. end
  63098. require 'active_support/core_ext/infinite_comparable'
  63099. class Date
  63100. include InfiniteComparable
  63101. end
  63102. require 'date'
  63103. require 'active_support/core_ext/time/zones'
  63104. class Date
  63105. # *DEPRECATED*: Use +Date#in_time_zone+ instead.
  63106. #
  63107. # Converts Date to a TimeWithZone in the current zone if <tt>Time.zone</tt> or
  63108. # <tt>Time.zone_default</tt> is set, otherwise converts Date to a Time via
  63109. # Date#to_time.
  63110. def to_time_in_current_zone
  63111. ActiveSupport::Deprecation.warn 'Date#to_time_in_current_zone is deprecated. Use Date#in_time_zone instead', caller
  63112. if ::Time.zone
  63113. ::Time.zone.local(year, month, day)
  63114. else
  63115. to_time
  63116. end
  63117. end
  63118. # Converts Date to a TimeWithZone in the current zone if Time.zone or Time.zone_default
  63119. # is set, otherwise converts Date to a Time via Date#to_time
  63120. #
  63121. # Time.zone = 'Hawaii' # => 'Hawaii'
  63122. # Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00
  63123. #
  63124. # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
  63125. # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
  63126. #
  63127. # Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00
  63128. def in_time_zone(zone = ::Time.zone)
  63129. if zone
  63130. ::Time.find_zone!(zone).local(year, month, day)
  63131. else
  63132. to_time
  63133. end
  63134. end
  63135. end
  63136. require 'active_support/core_ext/date/acts_like'
  63137. require 'active_support/core_ext/date/calculations'
  63138. require 'active_support/core_ext/date/conversions'
  63139. require 'active_support/core_ext/date/zones'
  63140. require 'active_support/core_ext/date/infinite_comparable'
  63141. module DateAndTime
  63142. module Calculations
  63143. DAYS_INTO_WEEK = {
  63144. :monday => 0,
  63145. :tuesday => 1,
  63146. :wednesday => 2,
  63147. :thursday => 3,
  63148. :friday => 4,
  63149. :saturday => 5,
  63150. :sunday => 6
  63151. }
  63152. # Returns a new date/time representing yesterday.
  63153. def yesterday
  63154. advance(:days => -1)
  63155. end
  63156. # Returns a new date/time representing tomorrow.
  63157. def tomorrow
  63158. advance(:days => 1)
  63159. end
  63160. # Returns true if the date/time is today.
  63161. def today?
  63162. to_date == ::Date.current
  63163. end
  63164. # Returns true if the date/time is in the past.
  63165. def past?
  63166. self < self.class.current
  63167. end
  63168. # Returns true if the date/time is in the future.
  63169. def future?
  63170. self > self.class.current
  63171. end
  63172. # Returns a new date/time the specified number of days ago.
  63173. def days_ago(days)
  63174. advance(:days => -days)
  63175. end
  63176. # Returns a new date/time the specified number of days in the future.
  63177. def days_since(days)
  63178. advance(:days => days)
  63179. end
  63180. # Returns a new date/time the specified number of weeks ago.
  63181. def weeks_ago(weeks)
  63182. advance(:weeks => -weeks)
  63183. end
  63184. # Returns a new date/time the specified number of weeks in the future.
  63185. def weeks_since(weeks)
  63186. advance(:weeks => weeks)
  63187. end
  63188. # Returns a new date/time the specified number of months ago.
  63189. def months_ago(months)
  63190. advance(:months => -months)
  63191. end
  63192. # Returns a new date/time the specified number of months in the future.
  63193. def months_since(months)
  63194. advance(:months => months)
  63195. end
  63196. # Returns a new date/time the specified number of years ago.
  63197. def years_ago(years)
  63198. advance(:years => -years)
  63199. end
  63200. # Returns a new date/time the specified number of years in the future.
  63201. def years_since(years)
  63202. advance(:years => years)
  63203. end
  63204. # Returns a new date/time at the start of the month.
  63205. # DateTime objects will have a time set to 0:00.
  63206. def beginning_of_month
  63207. first_hour{ change(:day => 1) }
  63208. end
  63209. alias :at_beginning_of_month :beginning_of_month
  63210. # Returns a new date/time at the start of the quarter.
  63211. # Example: 1st January, 1st July, 1st October.
  63212. # DateTime objects will have a time set to 0:00.
  63213. def beginning_of_quarter
  63214. first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
  63215. beginning_of_month.change(:month => first_quarter_month)
  63216. end
  63217. alias :at_beginning_of_quarter :beginning_of_quarter
  63218. # Returns a new date/time at the end of the quarter.
  63219. # Example: 31st March, 30th June, 30th September.
  63220. # DateTIme objects will have a time set to 23:59:59.
  63221. def end_of_quarter
  63222. last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
  63223. beginning_of_month.change(:month => last_quarter_month).end_of_month
  63224. end
  63225. alias :at_end_of_quarter :end_of_quarter
  63226. # Return a new date/time at the beginning of the year.
  63227. # Example: 1st January.
  63228. # DateTime objects will have a time set to 0:00.
  63229. def beginning_of_year
  63230. change(:month => 1).beginning_of_month
  63231. end
  63232. alias :at_beginning_of_year :beginning_of_year
  63233. # Returns a new date/time representing the given day in the next week.
  63234. # Week is assumed to start on +start_day+, default is
  63235. # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
  63236. # DateTime objects have their time set to 0:00.
  63237. def next_week(start_day = Date.beginning_of_week)
  63238. first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(start_day)) }
  63239. end
  63240. # Short-hand for months_since(1).
  63241. def next_month
  63242. months_since(1)
  63243. end
  63244. # Short-hand for months_since(3)
  63245. def next_quarter
  63246. months_since(3)
  63247. end
  63248. # Short-hand for years_since(1).
  63249. def next_year
  63250. years_since(1)
  63251. end
  63252. # Returns a new date/time representing the given day in the previous week.
  63253. # Week is assumed to start on +start_day+, default is
  63254. # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
  63255. # DateTime objects have their time set to 0:00.
  63256. def prev_week(start_day = Date.beginning_of_week)
  63257. first_hour{ weeks_ago(1).beginning_of_week.days_since(days_span(start_day)) }
  63258. end
  63259. alias_method :last_week, :prev_week
  63260. # Short-hand for months_ago(1).
  63261. def prev_month
  63262. months_ago(1)
  63263. end
  63264. alias_method :last_month, :prev_month
  63265. # Short-hand for months_ago(3).
  63266. def prev_quarter
  63267. months_ago(3)
  63268. end
  63269. alias_method :last_quarter, :prev_quarter
  63270. # Short-hand for years_ago(1).
  63271. def prev_year
  63272. years_ago(1)
  63273. end
  63274. alias_method :last_year, :prev_year
  63275. # Returns the number of days to the start of the week on the given day.
  63276. # Week is assumed to start on +start_day+, default is
  63277. # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
  63278. def days_to_week_start(start_day = Date.beginning_of_week)
  63279. start_day_number = DAYS_INTO_WEEK[start_day]
  63280. current_day_number = wday != 0 ? wday - 1 : 6
  63281. (current_day_number - start_day_number) % 7
  63282. end
  63283. # Returns a new date/time representing the start of this week on the given day.
  63284. # Week is assumed to start on +start_day+, default is
  63285. # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
  63286. # +DateTime+ objects have their time set to 0:00.
  63287. def beginning_of_week(start_day = Date.beginning_of_week)
  63288. result = days_ago(days_to_week_start(start_day))
  63289. acts_like?(:time) ? result.midnight : result
  63290. end
  63291. alias :at_beginning_of_week :beginning_of_week
  63292. # Returns Monday of this week assuming that week starts on Monday.
  63293. # +DateTime+ objects have their time set to 0:00.
  63294. def monday
  63295. beginning_of_week(:monday)
  63296. end
  63297. # Returns a new date/time representing the end of this week on the given day.
  63298. # Week is assumed to start on +start_day+, default is
  63299. # +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
  63300. # DateTime objects have their time set to 23:59:59.
  63301. def end_of_week(start_day = Date.beginning_of_week)
  63302. last_hour{ days_since(6 - days_to_week_start(start_day)) }
  63303. end
  63304. alias :at_end_of_week :end_of_week
  63305. # Returns Sunday of this week assuming that week starts on Monday.
  63306. # +DateTime+ objects have their time set to 23:59:59.
  63307. def sunday
  63308. end_of_week(:monday)
  63309. end
  63310. # Returns a new date/time representing the end of the month.
  63311. # DateTime objects will have a time set to 23:59:59.
  63312. def end_of_month
  63313. last_day = ::Time.days_in_month(month, year)
  63314. last_hour{ days_since(last_day - day) }
  63315. end
  63316. alias :at_end_of_month :end_of_month
  63317. # Returns a new date/time representing the end of the year.
  63318. # DateTime objects will have a time set to 23:59:59.
  63319. def end_of_year
  63320. change(:month => 12).end_of_month
  63321. end
  63322. alias :at_end_of_year :end_of_year
  63323. private
  63324. def first_hour
  63325. result = yield
  63326. acts_like?(:time) ? result.change(:hour => 0) : result
  63327. end
  63328. def last_hour
  63329. result = yield
  63330. acts_like?(:time) ? result.end_of_day : result
  63331. end
  63332. def days_span(day)
  63333. (DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
  63334. end
  63335. end
  63336. end
  63337. require 'active_support/core_ext/object/acts_like'
  63338. class DateTime
  63339. # Duck-types as a Date-like class. See Object#acts_like?.
  63340. def acts_like_date?
  63341. true
  63342. end
  63343. # Duck-types as a Time-like class. See Object#acts_like?.
  63344. def acts_like_time?
  63345. true
  63346. end
  63347. end
  63348. require 'active_support/deprecation'
  63349. class DateTime
  63350. class << self
  63351. # *DEPRECATED*: Use +DateTime.civil_from_format+ directly.
  63352. def local_offset
  63353. ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. Use DateTime.civil_from_format directly.'
  63354. ::Time.local(2012).utc_offset.to_r / 86400
  63355. end
  63356. # Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or
  63357. # <tt>config.time_zone</tt> are set, otherwise returns
  63358. # <tt>Time.now.to_datetime</tt>.
  63359. def current
  63360. ::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
  63361. end
  63362. end
  63363. # Tells whether the DateTime object's datetime lies in the past.
  63364. def past?
  63365. self < ::DateTime.current
  63366. end
  63367. # Tells whether the DateTime object's datetime lies in the future.
  63368. def future?
  63369. self > ::DateTime.current
  63370. end
  63371. # Seconds since midnight: DateTime.now.seconds_since_midnight.
  63372. def seconds_since_midnight
  63373. sec + (min * 60) + (hour * 3600)
  63374. end
  63375. # Returns the number of seconds until 23:59:59.
  63376. #
  63377. # DateTime.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
  63378. # DateTime.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
  63379. # DateTime.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
  63380. def seconds_until_end_of_day
  63381. end_of_day.to_i - to_i
  63382. end
  63383. # Returns a new DateTime where one or more of the elements have been changed
  63384. # according to the +options+ parameter. The time options (<tt>:hour</tt>,
  63385. # <tt>:min</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
  63386. # passed, then minute and sec is set to 0. If the hour and minute is passed,
  63387. # then sec is set to 0. The +options+ parameter takes a hash with any of these
  63388. # keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>,
  63389. # <tt>:min</tt>, <tt>:sec</tt>, <tt>:offset</tt>, <tt>:start</tt>.
  63390. #
  63391. # DateTime.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => DateTime.new(2012, 8, 1, 22, 35, 0)
  63392. # DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
  63393. # DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
  63394. def change(options)
  63395. ::DateTime.civil(
  63396. options.fetch(:year, year),
  63397. options.fetch(:month, month),
  63398. options.fetch(:day, day),
  63399. options.fetch(:hour, hour),
  63400. options.fetch(:min, options[:hour] ? 0 : min),
  63401. options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec),
  63402. options.fetch(:offset, offset),
  63403. options.fetch(:start, start)
  63404. )
  63405. end
  63406. # Uses Date to provide precise Time calculations for years, months, and days.
  63407. # The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
  63408. # <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
  63409. # <tt>:minutes</tt>, <tt>:seconds</tt>.
  63410. def advance(options)
  63411. d = to_date.advance(options)
  63412. datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
  63413. seconds_to_advance = \
  63414. options.fetch(:seconds, 0) +
  63415. options.fetch(:minutes, 0) * 60 +
  63416. options.fetch(:hours, 0) * 3600
  63417. if seconds_to_advance.zero?
  63418. datetime_advanced_by_date
  63419. else
  63420. datetime_advanced_by_date.since seconds_to_advance
  63421. end
  63422. end
  63423. # Returns a new DateTime representing the time a number of seconds ago.
  63424. # Do not use this method in combination with x.months, use months_ago instead!
  63425. def ago(seconds)
  63426. since(-seconds)
  63427. end
  63428. # Returns a new DateTime representing the time a number of seconds since the
  63429. # instance time. Do not use this method in combination with x.months, use
  63430. # months_since instead!
  63431. def since(seconds)
  63432. self + Rational(seconds.round, 86400)
  63433. end
  63434. alias :in :since
  63435. # Returns a new DateTime representing the start of the day (0:00).
  63436. def beginning_of_day
  63437. change(:hour => 0)
  63438. end
  63439. alias :midnight :beginning_of_day
  63440. alias :at_midnight :beginning_of_day
  63441. alias :at_beginning_of_day :beginning_of_day
  63442. # Returns a new DateTime representing the end of the day (23:59:59).
  63443. def end_of_day
  63444. change(:hour => 23, :min => 59, :sec => 59)
  63445. end
  63446. alias :at_end_of_day :end_of_day
  63447. # Returns a new DateTime representing the start of the hour (hh:00:00).
  63448. def beginning_of_hour
  63449. change(:min => 0)
  63450. end
  63451. alias :at_beginning_of_hour :beginning_of_hour
  63452. # Returns a new DateTime representing the end of the hour (hh:59:59).
  63453. def end_of_hour
  63454. change(:min => 59, :sec => 59)
  63455. end
  63456. alias :at_end_of_hour :end_of_hour
  63457. # Adjusts DateTime to UTC by adding its offset value; offset is set to 0.
  63458. #
  63459. # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
  63460. # DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000
  63461. def utc
  63462. new_offset(0)
  63463. end
  63464. alias_method :getutc, :utc
  63465. # Returns +true+ if <tt>offset == 0</tt>.
  63466. def utc?
  63467. offset == 0
  63468. end
  63469. # Returns the offset value in seconds.
  63470. def utc_offset
  63471. (offset * 86400).to_i
  63472. end
  63473. end
  63474. require 'active_support/inflector/methods'
  63475. require 'active_support/core_ext/time/conversions'
  63476. require 'active_support/core_ext/date_time/calculations'
  63477. require 'active_support/values/time_zone'
  63478. class DateTime
  63479. # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
  63480. #
  63481. # This method is aliased to <tt>to_s</tt>.
  63482. #
  63483. # === Examples
  63484. # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
  63485. #
  63486. # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
  63487. # datetime.to_s(:db) # => "2007-12-04 00:00:00"
  63488. # datetime.to_s(:number) # => "20071204000000"
  63489. # datetime.to_formatted_s(:short) # => "04 Dec 00:00"
  63490. # datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
  63491. # datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
  63492. # datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
  63493. #
  63494. # == Adding your own datetime formats to to_formatted_s
  63495. # DateTime formats are shared with Time. You can add your own to the
  63496. # Time::DATE_FORMATS hash. Use the format name as the hash key and
  63497. # either a strftime string or Proc instance that takes a time or
  63498. # datetime argument as the value.
  63499. #
  63500. # # config/initializers/time_formats.rb
  63501. # Time::DATE_FORMATS[:month_and_year] = '%B %Y'
  63502. # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
  63503. def to_formatted_s(format = :default)
  63504. if formatter = ::Time::DATE_FORMATS[format]
  63505. formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
  63506. else
  63507. to_default_s
  63508. end
  63509. end
  63510. alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
  63511. alias_method :to_s, :to_formatted_s
  63512. #
  63513. # datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
  63514. # datetime.formatted_offset # => "-06:00"
  63515. # datetime.formatted_offset(false) # => "-0600"
  63516. def formatted_offset(colon = true, alternate_utc_string = nil)
  63517. utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
  63518. end
  63519. # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
  63520. def readable_inspect
  63521. to_s(:rfc822)
  63522. end
  63523. alias_method :default_inspect, :inspect
  63524. alias_method :inspect, :readable_inspect
  63525. # Returns DateTime with local offset for given year if format is local else
  63526. # offset is zero.
  63527. #
  63528. # DateTime.civil_from_format :local, 2012
  63529. # # => Sun, 01 Jan 2012 00:00:00 +0300
  63530. # DateTime.civil_from_format :local, 2012, 12, 17
  63531. # # => Mon, 17 Dec 2012 00:00:00 +0000
  63532. def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0)
  63533. if utc_or_local.to_sym == :local
  63534. offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
  63535. else
  63536. offset = 0
  63537. end
  63538. civil(year, month, day, hour, min, sec, offset)
  63539. end
  63540. # Converts +self+ to a floating-point number of seconds since the Unix epoch.
  63541. def to_f
  63542. seconds_since_unix_epoch.to_f
  63543. end
  63544. # Converts +self+ to an integer number of seconds since the Unix epoch.
  63545. def to_i
  63546. seconds_since_unix_epoch.to_i
  63547. end
  63548. private
  63549. def offset_in_seconds
  63550. (offset * 86400).to_i
  63551. end
  63552. def seconds_since_unix_epoch
  63553. (jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
  63554. end
  63555. end
  63556. require 'active_support/core_ext/infinite_comparable'
  63557. class DateTime
  63558. include InfiniteComparable
  63559. end
  63560. require 'active_support/core_ext/time/zones'
  63561. class DateTime
  63562. # Returns the simultaneous time in <tt>Time.zone</tt>.
  63563. #
  63564. # Time.zone = 'Hawaii' # => 'Hawaii'
  63565. # DateTime.new(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
  63566. #
  63567. # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt>
  63568. # as the local zone instead of the operating system's time zone.
  63569. #
  63570. # You can also pass in a TimeZone instance or string that identifies a TimeZone
  63571. # as an argument, and the conversion will be based on that zone instead of
  63572. # <tt>Time.zone</tt>.
  63573. #
  63574. # DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
  63575. def in_time_zone(zone = ::Time.zone)
  63576. if zone
  63577. ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
  63578. else
  63579. self
  63580. end
  63581. end
  63582. end
  63583. require 'active_support/core_ext/date_time/acts_like'
  63584. require 'active_support/core_ext/date_time/calculations'
  63585. require 'active_support/core_ext/date_time/conversions'
  63586. require 'active_support/core_ext/date_time/zones'
  63587. require 'active_support/core_ext/date_time/infinite_comparable'
  63588. module Enumerable
  63589. # Calculates a sum from the elements.
  63590. #
  63591. # payments.sum { |p| p.price * p.tax_rate }
  63592. # payments.sum(&:price)
  63593. #
  63594. # The latter is a shortcut for:
  63595. #
  63596. # payments.inject(0) { |sum, p| sum + p.price }
  63597. #
  63598. # It can also calculate the sum without the use of a block.
  63599. #
  63600. # [5, 15, 10].sum # => 30
  63601. # ['foo', 'bar'].sum # => "foobar"
  63602. # [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
  63603. #
  63604. # The default sum of an empty list is zero. You can override this default:
  63605. #
  63606. # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
  63607. def sum(identity = 0, &block)
  63608. if block_given?
  63609. map(&block).sum(identity)
  63610. else
  63611. inject { |sum, element| sum + element } || identity
  63612. end
  63613. end
  63614. # Convert an enumerable to a hash.
  63615. #
  63616. # people.index_by(&:login)
  63617. # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
  63618. # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
  63619. # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
  63620. def index_by
  63621. if block_given?
  63622. Hash[map { |elem| [yield(elem), elem] }]
  63623. else
  63624. to_enum :index_by
  63625. end
  63626. end
  63627. # Returns +true+ if the enumerable has more than 1 element. Functionally
  63628. # equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
  63629. # much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
  63630. # if more than one person is over 26.
  63631. def many?
  63632. cnt = 0
  63633. if block_given?
  63634. any? do |element|
  63635. cnt += 1 if yield element
  63636. cnt > 1
  63637. end
  63638. else
  63639. any? { (cnt += 1) > 1 }
  63640. end
  63641. end
  63642. # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
  63643. # collection does not include the object.
  63644. def exclude?(object)
  63645. !include?(object)
  63646. end
  63647. end
  63648. class Range #:nodoc:
  63649. # Optimize range sum to use arithmetic progression if a block is not given and
  63650. # we have a range of numeric values.
  63651. def sum(identity = 0)
  63652. if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
  63653. super
  63654. else
  63655. actual_last = exclude_end? ? (last - 1) : last
  63656. if actual_last >= first
  63657. (actual_last - first + 1) * (actual_last + first) / 2
  63658. else
  63659. identity
  63660. end
  63661. end
  63662. end
  63663. end
  63664. require 'fileutils'
  63665. class File
  63666. # Write to a file atomically. Useful for situations where you don't
  63667. # want other processes or threads to see half-written files.
  63668. #
  63669. # File.atomic_write('important.file') do |file|
  63670. # file.write('hello')
  63671. # end
  63672. #
  63673. # If your temp directory is not on the same filesystem as the file you're
  63674. # trying to write, you can provide a different temporary directory.
  63675. #
  63676. # File.atomic_write('/data/something.important', '/data/tmp') do |file|
  63677. # file.write('hello')
  63678. # end
  63679. def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
  63680. require 'tempfile' unless defined?(Tempfile)
  63681. require 'fileutils' unless defined?(FileUtils)
  63682. temp_file = Tempfile.new(basename(file_name), temp_dir)
  63683. temp_file.binmode
  63684. yield temp_file
  63685. temp_file.close
  63686. if File.exists?(file_name)
  63687. # Get original file permissions
  63688. old_stat = stat(file_name)
  63689. else
  63690. # If not possible, probe which are the default permissions in the
  63691. # destination directory.
  63692. old_stat = probe_stat_in(dirname(file_name))
  63693. end
  63694. # Overwrite original file with temp file
  63695. FileUtils.mv(temp_file.path, file_name)
  63696. # Set correct permissions on new file
  63697. begin
  63698. chown(old_stat.uid, old_stat.gid, file_name)
  63699. # This operation will affect filesystem ACL's
  63700. chmod(old_stat.mode, file_name)
  63701. rescue Errno::EPERM
  63702. # Changing file ownership failed, moving on.
  63703. end
  63704. end
  63705. # Private utility method.
  63706. def self.probe_stat_in(dir) #:nodoc:
  63707. basename = [
  63708. '.permissions_check',
  63709. Thread.current.object_id,
  63710. Process.pid,
  63711. rand(1000000)
  63712. ].join('.')
  63713. file_name = join(dir, basename)
  63714. FileUtils.touch(file_name)
  63715. stat(file_name)
  63716. ensure
  63717. FileUtils.rm_f(file_name) if file_name
  63718. end
  63719. end
  63720. require 'active_support/core_ext/file/atomic'
  63721. require 'active_support/xml_mini'
  63722. require 'active_support/time'
  63723. require 'active_support/core_ext/object/blank'
  63724. require 'active_support/core_ext/object/to_param'
  63725. require 'active_support/core_ext/object/to_query'
  63726. require 'active_support/core_ext/array/wrap'
  63727. require 'active_support/core_ext/hash/reverse_merge'
  63728. require 'active_support/core_ext/string/inflections'
  63729. class Hash
  63730. # Returns a string containing an XML representation of its receiver:
  63731. #
  63732. # {'foo' => 1, 'bar' => 2}.to_xml
  63733. # # =>
  63734. # # <?xml version="1.0" encoding="UTF-8"?>
  63735. # # <hash>
  63736. # # <foo type="integer">1</foo>
  63737. # # <bar type="integer">2</bar>
  63738. # # </hash>
  63739. #
  63740. # To do so, the method loops over the pairs and builds nodes that depend on
  63741. # the _values_. Given a pair +key+, +value+:
  63742. #
  63743. # * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
  63744. #
  63745. # * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
  63746. # and +key+ singularized as <tt>:children</tt>.
  63747. #
  63748. # * If +value+ is a callable object it must expect one or two arguments. Depending
  63749. # on the arity, the callable is invoked with the +options+ hash as first argument
  63750. # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
  63751. # callable can add nodes by using <tt>options[:builder]</tt>.
  63752. #
  63753. # 'foo'.to_xml(lambda { |options, key| options[:builder].b(key) })
  63754. # # => "<b>foo</b>"
  63755. #
  63756. # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
  63757. #
  63758. # class Foo
  63759. # def to_xml(options)
  63760. # options[:builder].bar 'fooing!'
  63761. # end
  63762. # end
  63763. #
  63764. # { foo: Foo.new }.to_xml(skip_instruct: true)
  63765. # # => "<hash><bar>fooing!</bar></hash>"
  63766. #
  63767. # * Otherwise, a node with +key+ as tag is created with a string representation of
  63768. # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
  63769. # Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
  63770. # added as well according to the following mapping:
  63771. #
  63772. # XML_TYPE_NAMES = {
  63773. # "Symbol" => "symbol",
  63774. # "Fixnum" => "integer",
  63775. # "Bignum" => "integer",
  63776. # "BigDecimal" => "decimal",
  63777. # "Float" => "float",
  63778. # "TrueClass" => "boolean",
  63779. # "FalseClass" => "boolean",
  63780. # "Date" => "date",
  63781. # "DateTime" => "dateTime",
  63782. # "Time" => "dateTime"
  63783. # }
  63784. #
  63785. # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
  63786. #
  63787. # The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
  63788. # configure your own builder with the <tt>:builder</tt> option. The method also accepts
  63789. # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
  63790. def to_xml(options = {})
  63791. require 'active_support/builder' unless defined?(Builder)
  63792. options = options.dup
  63793. options[:indent] ||= 2
  63794. options[:root] ||= 'hash'
  63795. options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
  63796. builder = options[:builder]
  63797. builder.instruct! unless options.delete(:skip_instruct)
  63798. root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
  63799. builder.tag!(root) do
  63800. each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
  63801. yield builder if block_given?
  63802. end
  63803. end
  63804. class << self
  63805. # Returns a Hash containing a collection of pairs when the key is the node name and the value is
  63806. # its content
  63807. #
  63808. # xml = <<-XML
  63809. # <?xml version="1.0" encoding="UTF-8"?>
  63810. # <hash>
  63811. # <foo type="integer">1</foo>
  63812. # <bar type="integer">2</bar>
  63813. # </hash>
  63814. # XML
  63815. #
  63816. # hash = Hash.from_xml(xml)
  63817. # # => {"hash"=>{"foo"=>1, "bar"=>2}}
  63818. #
  63819. # DisallowedType is raise if the XML contains attributes with <tt>type="yaml"</tt> or
  63820. # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
  63821. def from_xml(xml, disallowed_types = nil)
  63822. ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
  63823. end
  63824. # Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML.
  63825. def from_trusted_xml(xml)
  63826. from_xml xml, []
  63827. end
  63828. end
  63829. end
  63830. module ActiveSupport
  63831. class XMLConverter # :nodoc:
  63832. class DisallowedType < StandardError
  63833. def initialize(type)
  63834. super "Disallowed type attribute: #{type.inspect}"
  63835. end
  63836. end
  63837. DISALLOWED_TYPES = %w(symbol yaml)
  63838. def initialize(xml, disallowed_types = nil)
  63839. @xml = normalize_keys(XmlMini.parse(xml))
  63840. @disallowed_types = disallowed_types || DISALLOWED_TYPES
  63841. end
  63842. def to_h
  63843. deep_to_h(@xml)
  63844. end
  63845. private
  63846. def normalize_keys(params)
  63847. case params
  63848. when Hash
  63849. Hash[params.map { |k,v| [k.to_s.tr('-', '_'), normalize_keys(v)] } ]
  63850. when Array
  63851. params.map { |v| normalize_keys(v) }
  63852. else
  63853. params
  63854. end
  63855. end
  63856. def deep_to_h(value)
  63857. case value
  63858. when Hash
  63859. process_hash(value)
  63860. when Array
  63861. process_array(value)
  63862. when String
  63863. value
  63864. else
  63865. raise "can't typecast #{value.class.name} - #{value.inspect}"
  63866. end
  63867. end
  63868. def process_hash(value)
  63869. if value.include?('type') && !value['type'].is_a?(Hash) && @disallowed_types.include?(value['type'])
  63870. raise DisallowedType, value['type']
  63871. end
  63872. if become_array?(value)
  63873. _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
  63874. if entries.nil? || value['__content__'].try(:empty?)
  63875. []
  63876. else
  63877. case entries
  63878. when Array
  63879. entries.collect { |v| deep_to_h(v) }
  63880. when Hash
  63881. [deep_to_h(entries)]
  63882. else
  63883. raise "can't typecast #{entries.inspect}"
  63884. end
  63885. end
  63886. elsif become_content?(value)
  63887. process_content(value)
  63888. elsif become_empty_string?(value)
  63889. ''
  63890. elsif become_hash?(value)
  63891. xml_value = Hash[value.map { |k,v| [k, deep_to_h(v)] }]
  63892. # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
  63893. # how multipart uploaded files from HTML appear
  63894. xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value
  63895. end
  63896. end
  63897. def become_content?(value)
  63898. value['type'] == 'file' || (value['__content__'] && (value.keys.size == 1 || value['__content__'].present?))
  63899. end
  63900. def become_array?(value)
  63901. value['type'] == 'array'
  63902. end
  63903. def become_empty_string?(value)
  63904. # {"string" => true}
  63905. # No tests fail when the second term is removed.
  63906. value['type'] == 'string' && value['nil'] != 'true'
  63907. end
  63908. def become_hash?(value)
  63909. !nothing?(value) && !garbage?(value)
  63910. end
  63911. def nothing?(value)
  63912. # blank or nil parsed values are represented by nil
  63913. value.blank? || value['nil'] == 'true'
  63914. end
  63915. def garbage?(value)
  63916. # If the type is the only element which makes it then
  63917. # this still makes the value nil, except if type is
  63918. # a XML node(where type['value'] is a Hash)
  63919. value['type'] && !value['type'].is_a?(::Hash) && value.size == 1
  63920. end
  63921. def process_content(value)
  63922. content = value['__content__']
  63923. if parser = ActiveSupport::XmlMini::PARSING[value['type']]
  63924. parser.arity == 1 ? parser.call(content) : parser.call(content, value)
  63925. else
  63926. content
  63927. end
  63928. end
  63929. def process_array(value)
  63930. value.map! { |i| deep_to_h(i) }
  63931. value.length > 1 ? value : value.first
  63932. end
  63933. end
  63934. end
  63935. class Hash
  63936. # Returns a new hash with +self+ and +other_hash+ merged recursively.
  63937. #
  63938. # h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
  63939. # h2 = { x: { y: [7,8,9] }, z: 'xyz' }
  63940. #
  63941. # h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"}
  63942. # h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]}
  63943. # h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) }
  63944. # #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
  63945. def deep_merge(other_hash, &block)
  63946. dup.deep_merge!(other_hash, &block)
  63947. end
  63948. # Same as +deep_merge+, but modifies +self+.
  63949. def deep_merge!(other_hash, &block)
  63950. other_hash.each_pair do |k,v|
  63951. tv = self[k]
  63952. if tv.is_a?(Hash) && v.is_a?(Hash)
  63953. self[k] = tv.deep_merge(v, &block)
  63954. else
  63955. self[k] = block && tv ? block.call(k, tv, v) : v
  63956. end
  63957. end
  63958. self
  63959. end
  63960. end
  63961. class Hash
  63962. # Returns a hash that represents the difference between two hashes.
  63963. #
  63964. # {1 => 2}.diff(1 => 2) # => {}
  63965. # {1 => 2}.diff(1 => 3) # => {1 => 2}
  63966. # {}.diff(1 => 2) # => {1 => 2}
  63967. # {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
  63968. def diff(other)
  63969. ActiveSupport::Deprecation.warn "Hash#diff is no longer used inside of Rails, and is being deprecated with no replacement. If you're using it to compare hashes for the purpose of testing, please use MiniTest's assert_equal instead."
  63970. dup.
  63971. delete_if { |k, v| other[k] == v }.
  63972. merge!(other.dup.delete_if { |k, v| has_key?(k) })
  63973. end
  63974. end
  63975. class Hash
  63976. # Return a hash that includes everything but the given keys. This is useful for
  63977. # limiting a set of parameters to everything but a few known toggles:
  63978. #
  63979. # @person.update(params[:person].except(:admin))
  63980. def except(*keys)
  63981. dup.except!(*keys)
  63982. end
  63983. # Replaces the hash without the given keys.
  63984. def except!(*keys)
  63985. keys.each { |key| delete(key) }
  63986. self
  63987. end
  63988. end
  63989. require 'active_support/hash_with_indifferent_access'
  63990. class Hash
  63991. # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
  63992. #
  63993. # { a: 1 }.with_indifferent_access['a'] # => 1
  63994. def with_indifferent_access
  63995. ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
  63996. end
  63997. # Called when object is nested under an object that receives
  63998. # #with_indifferent_access. This method will be called on the current object
  63999. # by the enclosing object and is aliased to #with_indifferent_access by
  64000. # default. Subclasses of Hash may overwrite this method to return +self+ if
  64001. # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
  64002. # desirable.
  64003. #
  64004. # b = { b: 1 }
  64005. # { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
  64006. alias nested_under_indifferent_access with_indifferent_access
  64007. end
  64008. class Hash
  64009. # Return a new hash with all keys converted using the block operation.
  64010. #
  64011. # hash = { name: 'Rob', age: '28' }
  64012. #
  64013. # hash.transform_keys{ |key| key.to_s.upcase }
  64014. # # => { "NAME" => "Rob", "AGE" => "28" }
  64015. def transform_keys
  64016. result = {}
  64017. each_key do |key|
  64018. result[yield(key)] = self[key]
  64019. end
  64020. result
  64021. end
  64022. # Destructively convert all keys using the block operations.
  64023. # Same as transform_keys but modifies +self+.
  64024. def transform_keys!
  64025. keys.each do |key|
  64026. self[yield(key)] = delete(key)
  64027. end
  64028. self
  64029. end
  64030. # Return a new hash with all keys converted to strings.
  64031. #
  64032. # hash = { name: 'Rob', age: '28' }
  64033. #
  64034. # hash.stringify_keys
  64035. # #=> { "name" => "Rob", "age" => "28" }
  64036. def stringify_keys
  64037. transform_keys{ |key| key.to_s }
  64038. end
  64039. # Destructively convert all keys to strings. Same as
  64040. # +stringify_keys+, but modifies +self+.
  64041. def stringify_keys!
  64042. transform_keys!{ |key| key.to_s }
  64043. end
  64044. # Return a new hash with all keys converted to symbols, as long as
  64045. # they respond to +to_sym+.
  64046. #
  64047. # hash = { 'name' => 'Rob', 'age' => '28' }
  64048. #
  64049. # hash.symbolize_keys
  64050. # #=> { name: "Rob", age: "28" }
  64051. def symbolize_keys
  64052. transform_keys{ |key| key.to_sym rescue key }
  64053. end
  64054. alias_method :to_options, :symbolize_keys
  64055. # Destructively convert all keys to symbols, as long as they respond
  64056. # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
  64057. def symbolize_keys!
  64058. transform_keys!{ |key| key.to_sym rescue key }
  64059. end
  64060. alias_method :to_options!, :symbolize_keys!
  64061. # Validate all keys in a hash match <tt>*valid_keys</tt>, raising ArgumentError
  64062. # on a mismatch. Note that keys are NOT treated indifferently, meaning if you
  64063. # use strings for keys but assert symbols as keys, this will fail.
  64064. #
  64065. # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
  64066. # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: name"
  64067. # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
  64068. def assert_valid_keys(*valid_keys)
  64069. valid_keys.flatten!
  64070. each_key do |k|
  64071. raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k)
  64072. end
  64073. end
  64074. # Return a new hash with all keys converted by the block operation.
  64075. # This includes the keys from the root hash and from all
  64076. # nested hashes.
  64077. #
  64078. # hash = { person: { name: 'Rob', age: '28' } }
  64079. #
  64080. # hash.deep_transform_keys{ |key| key.to_s.upcase }
  64081. # # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
  64082. def deep_transform_keys(&block)
  64083. result = {}
  64084. each do |key, value|
  64085. result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
  64086. end
  64087. result
  64088. end
  64089. # Destructively convert all keys by using the block operation.
  64090. # This includes the keys from the root hash and from all
  64091. # nested hashes.
  64092. def deep_transform_keys!(&block)
  64093. keys.each do |key|
  64094. value = delete(key)
  64095. self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
  64096. end
  64097. self
  64098. end
  64099. # Return a new hash with all keys converted to strings.
  64100. # This includes the keys from the root hash and from all
  64101. # nested hashes.
  64102. #
  64103. # hash = { person: { name: 'Rob', age: '28' } }
  64104. #
  64105. # hash.deep_stringify_keys
  64106. # # => { "person" => { "name" => "Rob", "age" => "28" } }
  64107. def deep_stringify_keys
  64108. deep_transform_keys{ |key| key.to_s }
  64109. end
  64110. # Destructively convert all keys to strings.
  64111. # This includes the keys from the root hash and from all
  64112. # nested hashes.
  64113. def deep_stringify_keys!
  64114. deep_transform_keys!{ |key| key.to_s }
  64115. end
  64116. # Return a new hash with all keys converted to symbols, as long as
  64117. # they respond to +to_sym+. This includes the keys from the root hash
  64118. # and from all nested hashes.
  64119. #
  64120. # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
  64121. #
  64122. # hash.deep_symbolize_keys
  64123. # # => { person: { name: "Rob", age: "28" } }
  64124. def deep_symbolize_keys
  64125. deep_transform_keys{ |key| key.to_sym rescue key }
  64126. end
  64127. # Destructively convert all keys to symbols, as long as they respond
  64128. # to +to_sym+. This includes the keys from the root hash and from all
  64129. # nested hashes.
  64130. def deep_symbolize_keys!
  64131. deep_transform_keys!{ |key| key.to_sym rescue key }
  64132. end
  64133. end
  64134. class Hash
  64135. # Merges the caller into +other_hash+. For example,
  64136. #
  64137. # options = options.reverse_merge(size: 25, velocity: 10)
  64138. #
  64139. # is equivalent to
  64140. #
  64141. # options = { size: 25, velocity: 10 }.merge(options)
  64142. #
  64143. # This is particularly useful for initializing an options hash
  64144. # with default values.
  64145. def reverse_merge(other_hash)
  64146. other_hash.merge(self)
  64147. end
  64148. # Destructive +reverse_merge+.
  64149. def reverse_merge!(other_hash)
  64150. # right wins if there is no left
  64151. merge!( other_hash ){|key,left,right| left }
  64152. end
  64153. alias_method :reverse_update, :reverse_merge!
  64154. end
  64155. class Hash
  64156. # Slice a hash to include only the given keys. This is useful for
  64157. # limiting an options hash to valid keys before passing to a method:
  64158. #
  64159. # def search(criteria = {})
  64160. # criteria.assert_valid_keys(:mass, :velocity, :time)
  64161. # end
  64162. #
  64163. # search(options.slice(:mass, :velocity, :time))
  64164. #
  64165. # If you have an array of keys you want to limit to, you should splat them:
  64166. #
  64167. # valid_keys = [:mass, :velocity, :time]
  64168. # search(options.slice(*valid_keys))
  64169. def slice(*keys)
  64170. keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
  64171. keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
  64172. end
  64173. # Replaces the hash with only the given keys.
  64174. # Returns a hash containing the removed key/value pairs.
  64175. #
  64176. # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
  64177. # # => {:c=>3, :d=>4}
  64178. def slice!(*keys)
  64179. keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
  64180. omit = slice(*self.keys - keys)
  64181. hash = slice(*keys)
  64182. replace(hash)
  64183. omit
  64184. end
  64185. # Removes and returns the key/value pairs matching the given keys.
  64186. #
  64187. # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
  64188. # { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
  64189. def extract!(*keys)
  64190. keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
  64191. end
  64192. end
  64193. require 'active_support/core_ext/hash/conversions'
  64194. require 'active_support/core_ext/hash/deep_merge'
  64195. require 'active_support/core_ext/hash/diff'
  64196. require 'active_support/core_ext/hash/except'
  64197. require 'active_support/core_ext/hash/indifferent_access'
  64198. require 'active_support/core_ext/hash/keys'
  64199. require 'active_support/core_ext/hash/reverse_merge'
  64200. require 'active_support/core_ext/hash/slice'
  64201. require 'active_support/concern'
  64202. require 'active_support/core_ext/module/aliasing'
  64203. require 'active_support/core_ext/object/try'
  64204. module InfiniteComparable
  64205. extend ActiveSupport::Concern
  64206. included do
  64207. alias_method_chain :<=>, :infinity
  64208. end
  64209. define_method :'<=>_with_infinity' do |other|
  64210. if other.class == self.class
  64211. public_send :'<=>_without_infinity', other
  64212. else
  64213. infinite = try(:infinite?)
  64214. other_infinite = other.try(:infinite?)
  64215. # inf <=> inf
  64216. if infinite && other_infinite
  64217. infinite <=> other_infinite
  64218. # not_inf <=> inf
  64219. elsif other_infinite
  64220. -other_infinite
  64221. # inf <=> not_inf
  64222. elsif infinite
  64223. infinite
  64224. else
  64225. conversion = "to_#{self.class.name.downcase}"
  64226. other = other.public_send(conversion) if other.respond_to?(conversion)
  64227. public_send :'<=>_without_infinity', other
  64228. end
  64229. end
  64230. end
  64231. end
  64232. require 'active_support/inflector'
  64233. class Integer
  64234. # Ordinalize turns a number into an ordinal string used to denote the
  64235. # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
  64236. #
  64237. # 1.ordinalize # => "1st"
  64238. # 2.ordinalize # => "2nd"
  64239. # 1002.ordinalize # => "1002nd"
  64240. # 1003.ordinalize # => "1003rd"
  64241. # -11.ordinalize # => "-11th"
  64242. # -1001.ordinalize # => "-1001st"
  64243. def ordinalize
  64244. ActiveSupport::Inflector.ordinalize(self)
  64245. end
  64246. # Ordinal returns the suffix used to denote the position
  64247. # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
  64248. #
  64249. # 1.ordinal # => "st"
  64250. # 2.ordinal # => "nd"
  64251. # 1002.ordinal # => "nd"
  64252. # 1003.ordinal # => "rd"
  64253. # -11.ordinal # => "th"
  64254. # -1001.ordinal # => "st"
  64255. def ordinal
  64256. ActiveSupport::Inflector.ordinal(self)
  64257. end
  64258. end
  64259. class Integer
  64260. # Check whether the integer is evenly divisible by the argument.
  64261. #
  64262. # 0.multiple_of?(0) #=> true
  64263. # 6.multiple_of?(5) #=> false
  64264. # 10.multiple_of?(2) #=> true
  64265. def multiple_of?(number)
  64266. number != 0 ? self % number == 0 : zero?
  64267. end
  64268. end
  64269. require 'active_support/duration'
  64270. require 'active_support/core_ext/numeric/time'
  64271. class Integer
  64272. # Enables the use of time calculations and declarations, like <tt>45.minutes +
  64273. # 2.hours + 4.years</tt>.
  64274. #
  64275. # These methods use Time#advance for precise date calculations when using
  64276. # <tt>from_now</tt>, +ago+, etc. as well as adding or subtracting their
  64277. # results from a Time object.
  64278. #
  64279. # # equivalent to Time.now.advance(months: 1)
  64280. # 1.month.from_now
  64281. #
  64282. # # equivalent to Time.now.advance(years: 2)
  64283. # 2.years.from_now
  64284. #
  64285. # # equivalent to Time.now.advance(months: 4, years: 5)
  64286. # (4.months + 5.years).from_now
  64287. #
  64288. # While these methods provide precise calculation when used as in the examples
  64289. # above, care should be taken to note that this is not true if the result of
  64290. # +months+, +years+, etc is converted before use:
  64291. #
  64292. # # equivalent to 30.days.to_i.from_now
  64293. # 1.month.to_i.from_now
  64294. #
  64295. # # equivalent to 365.25.days.to_f.from_now
  64296. # 1.year.to_f.from_now
  64297. #
  64298. # In such cases, Ruby's core
  64299. # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
  64300. # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
  64301. # date and time arithmetic.
  64302. def months
  64303. ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
  64304. end
  64305. alias :month :months
  64306. def years
  64307. ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
  64308. end
  64309. alias :year :years
  64310. end
  64311. require 'active_support/core_ext/integer/multiple'
  64312. require 'active_support/core_ext/integer/inflections'
  64313. require 'active_support/core_ext/integer/time'
  64314. class Object
  64315. # Makes backticks behave (somewhat more) similarly on all platforms.
  64316. # On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
  64317. # spawned shell prints a message to stderr and sets $?. We emulate
  64318. # Unix on the former but not the latter.
  64319. def `(command) #:nodoc:
  64320. super
  64321. rescue Errno::ENOENT => e
  64322. STDERR.puts "#$0: #{e}"
  64323. end
  64324. end
  64325. module Kernel
  64326. unless respond_to?(:debugger)
  64327. # Starts a debugging session if the +debugger+ gem has been loaded (call rails server --debugger to do load it).
  64328. def debugger
  64329. message = "\n***** Debugger requested, but was not available (ensure the debugger gem is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n"
  64330. defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
  64331. end
  64332. alias breakpoint debugger unless respond_to?(:breakpoint)
  64333. end
  64334. end
  64335. require 'rbconfig'
  64336. require 'tempfile'
  64337. module Kernel
  64338. # Sets $VERBOSE to nil for the duration of the block and back to its original
  64339. # value afterwards.
  64340. #
  64341. # silence_warnings do
  64342. # value = noisy_call # no warning voiced
  64343. # end
  64344. #
  64345. # noisy_call # warning voiced
  64346. def silence_warnings
  64347. with_warnings(nil) { yield }
  64348. end
  64349. # Sets $VERBOSE to +true+ for the duration of the block and back to its
  64350. # original value afterwards.
  64351. def enable_warnings
  64352. with_warnings(true) { yield }
  64353. end
  64354. # Sets $VERBOSE for the duration of the block and back to its original
  64355. # value afterwards.
  64356. def with_warnings(flag)
  64357. old_verbose, $VERBOSE = $VERBOSE, flag
  64358. yield
  64359. ensure
  64360. $VERBOSE = old_verbose
  64361. end
  64362. # For compatibility
  64363. def silence_stderr #:nodoc:
  64364. silence_stream(STDERR) { yield }
  64365. end
  64366. # Silences any stream for the duration of the block.
  64367. #
  64368. # silence_stream(STDOUT) do
  64369. # puts 'This will never be seen'
  64370. # end
  64371. #
  64372. # puts 'But this will'
  64373. def silence_stream(stream)
  64374. old_stream = stream.dup
  64375. stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
  64376. stream.sync = true
  64377. yield
  64378. ensure
  64379. stream.reopen(old_stream)
  64380. end
  64381. # Blocks and ignores any exception passed as argument if raised within the block.
  64382. #
  64383. # suppress(ZeroDivisionError) do
  64384. # 1/0
  64385. # puts 'This code is NOT reached'
  64386. # end
  64387. #
  64388. # puts 'This code gets executed and nothing related to ZeroDivisionError was seen'
  64389. def suppress(*exception_classes)
  64390. yield
  64391. rescue Exception => e
  64392. raise unless exception_classes.any? { |cls| e.kind_of?(cls) }
  64393. end
  64394. # Captures the given stream and returns it:
  64395. #
  64396. # stream = capture(:stdout) { puts 'notice' }
  64397. # stream # => "notice\n"
  64398. #
  64399. # stream = capture(:stderr) { warn 'error' }
  64400. # stream # => "error\n"
  64401. #
  64402. # even for subprocesses:
  64403. #
  64404. # stream = capture(:stdout) { system('echo notice') }
  64405. # stream # => "notice\n"
  64406. #
  64407. # stream = capture(:stderr) { system('echo error 1>&2') }
  64408. # stream # => "error\n"
  64409. def capture(stream)
  64410. stream = stream.to_s
  64411. captured_stream = Tempfile.new(stream)
  64412. stream_io = eval("$#{stream}")
  64413. origin_stream = stream_io.dup
  64414. stream_io.reopen(captured_stream)
  64415. yield
  64416. stream_io.rewind
  64417. return captured_stream.read
  64418. ensure
  64419. captured_stream.unlink
  64420. stream_io.reopen(origin_stream)
  64421. end
  64422. alias :silence :capture
  64423. # Silences both STDOUT and STDERR, even for subprocesses.
  64424. #
  64425. # quietly { system 'bundle install' }
  64426. def quietly
  64427. silence_stream(STDOUT) do
  64428. silence_stream(STDERR) do
  64429. yield
  64430. end
  64431. end
  64432. end
  64433. end
  64434. module Kernel
  64435. # class_eval on an object acts like singleton_class.class_eval.
  64436. def class_eval(*args, &block)
  64437. singleton_class.class_eval(*args, &block)
  64438. end
  64439. end
  64440. require 'active_support/core_ext/kernel/reporting'
  64441. require 'active_support/core_ext/kernel/agnostics'
  64442. require 'active_support/core_ext/kernel/debugger'
  64443. require 'active_support/core_ext/kernel/singleton_class'
  64444. class LoadError
  64445. REGEXPS = [
  64446. /^no such file to load -- (.+)$/i,
  64447. /^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
  64448. /^Missing API definition file in (.+)$/i,
  64449. /^cannot load such file -- (.+)$/i,
  64450. ]
  64451. unless method_defined?(:path)
  64452. def path
  64453. @path ||= begin
  64454. REGEXPS.find do |regex|
  64455. message =~ regex
  64456. end
  64457. $1
  64458. end
  64459. end
  64460. end
  64461. def is_missing?(location)
  64462. location.sub(/\.rb$/, '') == path.sub(/\.rb$/, '')
  64463. end
  64464. end
  64465. MissingSourceFile = LoadErrorrequire 'active_support/core_ext/class/attribute_accessors'
  64466. require 'active_support/deprecation'
  64467. require 'active_support/logger_silence'
  64468. ActiveSupport::Deprecation.warn 'this file is deprecated and will be removed'
  64469. # Adds the 'around_level' method to Logger.
  64470. class Logger #:nodoc:
  64471. def self.define_around_helper(level)
  64472. module_eval <<-end_eval, __FILE__, __LINE__ + 1
  64473. def around_#{level}(before_message, after_message) # def around_debug(before_message, after_message, &block)
  64474. self.#{level}(before_message) # self.debug(before_message)
  64475. return_value = yield(self) # return_value = yield(self)
  64476. self.#{level}(after_message) # self.debug(after_message)
  64477. return_value # return_value
  64478. end # end
  64479. end_eval
  64480. end
  64481. [:debug, :info, :error, :fatal].each {|level| define_around_helper(level) }
  64482. end
  64483. require 'logger'
  64484. # Extensions to the built-in Ruby logger.
  64485. #
  64486. # If you want to use the default log formatter as defined in the Ruby core, then you
  64487. # will need to set the formatter for the logger as in:
  64488. #
  64489. # logger.formatter = Formatter.new
  64490. #
  64491. # You can then specify the datetime format, for example:
  64492. #
  64493. # logger.datetime_format = "%Y-%m-%d"
  64494. #
  64495. # Note: This logger is deprecated in favor of ActiveSupport::Logger
  64496. class Logger
  64497. include LoggerSilence
  64498. alias :old_datetime_format= :datetime_format=
  64499. # Logging date-time format (string passed to +strftime+). Ignored if the formatter
  64500. # does not respond to datetime_format=.
  64501. def datetime_format=(format)
  64502. formatter.datetime_format = format if formatter.respond_to?(:datetime_format=)
  64503. end
  64504. alias :old_datetime_format :datetime_format
  64505. # Get the logging datetime format. Returns nil if the formatter does not support
  64506. # datetime formatting.
  64507. def datetime_format
  64508. formatter.datetime_format if formatter.respond_to?(:datetime_format)
  64509. end
  64510. alias :old_initialize :initialize
  64511. # Overwrite initialize to set a default formatter.
  64512. def initialize(*args)
  64513. old_initialize(*args)
  64514. self.formatter = SimpleFormatter.new
  64515. end
  64516. # Simple formatter which only displays the message.
  64517. class SimpleFormatter < Logger::Formatter
  64518. # This method is invoked when a log event occurs
  64519. def call(severity, timestamp, progname, msg)
  64520. "#{String === msg ? msg : msg.inspect}\n"
  64521. end
  64522. end
  64523. end
  64524. module Marshal
  64525. class << self
  64526. def load_with_autoloading(source)
  64527. load_without_autoloading(source)
  64528. rescue ArgumentError, NameError => exc
  64529. if exc.message.match(%r|undefined class/module (.+)|)
  64530. # try loading the class/module
  64531. $1.constantize
  64532. # if it is a IO we need to go back to read the object
  64533. source.rewind if source.respond_to?(:rewind)
  64534. retry
  64535. else
  64536. raise exc
  64537. end
  64538. end
  64539. alias_method_chain :load, :autoloading
  64540. end
  64541. end
  64542. class Module
  64543. # Encapsulates the common pattern of:
  64544. #
  64545. # alias_method :foo_without_feature, :foo
  64546. # alias_method :foo, :foo_with_feature
  64547. #
  64548. # With this, you simply do:
  64549. #
  64550. # alias_method_chain :foo, :feature
  64551. #
  64552. # And both aliases are set up for you.
  64553. #
  64554. # Query and bang methods (foo?, foo!) keep the same punctuation:
  64555. #
  64556. # alias_method_chain :foo?, :feature
  64557. #
  64558. # is equivalent to
  64559. #
  64560. # alias_method :foo_without_feature?, :foo?
  64561. # alias_method :foo?, :foo_with_feature?
  64562. #
  64563. # so you can safely chain foo, foo?, and foo! with the same feature.
  64564. def alias_method_chain(target, feature)
  64565. # Strip out punctuation on predicates or bang methods since
  64566. # e.g. target?_without_feature is not a valid method name.
  64567. aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
  64568. yield(aliased_target, punctuation) if block_given?
  64569. with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
  64570. without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
  64571. alias_method without_method, target
  64572. alias_method target, with_method
  64573. case
  64574. when public_method_defined?(without_method)
  64575. public target
  64576. when protected_method_defined?(without_method)
  64577. protected target
  64578. when private_method_defined?(without_method)
  64579. private target
  64580. end
  64581. end
  64582. # Allows you to make aliases for attributes, which includes
  64583. # getter, setter, and query methods.
  64584. #
  64585. # class Content < ActiveRecord::Base
  64586. # # has a title attribute
  64587. # end
  64588. #
  64589. # class Email < Content
  64590. # alias_attribute :subject, :title
  64591. # end
  64592. #
  64593. # e = Email.find(1)
  64594. # e.title # => "Superstars"
  64595. # e.subject # => "Superstars"
  64596. # e.subject? # => true
  64597. # e.subject = "Megastars"
  64598. # e.title # => "Megastars"
  64599. def alias_attribute(new_name, old_name)
  64600. module_eval <<-STR, __FILE__, __LINE__ + 1
  64601. def #{new_name}; self.#{old_name}; end # def subject; self.title; end
  64602. def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
  64603. def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
  64604. STR
  64605. end
  64606. end
  64607. class Module
  64608. # A module may or may not have a name.
  64609. #
  64610. # module M; end
  64611. # M.name # => "M"
  64612. #
  64613. # m = Module.new
  64614. # m.name # => nil
  64615. #
  64616. # A module gets a name when it is first assigned to a constant. Either
  64617. # via the +module+ or +class+ keyword or by an explicit assignment:
  64618. #
  64619. # m = Module.new # creates an anonymous module
  64620. # M = m # => m gets a name here as a side-effect
  64621. # m.name # => "M"
  64622. def anonymous?
  64623. name.nil?
  64624. end
  64625. end
  64626. class Module
  64627. # Declares an attribute reader backed by an internally-named instance variable.
  64628. def attr_internal_reader(*attrs)
  64629. attrs.each {|attr_name| attr_internal_define(attr_name, :reader)}
  64630. end
  64631. # Declares an attribute writer backed by an internally-named instance variable.
  64632. def attr_internal_writer(*attrs)
  64633. attrs.each {|attr_name| attr_internal_define(attr_name, :writer)}
  64634. end
  64635. # Declares an attribute reader and writer backed by an internally-named instance
  64636. # variable.
  64637. def attr_internal_accessor(*attrs)
  64638. attr_internal_reader(*attrs)
  64639. attr_internal_writer(*attrs)
  64640. end
  64641. alias_method :attr_internal, :attr_internal_accessor
  64642. class << self; attr_accessor :attr_internal_naming_format end
  64643. self.attr_internal_naming_format = '@_%s'
  64644. private
  64645. def attr_internal_ivar_name(attr)
  64646. Module.attr_internal_naming_format % attr
  64647. end
  64648. def attr_internal_define(attr_name, type)
  64649. internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
  64650. class_eval do # class_eval is necessary on 1.9 or else the methods a made private
  64651. # use native attr_* methods as they are faster on some Ruby implementations
  64652. send("attr_#{type}", internal_name)
  64653. end
  64654. attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
  64655. alias_method attr_name, internal_name
  64656. remove_method internal_name
  64657. end
  64658. end
  64659. require 'active_support/core_ext/array/extract_options'
  64660. class Module
  64661. def mattr_reader(*syms)
  64662. options = syms.extract_options!
  64663. syms.each do |sym|
  64664. raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
  64665. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  64666. @@#{sym} = nil unless defined? @@#{sym}
  64667. def self.#{sym}
  64668. @@#{sym}
  64669. end
  64670. EOS
  64671. unless options[:instance_reader] == false || options[:instance_accessor] == false
  64672. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  64673. def #{sym}
  64674. @@#{sym}
  64675. end
  64676. EOS
  64677. end
  64678. end
  64679. end
  64680. def mattr_writer(*syms)
  64681. options = syms.extract_options!
  64682. syms.each do |sym|
  64683. raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
  64684. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  64685. def self.#{sym}=(obj)
  64686. @@#{sym} = obj
  64687. end
  64688. EOS
  64689. unless options[:instance_writer] == false || options[:instance_accessor] == false
  64690. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  64691. def #{sym}=(obj)
  64692. @@#{sym} = obj
  64693. end
  64694. EOS
  64695. end
  64696. end
  64697. end
  64698. # Extends the module object with module and instance accessors for class attributes,
  64699. # just like the native attr* accessors for instance attributes.
  64700. #
  64701. # module AppConfiguration
  64702. # mattr_accessor :google_api_key
  64703. #
  64704. # self.google_api_key = "123456789"
  64705. # end
  64706. #
  64707. # AppConfiguration.google_api_key # => "123456789"
  64708. # AppConfiguration.google_api_key = "overriding the api key!"
  64709. # AppConfiguration.google_api_key # => "overriding the api key!"
  64710. #
  64711. # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
  64712. # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
  64713. # To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
  64714. def mattr_accessor(*syms)
  64715. mattr_reader(*syms)
  64716. mattr_writer(*syms)
  64717. end
  64718. end
  64719. class Module
  64720. # Provides a delegate class method to easily expose contained objects' public methods
  64721. # as your own. Pass one or more methods (specified as symbols or strings)
  64722. # and the name of the target object via the <tt>:to</tt> option (also a symbol
  64723. # or string). At least one method and the <tt>:to</tt> option are required.
  64724. #
  64725. # Delegation is particularly useful with Active Record associations:
  64726. #
  64727. # class Greeter < ActiveRecord::Base
  64728. # def hello
  64729. # 'hello'
  64730. # end
  64731. #
  64732. # def goodbye
  64733. # 'goodbye'
  64734. # end
  64735. # end
  64736. #
  64737. # class Foo < ActiveRecord::Base
  64738. # belongs_to :greeter
  64739. # delegate :hello, to: :greeter
  64740. # end
  64741. #
  64742. # Foo.new.hello # => "hello"
  64743. # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
  64744. #
  64745. # Multiple delegates to the same target are allowed:
  64746. #
  64747. # class Foo < ActiveRecord::Base
  64748. # belongs_to :greeter
  64749. # delegate :hello, :goodbye, to: :greeter
  64750. # end
  64751. #
  64752. # Foo.new.goodbye # => "goodbye"
  64753. #
  64754. # Methods can be delegated to instance variables, class variables, or constants
  64755. # by providing them as a symbols:
  64756. #
  64757. # class Foo
  64758. # CONSTANT_ARRAY = [0,1,2,3]
  64759. # @@class_array = [4,5,6,7]
  64760. #
  64761. # def initialize
  64762. # @instance_array = [8,9,10,11]
  64763. # end
  64764. # delegate :sum, to: :CONSTANT_ARRAY
  64765. # delegate :min, to: :@@class_array
  64766. # delegate :max, to: :@instance_array
  64767. # end
  64768. #
  64769. # Foo.new.sum # => 6
  64770. # Foo.new.min # => 4
  64771. # Foo.new.max # => 11
  64772. #
  64773. # It's also possible to delegate a method to the class by using +:class+:
  64774. #
  64775. # class Foo
  64776. # def self.hello
  64777. # "world"
  64778. # end
  64779. #
  64780. # delegate :hello, to: :class
  64781. # end
  64782. #
  64783. # Foo.new.hello # => "world"
  64784. #
  64785. # Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
  64786. # is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
  64787. # delegated to.
  64788. #
  64789. # Person = Struct.new(:name, :address)
  64790. #
  64791. # class Invoice < Struct.new(:client)
  64792. # delegate :name, :address, to: :client, prefix: true
  64793. # end
  64794. #
  64795. # john_doe = Person.new('John Doe', 'Vimmersvej 13')
  64796. # invoice = Invoice.new(john_doe)
  64797. # invoice.client_name # => "John Doe"
  64798. # invoice.client_address # => "Vimmersvej 13"
  64799. #
  64800. # It is also possible to supply a custom prefix.
  64801. #
  64802. # class Invoice < Struct.new(:client)
  64803. # delegate :name, :address, to: :client, prefix: :customer
  64804. # end
  64805. #
  64806. # invoice = Invoice.new(john_doe)
  64807. # invoice.customer_name # => 'John Doe'
  64808. # invoice.customer_address # => 'Vimmersvej 13'
  64809. #
  64810. # If the delegate object is +nil+ an exception is raised, and that happens
  64811. # no matter whether +nil+ responds to the delegated method. You can get a
  64812. # +nil+ instead with the +:allow_nil+ option.
  64813. #
  64814. # class Foo
  64815. # attr_accessor :bar
  64816. # def initialize(bar = nil)
  64817. # @bar = bar
  64818. # end
  64819. # delegate :zoo, to: :bar
  64820. # end
  64821. #
  64822. # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
  64823. #
  64824. # class Foo
  64825. # attr_accessor :bar
  64826. # def initialize(bar = nil)
  64827. # @bar = bar
  64828. # end
  64829. # delegate :zoo, to: :bar, allow_nil: true
  64830. # end
  64831. #
  64832. # Foo.new.zoo # returns nil
  64833. def delegate(*methods)
  64834. options = methods.pop
  64835. unless options.is_a?(Hash) && to = options[:to]
  64836. raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
  64837. end
  64838. prefix, allow_nil = options.values_at(:prefix, :allow_nil)
  64839. if prefix == true && to =~ /^[^a-z_]/
  64840. raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
  64841. end
  64842. method_prefix = \
  64843. if prefix
  64844. "#{prefix == true ? to : prefix}_"
  64845. else
  64846. ''
  64847. end
  64848. file, line = caller.first.split(':', 2)
  64849. line = line.to_i
  64850. to = to.to_s
  64851. to = 'self.class' if to == 'class'
  64852. methods.each do |method|
  64853. # Attribute writer methods only accept one argument. Makes sure []=
  64854. # methods still accept two arguments.
  64855. definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
  64856. if allow_nil
  64857. module_eval(<<-EOS, file, line - 2)
  64858. def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
  64859. if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
  64860. #{to}.#{method}(#{definition}) # client.name(*args, &block)
  64861. end # end
  64862. end # end
  64863. EOS
  64864. else
  64865. exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
  64866. module_eval(<<-EOS, file, line - 1)
  64867. def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
  64868. #{to}.#{method}(#{definition}) # client.name(*args, &block)
  64869. rescue NoMethodError # rescue NoMethodError
  64870. if #{to}.nil? # if client.nil?
  64871. #{exception} # # add helpful message to the exception
  64872. else # else
  64873. raise # raise
  64874. end # end
  64875. end # end
  64876. EOS
  64877. end
  64878. end
  64879. end
  64880. end
  64881. require 'active_support/deprecation/method_wrappers'
  64882. class Module
  64883. # deprecate :foo
  64884. # deprecate bar: 'message'
  64885. # deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
  64886. #
  64887. # You can also use custom deprecator instance:
  64888. #
  64889. # deprecate :foo, deprecator: MyLib::Deprecator.new
  64890. # deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
  64891. #
  64892. # \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt>
  64893. # method where you can implement your custom warning behavior.
  64894. #
  64895. # class MyLib::Deprecator
  64896. # def deprecation_warning(deprecated_method_name, message, caller_backtrace)
  64897. # message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
  64898. # Kernel.warn message
  64899. # end
  64900. # end
  64901. def deprecate(*method_names)
  64902. ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
  64903. end
  64904. end
  64905. require 'active_support/inflector'
  64906. class Module
  64907. # Returns the name of the module containing this one.
  64908. #
  64909. # M::N.parent_name # => "M"
  64910. def parent_name
  64911. if defined? @parent_name
  64912. @parent_name
  64913. else
  64914. @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
  64915. end
  64916. end
  64917. # Returns the module which contains this one according to its name.
  64918. #
  64919. # module M
  64920. # module N
  64921. # end
  64922. # end
  64923. # X = M::N
  64924. #
  64925. # M::N.parent # => M
  64926. # X.parent # => M
  64927. #
  64928. # The parent of top-level and anonymous modules is Object.
  64929. #
  64930. # M.parent # => Object
  64931. # Module.new.parent # => Object
  64932. def parent
  64933. parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
  64934. end
  64935. # Returns all the parents of this module according to its name, ordered from
  64936. # nested outwards. The receiver is not contained within the result.
  64937. #
  64938. # module M
  64939. # module N
  64940. # end
  64941. # end
  64942. # X = M::N
  64943. #
  64944. # M.parents # => [Object]
  64945. # M::N.parents # => [M, Object]
  64946. # X.parents # => [M, Object]
  64947. def parents
  64948. parents = []
  64949. if parent_name
  64950. parts = parent_name.split('::')
  64951. until parts.empty?
  64952. parents << ActiveSupport::Inflector.constantize(parts * '::')
  64953. parts.pop
  64954. end
  64955. end
  64956. parents << Object unless parents.include? Object
  64957. parents
  64958. end
  64959. def local_constants #:nodoc:
  64960. constants(false)
  64961. end
  64962. # *DEPRECATED*: Use +local_constants+ instead.
  64963. #
  64964. # Returns the names of the constants defined locally as strings.
  64965. #
  64966. # module M
  64967. # X = 1
  64968. # end
  64969. # M.local_constant_names # => ["X"]
  64970. #
  64971. # This method is useful for forward compatibility, since Ruby 1.8 returns
  64972. # constant names as strings, whereas 1.9 returns them as symbols.
  64973. def local_constant_names
  64974. ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead'
  64975. local_constants.map { |c| c.to_s }
  64976. end
  64977. end
  64978. require 'active_support/core_ext/string/inflections'
  64979. #--
  64980. # Allows code reuse in the methods below without polluting Module.
  64981. #++
  64982. module QualifiedConstUtils
  64983. def self.raise_if_absolute(path)
  64984. raise NameError.new("wrong constant name #$&") if path =~ /\A::[^:]+/
  64985. end
  64986. def self.names(path)
  64987. path.split('::')
  64988. end
  64989. end
  64990. ##
  64991. # Extends the API for constants to be able to deal with qualified names. Arguments
  64992. # are assumed to be relative to the receiver.
  64993. #
  64994. #--
  64995. # Qualified names are required to be relative because we are extending existing
  64996. # methods that expect constant names, ie, relative paths of length 1. For example,
  64997. # Object.const_get('::String') raises NameError and so does qualified_const_get.
  64998. #++
  64999. class Module
  65000. def qualified_const_defined?(path, search_parents=true)
  65001. QualifiedConstUtils.raise_if_absolute(path)
  65002. QualifiedConstUtils.names(path).inject(self) do |mod, name|
  65003. return unless mod.const_defined?(name, search_parents)
  65004. mod.const_get(name)
  65005. end
  65006. return true
  65007. end
  65008. def qualified_const_get(path)
  65009. QualifiedConstUtils.raise_if_absolute(path)
  65010. QualifiedConstUtils.names(path).inject(self) do |mod, name|
  65011. mod.const_get(name)
  65012. end
  65013. end
  65014. def qualified_const_set(path, value)
  65015. QualifiedConstUtils.raise_if_absolute(path)
  65016. const_name = path.demodulize
  65017. mod_name = path.deconstantize
  65018. mod = mod_name.empty? ? self : qualified_const_get(mod_name)
  65019. mod.const_set(const_name, value)
  65020. end
  65021. end
  65022. require 'active_support/core_ext/module/anonymous'
  65023. require 'active_support/core_ext/string/inflections'
  65024. class Module
  65025. def reachable? #:nodoc:
  65026. !anonymous? && name.safe_constantize.equal?(self)
  65027. end
  65028. end
  65029. class Module
  65030. def remove_possible_method(method)
  65031. if method_defined?(method) || private_method_defined?(method)
  65032. undef_method(method)
  65033. end
  65034. end
  65035. def redefine_method(method, &block)
  65036. remove_possible_method(method)
  65037. define_method(method, &block)
  65038. end
  65039. end
  65040. require 'active_support/core_ext/module/aliasing'
  65041. require 'active_support/core_ext/module/introspection'
  65042. require 'active_support/core_ext/module/anonymous'
  65043. require 'active_support/core_ext/module/reachable'
  65044. require 'active_support/core_ext/module/attribute_accessors'
  65045. require 'active_support/core_ext/module/attr_internal'
  65046. require 'active_support/core_ext/module/delegation'
  65047. require 'active_support/core_ext/module/deprecation'
  65048. require 'active_support/core_ext/module/remove_method'
  65049. require 'active_support/core_ext/module/qualified_const'
  65050. class NameError
  65051. # Extract the name of the missing constant from the exception message.
  65052. def missing_name
  65053. if /undefined local variable or method/ !~ message
  65054. $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
  65055. end
  65056. end
  65057. # Was this exception raised because the given name was missing?
  65058. def missing_name?(name)
  65059. if name.is_a? Symbol
  65060. last_name = (missing_name || '').split('::').last
  65061. last_name == name.to_s
  65062. else
  65063. missing_name == name.to_s
  65064. end
  65065. end
  65066. end
  65067. class Numeric
  65068. KILOBYTE = 1024
  65069. MEGABYTE = KILOBYTE * 1024
  65070. GIGABYTE = MEGABYTE * 1024
  65071. TERABYTE = GIGABYTE * 1024
  65072. PETABYTE = TERABYTE * 1024
  65073. EXABYTE = PETABYTE * 1024
  65074. # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
  65075. def bytes
  65076. self
  65077. end
  65078. alias :byte :bytes
  65079. def kilobytes
  65080. self * KILOBYTE
  65081. end
  65082. alias :kilobyte :kilobytes
  65083. def megabytes
  65084. self * MEGABYTE
  65085. end
  65086. alias :megabyte :megabytes
  65087. def gigabytes
  65088. self * GIGABYTE
  65089. end
  65090. alias :gigabyte :gigabytes
  65091. def terabytes
  65092. self * TERABYTE
  65093. end
  65094. alias :terabyte :terabytes
  65095. def petabytes
  65096. self * PETABYTE
  65097. end
  65098. alias :petabyte :petabytes
  65099. def exabytes
  65100. self * EXABYTE
  65101. end
  65102. alias :exabyte :exabytes
  65103. end
  65104. require 'active_support/core_ext/big_decimal/conversions'
  65105. require 'active_support/number_helper'
  65106. class Numeric
  65107. # Provides options for converting numbers into formatted strings.
  65108. # Options are provided for phone numbers, currency, percentage,
  65109. # precision, positional notation, file size and pretty printing.
  65110. #
  65111. # ==== Options
  65112. #
  65113. # For details on which formats use which options, see ActiveSupport::NumberHelper
  65114. #
  65115. # ==== Examples
  65116. #
  65117. # Phone Numbers:
  65118. # 5551234.to_s(:phone) # => 555-1234
  65119. # 1235551234.to_s(:phone) # => 123-555-1234
  65120. # 1235551234.to_s(:phone, area_code: true) # => (123) 555-1234
  65121. # 1235551234.to_s(:phone, delimiter: ' ') # => 123 555 1234
  65122. # 1235551234.to_s(:phone, area_code: true, extension: 555) # => (123) 555-1234 x 555
  65123. # 1235551234.to_s(:phone, country_code: 1) # => +1-123-555-1234
  65124. # 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
  65125. # # => +1.123.555.1234 x 1343
  65126. #
  65127. # Currency:
  65128. # 1234567890.50.to_s(:currency) # => $1,234,567,890.50
  65129. # 1234567890.506.to_s(:currency) # => $1,234,567,890.51
  65130. # 1234567890.506.to_s(:currency, precision: 3) # => $1,234,567,890.506
  65131. # 1234567890.506.to_s(:currency, locale: :fr) # => 1 234 567 890,51
  65132. # -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
  65133. # # => ($1,234,567,890.50)
  65134. # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '')
  65135. # # => &pound;1234567890,50
  65136. # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
  65137. # # => 1234567890,50 &pound;
  65138. #
  65139. # Percentage:
  65140. # 100.to_s(:percentage) # => 100.000%
  65141. # 100.to_s(:percentage, precision: 0) # => 100%
  65142. # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => 1.000,000%
  65143. # 302.24398923423.to_s(:percentage, precision: 5) # => 302.24399%
  65144. # 1000.to_s(:percentage, locale: :fr) # => 1 000,000%
  65145. # 100.to_s(:percentage, format: '%n %') # => 100 %
  65146. #
  65147. # Delimited:
  65148. # 12345678.to_s(:delimited) # => 12,345,678
  65149. # 12345678.05.to_s(:delimited) # => 12,345,678.05
  65150. # 12345678.to_s(:delimited, delimiter: '.') # => 12.345.678
  65151. # 12345678.to_s(:delimited, delimiter: ',') # => 12,345,678
  65152. # 12345678.05.to_s(:delimited, separator: ' ') # => 12,345,678 05
  65153. # 12345678.05.to_s(:delimited, locale: :fr) # => 12 345 678,05
  65154. # 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
  65155. # # => 98 765 432,98
  65156. #
  65157. # Rounded:
  65158. # 111.2345.to_s(:rounded) # => 111.235
  65159. # 111.2345.to_s(:rounded, precision: 2) # => 111.23
  65160. # 13.to_s(:rounded, precision: 5) # => 13.00000
  65161. # 389.32314.to_s(:rounded, precision: 0) # => 389
  65162. # 111.2345.to_s(:rounded, significant: true) # => 111
  65163. # 111.2345.to_s(:rounded, precision: 1, significant: true) # => 100
  65164. # 13.to_s(:rounded, precision: 5, significant: true) # => 13.000
  65165. # 111.234.to_s(:rounded, locale: :fr) # => 111,234
  65166. # 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
  65167. # # => 13
  65168. # 389.32314.to_s(:rounded, precision: 4, significant: true) # => 389.3
  65169. # 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
  65170. # # => 1.111,23
  65171. #
  65172. # Human-friendly size in Bytes:
  65173. # 123.to_s(:human_size) # => 123 Bytes
  65174. # 1234.to_s(:human_size) # => 1.21 KB
  65175. # 12345.to_s(:human_size) # => 12.1 KB
  65176. # 1234567.to_s(:human_size) # => 1.18 MB
  65177. # 1234567890.to_s(:human_size) # => 1.15 GB
  65178. # 1234567890123.to_s(:human_size) # => 1.12 TB
  65179. # 1234567.to_s(:human_size, precision: 2) # => 1.2 MB
  65180. # 483989.to_s(:human_size, precision: 2) # => 470 KB
  65181. # 1234567.to_s(:human_size, precision: 2, separator: ',') # => 1,2 MB
  65182. # 1234567890123.to_s(:human_size, precision: 5) # => "1.1229 TB"
  65183. # 524288000.to_s(:human_size, precision: 5) # => "500 MB"
  65184. #
  65185. # Human-friendly format:
  65186. # 123.to_s(:human) # => "123"
  65187. # 1234.to_s(:human) # => "1.23 Thousand"
  65188. # 12345.to_s(:human) # => "12.3 Thousand"
  65189. # 1234567.to_s(:human) # => "1.23 Million"
  65190. # 1234567890.to_s(:human) # => "1.23 Billion"
  65191. # 1234567890123.to_s(:human) # => "1.23 Trillion"
  65192. # 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
  65193. # 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
  65194. # 489939.to_s(:human, precision: 2) # => "490 Thousand"
  65195. # 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
  65196. # 1234567.to_s(:human, precision: 4,
  65197. # significant: false) # => "1.2346 Million"
  65198. # 1234567.to_s(:human, precision: 1,
  65199. # separator: ',',
  65200. # significant: false) # => "1,2 Million"
  65201. def to_formatted_s(format = :default, options = {})
  65202. case format
  65203. when :phone
  65204. return ActiveSupport::NumberHelper.number_to_phone(self, options)
  65205. when :currency
  65206. return ActiveSupport::NumberHelper.number_to_currency(self, options)
  65207. when :percentage
  65208. return ActiveSupport::NumberHelper.number_to_percentage(self, options)
  65209. when :delimited
  65210. return ActiveSupport::NumberHelper.number_to_delimited(self, options)
  65211. when :rounded
  65212. return ActiveSupport::NumberHelper.number_to_rounded(self, options)
  65213. when :human
  65214. return ActiveSupport::NumberHelper.number_to_human(self, options)
  65215. when :human_size
  65216. return ActiveSupport::NumberHelper.number_to_human_size(self, options)
  65217. else
  65218. self.to_default_s
  65219. end
  65220. end
  65221. [Float, Fixnum, Bignum, BigDecimal].each do |klass|
  65222. klass.send(:alias_method, :to_default_s, :to_s)
  65223. klass.send(:define_method, :to_s) do |*args|
  65224. if args[0].is_a?(Symbol)
  65225. format = args[0]
  65226. options = args[1] || {}
  65227. self.to_formatted_s(format, options)
  65228. else
  65229. to_default_s(*args)
  65230. end
  65231. end
  65232. end
  65233. end
  65234. require 'active_support/core_ext/infinite_comparable'
  65235. class Float
  65236. include InfiniteComparable
  65237. end
  65238. class BigDecimal
  65239. include InfiniteComparable
  65240. end
  65241. require 'active_support/duration'
  65242. require 'active_support/core_ext/time/calculations'
  65243. require 'active_support/core_ext/time/acts_like'
  65244. class Numeric
  65245. # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
  65246. #
  65247. # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
  65248. # as well as adding or subtracting their results from a Time object. For example:
  65249. #
  65250. # # equivalent to Time.current.advance(months: 1)
  65251. # 1.month.from_now
  65252. #
  65253. # # equivalent to Time.current.advance(years: 2)
  65254. # 2.years.from_now
  65255. #
  65256. # # equivalent to Time.current.advance(months: 4, years: 5)
  65257. # (4.months + 5.years).from_now
  65258. #
  65259. # While these methods provide precise calculation when used as in the examples above, care
  65260. # should be taken to note that this is not true if the result of `months', `years', etc is
  65261. # converted before use:
  65262. #
  65263. # # equivalent to 30.days.to_i.from_now
  65264. # 1.month.to_i.from_now
  65265. #
  65266. # # equivalent to 365.25.days.to_f.from_now
  65267. # 1.year.to_f.from_now
  65268. #
  65269. # In such cases, Ruby's core
  65270. # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
  65271. # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
  65272. # date and time arithmetic.
  65273. def seconds
  65274. ActiveSupport::Duration.new(self, [[:seconds, self]])
  65275. end
  65276. alias :second :seconds
  65277. def minutes
  65278. ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
  65279. end
  65280. alias :minute :minutes
  65281. def hours
  65282. ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
  65283. end
  65284. alias :hour :hours
  65285. def days
  65286. ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
  65287. end
  65288. alias :day :days
  65289. def weeks
  65290. ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
  65291. end
  65292. alias :week :weeks
  65293. def fortnights
  65294. ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
  65295. end
  65296. alias :fortnight :fortnights
  65297. # Reads best without arguments: 10.minutes.ago
  65298. def ago(time = ::Time.current)
  65299. time - self
  65300. end
  65301. # Reads best with argument: 10.minutes.until(time)
  65302. alias :until :ago
  65303. # Reads best with argument: 10.minutes.since(time)
  65304. def since(time = ::Time.current)
  65305. time + self
  65306. end
  65307. # Reads best without arguments: 10.minutes.from_now
  65308. alias :from_now :since
  65309. end
  65310. require 'active_support/core_ext/numeric/bytes'
  65311. require 'active_support/core_ext/numeric/time'
  65312. require 'active_support/core_ext/numeric/conversions'
  65313. require 'active_support/core_ext/numeric/infinite_comparable'
  65314. class Object
  65315. # A duck-type assistant method. For example, Active Support extends Date
  65316. # to define an <tt>acts_like_date?</tt> method, and extends Time to define
  65317. # <tt>acts_like_time?</tt>. As a result, we can do <tt>x.acts_like?(:time)</tt> and
  65318. # <tt>x.acts_like?(:date)</tt> to do duck-type-safe comparisons, since classes that
  65319. # we want to act like Time simply need to define an <tt>acts_like_time?</tt> method.
  65320. def acts_like?(duck)
  65321. respond_to? :"acts_like_#{duck}?"
  65322. end
  65323. end
  65324. # encoding: utf-8
  65325. class Object
  65326. # An object is blank if it's false, empty, or a whitespace string.
  65327. # For example, '', ' ', +nil+, [], and {} are all blank.
  65328. #
  65329. # This simplifies:
  65330. #
  65331. # if address.nil? || address.empty?
  65332. #
  65333. # ...to:
  65334. #
  65335. # if address.blank?
  65336. def blank?
  65337. respond_to?(:empty?) ? empty? : !self
  65338. end
  65339. # An object is present if it's not <tt>blank?</tt>.
  65340. def present?
  65341. !blank?
  65342. end
  65343. # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
  65344. # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
  65345. #
  65346. # This is handy for any representation of objects where blank is the same
  65347. # as not present at all. For example, this simplifies a common check for
  65348. # HTTP POST/query parameters:
  65349. #
  65350. # state = params[:state] if params[:state].present?
  65351. # country = params[:country] if params[:country].present?
  65352. # region = state || country || 'US'
  65353. #
  65354. # ...becomes:
  65355. #
  65356. # region = params[:state].presence || params[:country].presence || 'US'
  65357. def presence
  65358. self if present?
  65359. end
  65360. end
  65361. class NilClass
  65362. # +nil+ is blank:
  65363. #
  65364. # nil.blank? # => true
  65365. def blank?
  65366. true
  65367. end
  65368. end
  65369. class FalseClass
  65370. # +false+ is blank:
  65371. #
  65372. # false.blank? # => true
  65373. def blank?
  65374. true
  65375. end
  65376. end
  65377. class TrueClass
  65378. # +true+ is not blank:
  65379. #
  65380. # true.blank? # => false
  65381. def blank?
  65382. false
  65383. end
  65384. end
  65385. class Array
  65386. # An array is blank if it's empty:
  65387. #
  65388. # [].blank? # => true
  65389. # [1,2,3].blank? # => false
  65390. alias_method :blank?, :empty?
  65391. end
  65392. class Hash
  65393. # A hash is blank if it's empty:
  65394. #
  65395. # {}.blank? # => true
  65396. # { key: 'value' }.blank? # => false
  65397. alias_method :blank?, :empty?
  65398. end
  65399. class String
  65400. # A string is blank if it's empty or contains whitespaces only:
  65401. #
  65402. # ''.blank? # => true
  65403. # ' '.blank? # => true
  65404. # ' '.blank? # => true
  65405. # ' something here '.blank? # => false
  65406. def blank?
  65407. self !~ /[^[:space:]]/
  65408. end
  65409. end
  65410. class Numeric #:nodoc:
  65411. # No number is blank:
  65412. #
  65413. # 1.blank? # => false
  65414. # 0.blank? # => false
  65415. def blank?
  65416. false
  65417. end
  65418. end
  65419. require 'active_support/core_ext/object/to_param'
  65420. require 'active_support/core_ext/object/to_query'
  65421. require 'active_support/core_ext/array/conversions'
  65422. require 'active_support/core_ext/hash/conversions'
  65423. require 'active_support/core_ext/object/duplicable'
  65424. class Object
  65425. # Returns a deep copy of object if it's duplicable. If it's
  65426. # not duplicable, returns +self+.
  65427. #
  65428. # object = Object.new
  65429. # dup = object.deep_dup
  65430. # dup.instance_variable_set(:@a, 1)
  65431. #
  65432. # object.instance_variable_defined?(:@a) #=> false
  65433. # dup.instance_variable_defined?(:@a) #=> true
  65434. def deep_dup
  65435. duplicable? ? dup : self
  65436. end
  65437. end
  65438. class Array
  65439. # Returns a deep copy of array.
  65440. #
  65441. # array = [1, [2, 3]]
  65442. # dup = array.deep_dup
  65443. # dup[1][2] = 4
  65444. #
  65445. # array[1][2] #=> nil
  65446. # dup[1][2] #=> 4
  65447. def deep_dup
  65448. map { |it| it.deep_dup }
  65449. end
  65450. end
  65451. class Hash
  65452. # Returns a deep copy of hash.
  65453. #
  65454. # hash = { a: { b: 'b' } }
  65455. # dup = hash.deep_dup
  65456. # dup[:a][:c] = 'c'
  65457. #
  65458. # hash[:a][:c] #=> nil
  65459. # dup[:a][:c] #=> "c"
  65460. def deep_dup
  65461. each_with_object(dup) do |(key, value), hash|
  65462. hash[key.deep_dup] = value.deep_dup
  65463. end
  65464. end
  65465. end
  65466. #--
  65467. # Most objects are cloneable, but not all. For example you can't dup +nil+:
  65468. #
  65469. # nil.dup # => TypeError: can't dup NilClass
  65470. #
  65471. # Classes may signal their instances are not duplicable removing +dup+/+clone+
  65472. # or raising exceptions from them. So, to dup an arbitrary object you normally
  65473. # use an optimistic approach and are ready to catch an exception, say:
  65474. #
  65475. # arbitrary_object.dup rescue object
  65476. #
  65477. # Rails dups objects in a few critical spots where they are not that arbitrary.
  65478. # That rescue is very expensive (like 40 times slower than a predicate), and it
  65479. # is often triggered.
  65480. #
  65481. # That's why we hardcode the following cases and check duplicable? instead of
  65482. # using that rescue idiom.
  65483. #++
  65484. class Object
  65485. # Can you safely dup this object?
  65486. #
  65487. # False for +nil+, +false+, +true+, symbol, and number objects;
  65488. # true otherwise.
  65489. def duplicable?
  65490. true
  65491. end
  65492. end
  65493. class NilClass
  65494. # +nil+ is not duplicable:
  65495. #
  65496. # nil.duplicable? # => false
  65497. # nil.dup # => TypeError: can't dup NilClass
  65498. def duplicable?
  65499. false
  65500. end
  65501. end
  65502. class FalseClass
  65503. # +false+ is not duplicable:
  65504. #
  65505. # false.duplicable? # => false
  65506. # false.dup # => TypeError: can't dup FalseClass
  65507. def duplicable?
  65508. false
  65509. end
  65510. end
  65511. class TrueClass
  65512. # +true+ is not duplicable:
  65513. #
  65514. # true.duplicable? # => false
  65515. # true.dup # => TypeError: can't dup TrueClass
  65516. def duplicable?
  65517. false
  65518. end
  65519. end
  65520. class Symbol
  65521. # Symbols are not duplicable:
  65522. #
  65523. # :my_symbol.duplicable? # => false
  65524. # :my_symbol.dup # => TypeError: can't dup Symbol
  65525. def duplicable?
  65526. false
  65527. end
  65528. end
  65529. class Numeric
  65530. # Numbers are not duplicable:
  65531. #
  65532. # 3.duplicable? # => false
  65533. # 3.dup # => TypeError: can't dup Fixnum
  65534. def duplicable?
  65535. false
  65536. end
  65537. end
  65538. require 'bigdecimal'
  65539. class BigDecimal
  65540. begin
  65541. BigDecimal.new('4.56').dup
  65542. def duplicable?
  65543. true
  65544. end
  65545. rescue TypeError
  65546. # can't dup, so use superclass implementation
  65547. end
  65548. end
  65549. class Object
  65550. # Returns true if this object is included in the argument(s). Argument must be
  65551. # any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage:
  65552. #
  65553. # characters = ['Konata', 'Kagami', 'Tsukasa']
  65554. # 'Konata'.in?(characters) # => true
  65555. #
  65556. # character = 'Konata'
  65557. # character.in?('Konata', 'Kagami', 'Tsukasa') # => true
  65558. #
  65559. # This will throw an ArgumentError if a single argument is passed in and it doesn't respond
  65560. # to +#include?+.
  65561. def in?(*args)
  65562. if args.length > 1
  65563. args.include? self
  65564. else
  65565. another_object = args.first
  65566. if another_object.respond_to? :include?
  65567. another_object.include? self
  65568. else
  65569. raise ArgumentError.new 'The single parameter passed to #in? must respond to #include?'
  65570. end
  65571. end
  65572. end
  65573. end
  65574. class Object
  65575. # Returns a hash with string keys that maps instance variable names without "@" to their
  65576. # corresponding values.
  65577. #
  65578. # class C
  65579. # def initialize(x, y)
  65580. # @x, @y = x, y
  65581. # end
  65582. # end
  65583. #
  65584. # C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
  65585. def instance_values
  65586. Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
  65587. end
  65588. # Returns an array of instance variable names as strings including "@".
  65589. #
  65590. # class C
  65591. # def initialize(x, y)
  65592. # @x, @y = x, y
  65593. # end
  65594. # end
  65595. #
  65596. # C.new(0, 1).instance_variable_names # => ["@y", "@x"]
  65597. def instance_variable_names
  65598. instance_variables.map { |var| var.to_s }
  65599. end
  65600. end
  65601. # Hack to load json gem first so we can overwrite its to_json.
  65602. begin
  65603. require 'json'
  65604. rescue LoadError
  65605. end
  65606. # The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
  65607. # their default behavior. That said, we need to define the basic to_json method in all of them,
  65608. # otherwise they will always use to_json gem implementation, which is backwards incompatible in
  65609. # several cases (for instance, the JSON implementation for Hash does not work) with inheritance
  65610. # and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
  65611. [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
  65612. klass.class_eval do
  65613. # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
  65614. def to_json(options = nil)
  65615. ActiveSupport::JSON.encode(self, options)
  65616. end
  65617. end
  65618. end
  65619. module Process
  65620. class Status
  65621. def as_json(options = nil)
  65622. { :exitstatus => exitstatus, :pid => pid }
  65623. end
  65624. end
  65625. end
  65626. class Object
  65627. # Alias of <tt>to_s</tt>.
  65628. def to_param
  65629. to_s
  65630. end
  65631. end
  65632. class NilClass
  65633. # Returns +self+.
  65634. def to_param
  65635. self
  65636. end
  65637. end
  65638. class TrueClass
  65639. # Returns +self+.
  65640. def to_param
  65641. self
  65642. end
  65643. end
  65644. class FalseClass
  65645. # Returns +self+.
  65646. def to_param
  65647. self
  65648. end
  65649. end
  65650. class Array
  65651. # Calls <tt>to_param</tt> on all its elements and joins the result with
  65652. # slashes. This is used by <tt>url_for</tt> in Action Pack.
  65653. def to_param
  65654. collect { |e| e.to_param }.join '/'
  65655. end
  65656. end
  65657. class Hash
  65658. # Returns a string representation of the receiver suitable for use as a URL
  65659. # query string:
  65660. #
  65661. # {name: 'David', nationality: 'Danish'}.to_param
  65662. # # => "name=David&nationality=Danish"
  65663. #
  65664. # An optional namespace can be passed to enclose the param names:
  65665. #
  65666. # {name: 'David', nationality: 'Danish'}.to_param('user')
  65667. # # => "user[name]=David&user[nationality]=Danish"
  65668. #
  65669. # The string pairs "key=value" that conform the query string
  65670. # are sorted lexicographically in ascending order.
  65671. #
  65672. # This method is also aliased as +to_query+.
  65673. def to_param(namespace = nil)
  65674. collect do |key, value|
  65675. value.to_query(namespace ? "#{namespace}[#{key}]" : key)
  65676. end.sort * '&'
  65677. end
  65678. end
  65679. require 'active_support/core_ext/object/to_param'
  65680. class Object
  65681. # Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
  65682. # param name.
  65683. #
  65684. # Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
  65685. def to_query(key)
  65686. require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
  65687. "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
  65688. end
  65689. end
  65690. class Array
  65691. # Converts an array into a string suitable for use as a URL query string,
  65692. # using the given +key+ as the param name.
  65693. #
  65694. # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
  65695. def to_query(key)
  65696. prefix = "#{key}[]"
  65697. collect { |value| value.to_query(prefix) }.join '&'
  65698. end
  65699. end
  65700. class Hash
  65701. alias_method :to_query, :to_param
  65702. end
  65703. class Object
  65704. # Invokes the public method whose name goes as first argument just like
  65705. # +public_send+ does, except that if the receiver does not respond to it the
  65706. # call returns +nil+ rather than raising an exception.
  65707. #
  65708. # This method is defined to be able to write
  65709. #
  65710. # @person.try(:name)
  65711. #
  65712. # instead of
  65713. #
  65714. # @person ? @person.name : nil
  65715. #
  65716. # +try+ returns +nil+ when called on +nil+ regardless of whether it responds
  65717. # to the method:
  65718. #
  65719. # nil.try(:to_i) # => nil, rather than 0
  65720. #
  65721. # Arguments and blocks are forwarded to the method if invoked:
  65722. #
  65723. # @posts.try(:each_slice, 2) do |a, b|
  65724. # ...
  65725. # end
  65726. #
  65727. # The number of arguments in the signature must match. If the object responds
  65728. # to the method the call is attempted and +ArgumentError+ is still raised
  65729. # otherwise.
  65730. #
  65731. # If +try+ is called without arguments it yields the receiver to a given
  65732. # block unless it is +nil+:
  65733. #
  65734. # @person.try do |p|
  65735. # ...
  65736. # end
  65737. #
  65738. # Please also note that +try+ is defined on +Object+, therefore it won't work
  65739. # with instances of classes that do not have +Object+ among their ancestors,
  65740. # like direct subclasses of +BasicObject+. For example, using +try+ with
  65741. # +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
  65742. # delegator itself.
  65743. def try(*a, &b)
  65744. if a.empty? && block_given?
  65745. yield self
  65746. else
  65747. public_send(*a, &b) if respond_to?(a.first)
  65748. end
  65749. end
  65750. # Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
  65751. # does not implemented the tried method.
  65752. def try!(*a, &b)
  65753. if a.empty? && block_given?
  65754. yield self
  65755. else
  65756. public_send(*a, &b)
  65757. end
  65758. end
  65759. end
  65760. class NilClass
  65761. # Calling +try+ on +nil+ always returns +nil+.
  65762. # It becomes specially helpful when navigating through associations that may return +nil+.
  65763. #
  65764. # nil.try(:name) # => nil
  65765. #
  65766. # Without +try+
  65767. # @person && !@person.children.blank? && @person.children.first.name
  65768. #
  65769. # With +try+
  65770. # @person.try(:children).try(:first).try(:name)
  65771. def try(*args)
  65772. nil
  65773. end
  65774. def try!(*args)
  65775. nil
  65776. end
  65777. end
  65778. require 'active_support/option_merger'
  65779. class Object
  65780. # An elegant way to factor duplication out of options passed to a series of
  65781. # method calls. Each method called in the block, with the block variable as
  65782. # the receiver, will have its options merged with the default +options+ hash
  65783. # provided. Each method called on the block variable must take an options
  65784. # hash as its final argument.
  65785. #
  65786. # Without <tt>with_options></tt>, this code contains duplication:
  65787. #
  65788. # class Account < ActiveRecord::Base
  65789. # has_many :customers, dependent: :destroy
  65790. # has_many :products, dependent: :destroy
  65791. # has_many :invoices, dependent: :destroy
  65792. # has_many :expenses, dependent: :destroy
  65793. # end
  65794. #
  65795. # Using <tt>with_options</tt>, we can remove the duplication:
  65796. #
  65797. # class Account < ActiveRecord::Base
  65798. # with_options dependent: :destroy do |assoc|
  65799. # assoc.has_many :customers
  65800. # assoc.has_many :products
  65801. # assoc.has_many :invoices
  65802. # assoc.has_many :expenses
  65803. # end
  65804. # end
  65805. #
  65806. # It can also be used with an explicit receiver:
  65807. #
  65808. # I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
  65809. # subject i18n.t :subject
  65810. # body i18n.t :body, user_name: user.name
  65811. # end
  65812. #
  65813. # <tt>with_options</tt> can also be nested since the call is forwarded to its receiver.
  65814. # Each nesting level will merge inherited defaults in addition to their own.
  65815. def with_options(options)
  65816. yield ActiveSupport::OptionMerger.new(self, options)
  65817. end
  65818. end
  65819. require 'active_support/core_ext/object/acts_like'
  65820. require 'active_support/core_ext/object/blank'
  65821. require 'active_support/core_ext/object/duplicable'
  65822. require 'active_support/core_ext/object/deep_dup'
  65823. require 'active_support/core_ext/object/try'
  65824. require 'active_support/core_ext/object/inclusion'
  65825. require 'active_support/core_ext/object/conversions'
  65826. require 'active_support/core_ext/object/instance_variables'
  65827. require 'active_support/core_ext/object/to_json'
  65828. require 'active_support/core_ext/object/to_param'
  65829. require 'active_support/core_ext/object/to_query'
  65830. require 'active_support/core_ext/object/with_options'
  65831. require "active_support/core_ext/kernel/singleton_class"
  65832. require "active_support/deprecation"
  65833. class Proc #:nodoc:
  65834. def bind(object)
  65835. ActiveSupport::Deprecation.warn 'Proc#bind is deprecated and will be removed in future versions'
  65836. block, time = self, Time.now
  65837. object.class_eval do
  65838. method_name = "__bind_#{time.to_i}_#{time.usec}"
  65839. define_method(method_name, &block)
  65840. method = instance_method(method_name)
  65841. remove_method(method_name)
  65842. method
  65843. end.bind(object)
  65844. end
  65845. end
  65846. class Range
  65847. RANGE_FORMATS = {
  65848. :db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
  65849. }
  65850. # Gives a human readable format of the range.
  65851. #
  65852. # (1..100).to_formatted_s # => "1..100"
  65853. def to_formatted_s(format = :default)
  65854. if formatter = RANGE_FORMATS[format]
  65855. formatter.call(first, last)
  65856. else
  65857. to_default_s
  65858. end
  65859. end
  65860. alias_method :to_default_s, :to_s
  65861. alias_method :to_s, :to_formatted_s
  65862. end
  65863. class Range
  65864. # Extends the default Range#include? to support range comparisons.
  65865. # (1..5).include?(1..5) # => true
  65866. # (1..5).include?(2..3) # => true
  65867. # (1..5).include?(2..6) # => false
  65868. #
  65869. # The native Range#include? behavior is untouched.
  65870. # ('a'..'f').include?('c') # => true
  65871. # (5..9).include?(11) # => false
  65872. def include_with_range?(value)
  65873. if value.is_a?(::Range)
  65874. # 1...10 includes 1..9 but it does not include 1..10.
  65875. operator = exclude_end? && !value.exclude_end? ? :< : :<=
  65876. include_without_range?(value.first) && value.last.send(operator, last)
  65877. else
  65878. include_without_range?(value)
  65879. end
  65880. end
  65881. alias_method_chain :include?, :range
  65882. end
  65883. class Range
  65884. # Compare two ranges and see if they overlap each other
  65885. # (1..5).overlaps?(4..6) # => true
  65886. # (1..5).overlaps?(7..9) # => false
  65887. def overlaps?(other)
  65888. cover?(other.first) || other.cover?(first)
  65889. end
  65890. end
  65891. require 'active_support/core_ext/range/conversions'
  65892. require 'active_support/core_ext/range/include_range'
  65893. require 'active_support/core_ext/range/overlaps'
  65894. class Regexp #:nodoc:
  65895. def multiline?
  65896. options & MULTILINE == MULTILINE
  65897. end
  65898. end
  65899. class String
  65900. # If you pass a single Fixnum, returns a substring of one character at that
  65901. # position. The first character of the string is at position 0, the next at
  65902. # position 1, and so on. If a range is supplied, a substring containing
  65903. # characters at offsets given by the range is returned. In both cases, if an
  65904. # offset is negative, it is counted from the end of the string. Returns nil
  65905. # if the initial offset falls outside the string. Returns an empty string if
  65906. # the beginning of the range is greater than the end of the string.
  65907. #
  65908. # str = "hello"
  65909. # str.at(0) #=> "h"
  65910. # str.at(1..3) #=> "ell"
  65911. # str.at(-2) #=> "l"
  65912. # str.at(-2..-1) #=> "lo"
  65913. # str.at(5) #=> nil
  65914. # str.at(5..-1) #=> ""
  65915. #
  65916. # If a Regexp is given, the matching portion of the string is returned.
  65917. # If a String is given, that given string is returned if it occurs in
  65918. # the string. In both cases, nil is returned if there is no match.
  65919. #
  65920. # str = "hello"
  65921. # str.at(/lo/) #=> "lo"
  65922. # str.at(/ol/) #=> nil
  65923. # str.at("lo") #=> "lo"
  65924. # str.at("ol") #=> nil
  65925. def at(position)
  65926. self[position]
  65927. end
  65928. # Returns a substring from the given position to the end of the string.
  65929. # If the position is negative, it is counted from the end of the string.
  65930. #
  65931. # str = "hello"
  65932. # str.from(0) #=> "hello"
  65933. # str.from(3) #=> "lo"
  65934. # str.from(-2) #=> "lo"
  65935. #
  65936. # You can mix it with +to+ method and do fun things like:
  65937. #
  65938. # str = "hello"
  65939. # str.from(0).to(-1) #=> "hello"
  65940. # str.from(1).to(-2) #=> "ell"
  65941. def from(position)
  65942. self[position..-1]
  65943. end
  65944. # Returns a substring from the beginning of the string to the given position.
  65945. # If the position is negative, it is counted from the end of the string.
  65946. #
  65947. # str = "hello"
  65948. # str.to(0) #=> "h"
  65949. # str.to(3) #=> "hell"
  65950. # str.to(-2) #=> "hell"
  65951. #
  65952. # You can mix it with +from+ method and do fun things like:
  65953. #
  65954. # str = "hello"
  65955. # str.from(0).to(-1) #=> "hello"
  65956. # str.from(1).to(-2) #=> "ell"
  65957. def to(position)
  65958. self[0..position]
  65959. end
  65960. # Returns the first character. If a limit is supplied, returns a substring
  65961. # from the beginning of the string until it reaches the limit value. If the
  65962. # given limit is greater than or equal to the string length, returns self.
  65963. #
  65964. # str = "hello"
  65965. # str.first #=> "h"
  65966. # str.first(1) #=> "h"
  65967. # str.first(2) #=> "he"
  65968. # str.first(0) #=> ""
  65969. # str.first(6) #=> "hello"
  65970. def first(limit = 1)
  65971. if limit == 0
  65972. ''
  65973. elsif limit >= size
  65974. self
  65975. else
  65976. to(limit - 1)
  65977. end
  65978. end
  65979. # Returns the last character of the string. If a limit is supplied, returns a substring
  65980. # from the end of the string until it reaches the limit value (counting backwards). If
  65981. # the given limit is greater than or equal to the string length, returns self.
  65982. #
  65983. # str = "hello"
  65984. # str.last #=> "o"
  65985. # str.last(1) #=> "o"
  65986. # str.last(2) #=> "lo"
  65987. # str.last(0) #=> ""
  65988. # str.last(6) #=> "hello"
  65989. def last(limit = 1)
  65990. if limit == 0
  65991. ''
  65992. elsif limit >= size
  65993. self
  65994. else
  65995. from(-limit)
  65996. end
  65997. end
  65998. end
  65999. class String
  66000. # Enable more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
  66001. def acts_like_string?
  66002. true
  66003. end
  66004. end
  66005. require 'date'
  66006. require 'active_support/core_ext/time/calculations'
  66007. class String
  66008. # Converts a string to a Time value.
  66009. # The +form+ can be either :utc or :local (default :local).
  66010. #
  66011. # The time is parsed using Time.parse method.
  66012. # If +form+ is :local, then the time is in the system timezone.
  66013. # If the date part is missing then the current date is used and if
  66014. # the time part is missing then it is assumed to be 00:00:00.
  66015. #
  66016. # "13-12-2012".to_time # => 2012-12-13 00:00:00 +0100
  66017. # "06:12".to_time # => 2012-12-13 06:12:00 +0100
  66018. # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100
  66019. # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
  66020. # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC
  66021. def to_time(form = :local)
  66022. parts = Date._parse(self, false)
  66023. return if parts.empty?
  66024. now = Time.now
  66025. offset = parts[:offset]
  66026. utc_offset = form == :utc ? 0 : now.utc_offset
  66027. adjustment = offset ? offset - utc_offset : 0
  66028. Time.send(
  66029. form,
  66030. parts.fetch(:year, now.year),
  66031. parts.fetch(:mon, now.month),
  66032. parts.fetch(:mday, now.day),
  66033. parts.fetch(:hour, 0),
  66034. parts.fetch(:min, 0),
  66035. parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0)
  66036. ) - adjustment
  66037. end
  66038. # Converts a string to a Date value.
  66039. #
  66040. # "1-1-2012".to_date #=> Sun, 01 Jan 2012
  66041. # "01/01/2012".to_date #=> Sun, 01 Jan 2012
  66042. # "2012-12-13".to_date #=> Thu, 13 Dec 2012
  66043. # "12/13/2012".to_date #=> ArgumentError: invalid date
  66044. def to_date
  66045. ::Date.parse(self, false) unless blank?
  66046. end
  66047. # Converts a string to a DateTime value.
  66048. #
  66049. # "1-1-2012".to_datetime #=> Sun, 01 Jan 2012 00:00:00 +0000
  66050. # "01/01/2012 23:59:59".to_datetime #=> Sun, 01 Jan 2012 23:59:59 +0000
  66051. # "2012-12-13 12:50".to_datetime #=> Thu, 13 Dec 2012 12:50:00 +0000
  66052. # "12/13/2012".to_datetime #=> ArgumentError: invalid date
  66053. def to_datetime
  66054. ::DateTime.parse(self, false) unless blank?
  66055. end
  66056. end
  66057. require 'active_support/deprecation'
  66058. class String
  66059. def encoding_aware?
  66060. ActiveSupport::Deprecation.warn 'String#encoding_aware? is deprecated'
  66061. true
  66062. end
  66063. end
  66064. class String
  66065. # The inverse of <tt>String#include?</tt>. Returns true if the string
  66066. # does not include the other string.
  66067. #
  66068. # "hello".exclude? "lo" #=> false
  66069. # "hello".exclude? "ol" #=> true
  66070. # "hello".exclude? ?h #=> false
  66071. def exclude?(string)
  66072. !include?(string)
  66073. end
  66074. end
  66075. class String
  66076. # Returns the string, first removing all whitespace on both ends of
  66077. # the string, and then changing remaining consecutive whitespace
  66078. # groups into one space each.
  66079. #
  66080. # Note that it handles both ASCII and Unicode whitespace like mongolian vowel separator (U+180E).
  66081. #
  66082. # %{ Multi-line
  66083. # string }.squish # => "Multi-line string"
  66084. # " foo bar \n \t boo".squish # => "foo bar boo"
  66085. def squish
  66086. dup.squish!
  66087. end
  66088. # Performs a destructive squish. See String#squish.
  66089. def squish!
  66090. gsub!(/\A[[:space:]]+/, '')
  66091. gsub!(/[[:space:]]+\z/, '')
  66092. gsub!(/[[:space:]]+/, ' ')
  66093. self
  66094. end
  66095. # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
  66096. #
  66097. # 'Once upon a time in a world far far away'.truncate(27)
  66098. # # => "Once upon a time in a wo..."
  66099. #
  66100. # Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
  66101. #
  66102. # 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
  66103. # # => "Once upon a time in a..."
  66104. #
  66105. # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
  66106. # # => "Once upon a time in a..."
  66107. #
  66108. # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
  66109. # for a total length not exceeding <tt>length</tt>:
  66110. #
  66111. # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
  66112. # # => "And they f... (continued)"
  66113. def truncate(truncate_at, options = {})
  66114. return dup unless length > truncate_at
  66115. options[:omission] ||= '...'
  66116. length_with_room_for_omission = truncate_at - options[:omission].length
  66117. stop = \
  66118. if options[:separator]
  66119. rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
  66120. else
  66121. length_with_room_for_omission
  66122. end
  66123. self[0...stop] + options[:omission]
  66124. end
  66125. end
  66126. class String
  66127. # Same as +indent+, except it indents the receiver in-place.
  66128. #
  66129. # Returns the indented string, or +nil+ if there was nothing to indent.
  66130. def indent!(amount, indent_string=nil, indent_empty_lines=false)
  66131. indent_string = indent_string || self[/^[ \t]/] || ' '
  66132. re = indent_empty_lines ? /^/ : /^(?!$)/
  66133. gsub!(re, indent_string * amount)
  66134. end
  66135. # Indents the lines in the receiver:
  66136. #
  66137. # <<EOS.indent(2)
  66138. # def some_method
  66139. # some_code
  66140. # end
  66141. # EOS
  66142. # # =>
  66143. # def some_method
  66144. # some_code
  66145. # end
  66146. #
  66147. # The second argument, +indent_string+, specifies which indent string to
  66148. # use. The default is +nil+, which tells the method to make a guess by
  66149. # peeking at the first indented line, and fallback to a space if there is
  66150. # none.
  66151. #
  66152. # " foo".indent(2) # => " foo"
  66153. # "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
  66154. # "foo".indent(2, "\t") # => "\t\tfoo"
  66155. #
  66156. # While +indent_string+ is tipically one space or tab, it may be any string.
  66157. #
  66158. # The third argument, +indent_empty_lines+, is a flag that says whether
  66159. # empty lines should be indented. Default is false.
  66160. #
  66161. # "foo\n\nbar".indent(2) # => " foo\n\n bar"
  66162. # "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
  66163. #
  66164. def indent(amount, indent_string=nil, indent_empty_lines=false)
  66165. dup.tap {|_| _.indent!(amount, indent_string, indent_empty_lines)}
  66166. end
  66167. end
  66168. require 'active_support/inflector/methods'
  66169. require 'active_support/inflector/transliterate'
  66170. # String inflections define new methods on the String class to transform names for different purposes.
  66171. # For instance, you can figure out the name of a table from the name of a class.
  66172. #
  66173. # 'ScaleScore'.tableize # => "scale_scores"
  66174. #
  66175. class String
  66176. # Returns the plural form of the word in the string.
  66177. #
  66178. # If the optional parameter +count+ is specified,
  66179. # the singular form will be returned if <tt>count == 1</tt>.
  66180. # For any other value of +count+ the plural will be returned.
  66181. #
  66182. # If the optional parameter +locale+ is specified,
  66183. # the word will be pluralized as a word of that language.
  66184. # By default, this parameter is set to <tt>:en</tt>.
  66185. # You must define your own inflection rules for languages other than English.
  66186. #
  66187. # 'post'.pluralize # => "posts"
  66188. # 'octopus'.pluralize # => "octopi"
  66189. # 'sheep'.pluralize # => "sheep"
  66190. # 'words'.pluralize # => "words"
  66191. # 'the blue mailman'.pluralize # => "the blue mailmen"
  66192. # 'CamelOctopus'.pluralize # => "CamelOctopi"
  66193. # 'apple'.pluralize(1) # => "apple"
  66194. # 'apple'.pluralize(2) # => "apples"
  66195. # 'ley'.pluralize(:es) # => "leyes"
  66196. # 'ley'.pluralize(1, :es) # => "ley"
  66197. def pluralize(count = nil, locale = :en)
  66198. locale = count if count.is_a?(Symbol)
  66199. if count == 1
  66200. self
  66201. else
  66202. ActiveSupport::Inflector.pluralize(self, locale)
  66203. end
  66204. end
  66205. # The reverse of +pluralize+, returns the singular form of a word in a string.
  66206. #
  66207. # If the optional parameter +locale+ is specified,
  66208. # the word will be singularized as a word of that language.
  66209. # By default, this paramter is set to <tt>:en</tt>.
  66210. # You must define your own inflection rules for languages other than English.
  66211. #
  66212. # 'posts'.singularize # => "post"
  66213. # 'octopi'.singularize # => "octopus"
  66214. # 'sheep'.singularize # => "sheep"
  66215. # 'word'.singularize # => "word"
  66216. # 'the blue mailmen'.singularize # => "the blue mailman"
  66217. # 'CamelOctopi'.singularize # => "CamelOctopus"
  66218. # 'leyes'.singularize(:es) # => "ley"
  66219. def singularize(locale = :en)
  66220. ActiveSupport::Inflector.singularize(self, locale)
  66221. end
  66222. # +constantize+ tries to find a declared constant with the name specified
  66223. # in the string. It raises a NameError when the name is not in CamelCase
  66224. # or is not initialized. See ActiveSupport::Inflector.constantize
  66225. #
  66226. # 'Module'.constantize # => Module
  66227. # 'Class'.constantize # => Class
  66228. # 'blargle'.constantize # => NameError: wrong constant name blargle
  66229. def constantize
  66230. ActiveSupport::Inflector.constantize(self)
  66231. end
  66232. # +safe_constantize+ tries to find a declared constant with the name specified
  66233. # in the string. It returns nil when the name is not in CamelCase
  66234. # or is not initialized. See ActiveSupport::Inflector.safe_constantize
  66235. #
  66236. # 'Module'.safe_constantize # => Module
  66237. # 'Class'.safe_constantize # => Class
  66238. # 'blargle'.safe_constantize # => nil
  66239. def safe_constantize
  66240. ActiveSupport::Inflector.safe_constantize(self)
  66241. end
  66242. # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
  66243. # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
  66244. #
  66245. # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
  66246. #
  66247. # 'active_record'.camelize # => "ActiveRecord"
  66248. # 'active_record'.camelize(:lower) # => "activeRecord"
  66249. # 'active_record/errors'.camelize # => "ActiveRecord::Errors"
  66250. # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
  66251. def camelize(first_letter = :upper)
  66252. case first_letter
  66253. when :upper
  66254. ActiveSupport::Inflector.camelize(self, true)
  66255. when :lower
  66256. ActiveSupport::Inflector.camelize(self, false)
  66257. end
  66258. end
  66259. alias_method :camelcase, :camelize
  66260. # Capitalizes all the words and replaces some characters in the string to create
  66261. # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
  66262. # used in the Rails internals.
  66263. #
  66264. # +titleize+ is also aliased as +titlecase+.
  66265. #
  66266. # 'man from the boondocks'.titleize # => "Man From The Boondocks"
  66267. # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
  66268. def titleize
  66269. ActiveSupport::Inflector.titleize(self)
  66270. end
  66271. alias_method :titlecase, :titleize
  66272. # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
  66273. #
  66274. # +underscore+ will also change '::' to '/' to convert namespaces to paths.
  66275. #
  66276. # 'ActiveModel'.underscore # => "active_model"
  66277. # 'ActiveModel::Errors'.underscore # => "active_model/errors"
  66278. def underscore
  66279. ActiveSupport::Inflector.underscore(self)
  66280. end
  66281. # Replaces underscores with dashes in the string.
  66282. #
  66283. # 'puni_puni'.dasherize # => "puni-puni"
  66284. def dasherize
  66285. ActiveSupport::Inflector.dasherize(self)
  66286. end
  66287. # Removes the module part from the constant expression in the string.
  66288. #
  66289. # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
  66290. # 'Inflections'.demodulize # => "Inflections"
  66291. #
  66292. # See also +deconstantize+.
  66293. def demodulize
  66294. ActiveSupport::Inflector.demodulize(self)
  66295. end
  66296. # Removes the rightmost segment from the constant expression in the string.
  66297. #
  66298. # 'Net::HTTP'.deconstantize # => "Net"
  66299. # '::Net::HTTP'.deconstantize # => "::Net"
  66300. # 'String'.deconstantize # => ""
  66301. # '::String'.deconstantize # => ""
  66302. # ''.deconstantize # => ""
  66303. #
  66304. # See also +demodulize+.
  66305. def deconstantize
  66306. ActiveSupport::Inflector.deconstantize(self)
  66307. end
  66308. # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
  66309. #
  66310. # class Person
  66311. # def to_param
  66312. # "#{id}-#{name.parameterize}"
  66313. # end
  66314. # end
  66315. #
  66316. # @person = Person.find(1)
  66317. # # => #<Person id: 1, name: "Donald E. Knuth">
  66318. #
  66319. # <%= link_to(@person.name, person_path) %>
  66320. # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
  66321. def parameterize(sep = '-')
  66322. ActiveSupport::Inflector.parameterize(self, sep)
  66323. end
  66324. # Creates the name of a table like Rails does for models to table names. This method
  66325. # uses the +pluralize+ method on the last word in the string.
  66326. #
  66327. # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
  66328. # 'egg_and_ham'.tableize # => "egg_and_hams"
  66329. # 'fancyCategory'.tableize # => "fancy_categories"
  66330. def tableize
  66331. ActiveSupport::Inflector.tableize(self)
  66332. end
  66333. # Create a class name from a plural table name like Rails does for table names to models.
  66334. # Note that this returns a string and not a class. (To convert to an actual class
  66335. # follow +classify+ with +constantize+.)
  66336. #
  66337. # 'egg_and_hams'.classify # => "EggAndHam"
  66338. # 'posts'.classify # => "Post"
  66339. #
  66340. # Singular names are not handled correctly.
  66341. #
  66342. # 'business'.classify # => "Busines"
  66343. def classify
  66344. ActiveSupport::Inflector.classify(self)
  66345. end
  66346. # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
  66347. # Like +titleize+, this is meant for creating pretty output.
  66348. #
  66349. # 'employee_salary'.humanize # => "Employee salary"
  66350. # 'author_id'.humanize # => "Author"
  66351. def humanize
  66352. ActiveSupport::Inflector.humanize(self)
  66353. end
  66354. # Creates a foreign key name from a class name.
  66355. # +separate_class_name_and_id_with_underscore+ sets whether
  66356. # the method should put '_' between the name and 'id'.
  66357. #
  66358. # 'Message'.foreign_key # => "message_id"
  66359. # 'Message'.foreign_key(false) # => "messageid"
  66360. # 'Admin::Post'.foreign_key # => "post_id"
  66361. def foreign_key(separate_class_name_and_id_with_underscore = true)
  66362. ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
  66363. end
  66364. end
  66365. require 'active_support/string_inquirer'
  66366. class String
  66367. # Wraps the current string in the <tt>ActiveSupport::StringInquirer</tt> class,
  66368. # which gives you a prettier way to test for equality.
  66369. #
  66370. # env = 'production'.inquiry
  66371. # env.production? # => true
  66372. # env.development? # => false
  66373. def inquiry
  66374. ActiveSupport::StringInquirer.new(self)
  66375. end
  66376. end
  66377. # encoding: utf-8
  66378. require 'active_support/multibyte'
  66379. class String
  66380. # == Multibyte proxy
  66381. #
  66382. # +mb_chars+ is a multibyte safe proxy for string methods.
  66383. #
  66384. # It creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
  66385. # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
  66386. # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
  66387. #
  66388. # name = 'Claus Müller'
  66389. # name.reverse # => "rell??M sualC"
  66390. # name.length # => 13
  66391. #
  66392. # name.mb_chars.reverse.to_s # => "rellüM sualC"
  66393. # name.mb_chars.length # => 12
  66394. #
  66395. # == Method chaining
  66396. #
  66397. # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
  66398. # method chaining on the result of any of these methods.
  66399. #
  66400. # name.mb_chars.reverse.length # => 12
  66401. #
  66402. # == Interoperability and configuration
  66403. #
  66404. # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
  66405. # String and Char work like expected. The bang! methods change the internal string representation in the Chars
  66406. # object. Interoperability problems can be resolved easily with a +to_s+ call.
  66407. #
  66408. # For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
  66409. # information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
  66410. def mb_chars
  66411. ActiveSupport::Multibyte.proxy_class.new(self)
  66412. end
  66413. def is_utf8?
  66414. case encoding
  66415. when Encoding::UTF_8
  66416. valid_encoding?
  66417. when Encoding::ASCII_8BIT, Encoding::US_ASCII
  66418. dup.force_encoding(Encoding::UTF_8).valid_encoding?
  66419. else
  66420. false
  66421. end
  66422. end
  66423. end
  66424. require 'erb'
  66425. require 'active_support/core_ext/kernel/singleton_class'
  66426. class ERB
  66427. module Util
  66428. HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
  66429. JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
  66430. HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
  66431. JSON_ESCAPE_REGEXP = /[&"><]/
  66432. # A utility method for escaping HTML tag characters.
  66433. # This method is also aliased as <tt>h</tt>.
  66434. #
  66435. # In your ERB templates, use this method to escape any unsafe content. For example:
  66436. # <%=h @person.name %>
  66437. #
  66438. # puts html_escape('is a > 0 & a < 10?')
  66439. # # => is a &gt; 0 &amp; a &lt; 10?
  66440. def html_escape(s)
  66441. s = s.to_s
  66442. if s.html_safe?
  66443. s
  66444. else
  66445. s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
  66446. end
  66447. end
  66448. # Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
  66449. remove_method(:h)
  66450. alias h html_escape
  66451. module_function :h
  66452. singleton_class.send(:remove_method, :html_escape)
  66453. module_function :html_escape
  66454. # A utility method for escaping HTML without affecting existing escaped entities.
  66455. #
  66456. # html_escape_once('1 < 2 &amp; 3')
  66457. # # => "1 &lt; 2 &amp; 3"
  66458. #
  66459. # html_escape_once('&lt;&lt; Accept & Checkout')
  66460. # # => "&lt;&lt; Accept &amp; Checkout"
  66461. def html_escape_once(s)
  66462. result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP) { |special| HTML_ESCAPE[special] }
  66463. s.html_safe? ? result.html_safe : result
  66464. end
  66465. module_function :html_escape_once
  66466. # A utility method for escaping HTML entities in JSON strings
  66467. # using \uXXXX JavaScript escape sequences for string literals:
  66468. #
  66469. # json_escape('is a > 0 & a < 10?')
  66470. # # => is a \u003E 0 \u0026 a \u003C 10?
  66471. #
  66472. # Note that after this operation is performed the output is not
  66473. # valid JSON. In particular double quotes are removed:
  66474. #
  66475. # json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
  66476. # # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
  66477. def json_escape(s)
  66478. result = s.to_s.gsub(JSON_ESCAPE_REGEXP) { |special| JSON_ESCAPE[special] }
  66479. s.html_safe? ? result.html_safe : result
  66480. end
  66481. module_function :json_escape
  66482. end
  66483. end
  66484. class Object
  66485. def html_safe?
  66486. false
  66487. end
  66488. end
  66489. class Numeric
  66490. def html_safe?
  66491. true
  66492. end
  66493. end
  66494. module ActiveSupport #:nodoc:
  66495. class SafeBuffer < String
  66496. UNSAFE_STRING_METHODS = %w(
  66497. capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
  66498. slice squeeze strip sub succ swapcase tr tr_s upcase prepend
  66499. )
  66500. alias_method :original_concat, :concat
  66501. private :original_concat
  66502. class SafeConcatError < StandardError
  66503. def initialize
  66504. super 'Could not concatenate to the buffer because it is not html safe.'
  66505. end
  66506. end
  66507. def [](*args)
  66508. if args.size < 2
  66509. super
  66510. else
  66511. if html_safe?
  66512. new_safe_buffer = super
  66513. new_safe_buffer.instance_eval { @html_safe = true }
  66514. new_safe_buffer
  66515. else
  66516. to_str[*args]
  66517. end
  66518. end
  66519. end
  66520. def safe_concat(value)
  66521. raise SafeConcatError unless html_safe?
  66522. original_concat(value)
  66523. end
  66524. def initialize(*)
  66525. @html_safe = true
  66526. super
  66527. end
  66528. def initialize_copy(other)
  66529. super
  66530. @html_safe = other.html_safe?
  66531. end
  66532. def clone_empty
  66533. self[0, 0]
  66534. end
  66535. def concat(value)
  66536. if !html_safe? || value.html_safe?
  66537. super(value)
  66538. else
  66539. super(ERB::Util.h(value))
  66540. end
  66541. end
  66542. alias << concat
  66543. def +(other)
  66544. dup.concat(other)
  66545. end
  66546. def %(args)
  66547. args = Array(args).map do |arg|
  66548. if !html_safe? || arg.html_safe?
  66549. arg
  66550. else
  66551. ERB::Util.h(arg)
  66552. end
  66553. end
  66554. self.class.new(super(args))
  66555. end
  66556. def html_safe?
  66557. defined?(@html_safe) && @html_safe
  66558. end
  66559. def to_s
  66560. self
  66561. end
  66562. def to_param
  66563. to_str
  66564. end
  66565. def encode_with(coder)
  66566. coder.represent_scalar nil, to_str
  66567. end
  66568. UNSAFE_STRING_METHODS.each do |unsafe_method|
  66569. if 'String'.respond_to?(unsafe_method)
  66570. class_eval <<-EOT, __FILE__, __LINE__ + 1
  66571. def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
  66572. to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
  66573. end # end
  66574. def #{unsafe_method}!(*args) # def capitalize!(*args)
  66575. @html_safe = false # @html_safe = false
  66576. super # super
  66577. end # end
  66578. EOT
  66579. end
  66580. end
  66581. end
  66582. end
  66583. class String
  66584. def html_safe
  66585. ActiveSupport::SafeBuffer.new(self)
  66586. end
  66587. end
  66588. class String
  66589. alias_method :starts_with?, :start_with?
  66590. alias_method :ends_with?, :end_with?
  66591. end
  66592. require 'active_support/core_ext/object/try'
  66593. class String
  66594. # Strips indentation in heredocs.
  66595. #
  66596. # For example in
  66597. #
  66598. # if options[:usage]
  66599. # puts <<-USAGE.strip_heredoc
  66600. # This command does such and such.
  66601. #
  66602. # Supported options are:
  66603. # -h This message
  66604. # ...
  66605. # USAGE
  66606. # end
  66607. #
  66608. # the user would see the usage message aligned against the left margin.
  66609. #
  66610. # Technically, it looks for the least indented line in the whole string, and removes
  66611. # that amount of leading whitespace.
  66612. def strip_heredoc
  66613. indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
  66614. gsub(/^[ \t]{#{indent}}/, '')
  66615. end
  66616. end
  66617. begin
  66618. # See http://fast-xs.rubyforge.org/ by Eric Wong.
  66619. # Also included with hpricot.
  66620. require 'fast_xs'
  66621. rescue LoadError
  66622. # fast_xs extension unavailable
  66623. else
  66624. begin
  66625. require 'builder'
  66626. rescue LoadError
  66627. # builder demands the first shot at defining String#to_xs
  66628. end
  66629. class String
  66630. alias_method :original_xs, :to_xs if method_defined?(:to_xs)
  66631. alias_method :to_xs, :fast_xs
  66632. end
  66633. end
  66634. require 'active_support/core_ext/time/zones'
  66635. class String
  66636. # Converts String to a TimeWithZone in the current zone if Time.zone or Time.zone_default
  66637. # is set, otherwise converts String to a Time via String#to_time
  66638. def in_time_zone(zone = ::Time.zone)
  66639. if zone
  66640. ::Time.find_zone!(zone).parse(self)
  66641. else
  66642. to_time
  66643. end
  66644. end
  66645. end
  66646. require 'active_support/core_ext/string/conversions'
  66647. require 'active_support/core_ext/string/filters'
  66648. require 'active_support/core_ext/string/multibyte'
  66649. require 'active_support/core_ext/string/starts_ends_with'
  66650. require 'active_support/core_ext/string/inflections'
  66651. require 'active_support/core_ext/string/access'
  66652. require 'active_support/core_ext/string/xchar'
  66653. require 'active_support/core_ext/string/behavior'
  66654. require 'active_support/core_ext/string/output_safety'
  66655. require 'active_support/core_ext/string/exclude'
  66656. require 'active_support/core_ext/string/strip'
  66657. require 'active_support/core_ext/string/inquiry'
  66658. require 'active_support/core_ext/string/indent'
  66659. require 'active_support/core_ext/string/zones'
  66660. # Backport of Struct#to_h from Ruby 2.0
  66661. class Struct # :nodoc:
  66662. def to_h
  66663. Hash[members.zip(values)]
  66664. end
  66665. end unless Struct.instance_methods.include?(:to_h)
  66666. class Thread
  66667. LOCK = Mutex.new # :nodoc:
  66668. # Returns the value of a thread local variable that has been set. Note that
  66669. # these are different than fiber local values.
  66670. #
  66671. # Thread local values are carried along with threads, and do not respect
  66672. # fibers. For example:
  66673. #
  66674. # Thread.new {
  66675. # Thread.current.thread_variable_set("foo", "bar") # set a thread local
  66676. # Thread.current["foo"] = "bar" # set a fiber local
  66677. #
  66678. # Fiber.new {
  66679. # Fiber.yield [
  66680. # Thread.current.thread_variable_get("foo"), # get the thread local
  66681. # Thread.current["foo"], # get the fiber local
  66682. # ]
  66683. # }.resume
  66684. # }.join.value # => ['bar', nil]
  66685. #
  66686. # The value <tt>"bar"</tt> is returned for the thread local, where +nil+ is returned
  66687. # for the fiber local. The fiber is executed in the same thread, so the
  66688. # thread local values are available.
  66689. def thread_variable_get(key)
  66690. locals[key.to_sym]
  66691. end
  66692. # Sets a thread local with +key+ to +value+. Note that these are local to
  66693. # threads, and not to fibers. Please see Thread#thread_variable_get for
  66694. # more information.
  66695. def thread_variable_set(key, value)
  66696. locals[key.to_sym] = value
  66697. end
  66698. # Returns an an array of the names of the thread-local variables (as Symbols).
  66699. #
  66700. # thr = Thread.new do
  66701. # Thread.current.thread_variable_set(:cat, 'meow')
  66702. # Thread.current.thread_variable_set("dog", 'woof')
  66703. # end
  66704. # thr.join #=> #<Thread:0x401b3f10 dead>
  66705. # thr.thread_variables #=> [:dog, :cat]
  66706. #
  66707. # Note that these are not fiber local variables. Please see Thread#thread_variable_get
  66708. # for more details.
  66709. def thread_variables
  66710. locals.keys
  66711. end
  66712. # Returns <tt>true</tt> if the given string (or symbol) exists as a
  66713. # thread-local variable.
  66714. #
  66715. # me = Thread.current
  66716. # me.thread_variable_set(:oliver, "a")
  66717. # me.thread_variable?(:oliver) #=> true
  66718. # me.thread_variable?(:stanley) #=> false
  66719. #
  66720. # Note that these are not fiber local variables. Please see Thread#thread_variable_get
  66721. # for more details.
  66722. def thread_variable?(key)
  66723. locals.has_key?(key.to_sym)
  66724. end
  66725. private
  66726. def locals
  66727. if defined?(@locals)
  66728. @locals
  66729. else
  66730. LOCK.synchronize { @locals ||= {} }
  66731. end
  66732. end
  66733. end unless Thread.instance_methods.include?(:thread_variable_set)
  66734. require 'active_support/core_ext/object/acts_like'
  66735. class Time
  66736. # Duck-types as a Time-like class. See Object#acts_like?.
  66737. def acts_like_time?
  66738. true
  66739. end
  66740. end
  66741. require 'active_support/duration'
  66742. require 'active_support/core_ext/time/conversions'
  66743. require 'active_support/time_with_zone'
  66744. require 'active_support/core_ext/time/zones'
  66745. require 'active_support/core_ext/date_and_time/calculations'
  66746. require 'active_support/deprecation'
  66747. class Time
  66748. include DateAndTime::Calculations
  66749. COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  66750. class << self
  66751. # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
  66752. def ===(other)
  66753. super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
  66754. end
  66755. # Return the number of days in the given month.
  66756. # If no year is specified, it will use the current year.
  66757. def days_in_month(month, year = now.year)
  66758. if month == 2 && ::Date.gregorian_leap?(year)
  66759. 29
  66760. else
  66761. COMMON_YEAR_DAYS_IN_MONTH[month]
  66762. end
  66763. end
  66764. # *DEPRECATED*: Use +Time#utc+ or +Time#local+ instead.
  66765. #
  66766. # Returns a new Time if requested year can be accommodated by Ruby's Time class
  66767. # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
  66768. # otherwise returns a DateTime.
  66769. def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
  66770. ActiveSupport::Deprecation.warn 'time_with_datetime_fallback is deprecated. Use Time#utc or Time#local instead', caller
  66771. time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
  66772. # This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138.
  66773. if time.year == year
  66774. time
  66775. else
  66776. ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
  66777. end
  66778. rescue
  66779. ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
  66780. end
  66781. # *DEPRECATED*: Use +Time#utc+ instead.
  66782. #
  66783. # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>.
  66784. def utc_time(*args)
  66785. ActiveSupport::Deprecation.warn 'utc_time is deprecated. Use Time#utc instead', caller
  66786. time_with_datetime_fallback(:utc, *args)
  66787. end
  66788. # *DEPRECATED*: Use +Time#local+ instead.
  66789. #
  66790. # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>.
  66791. def local_time(*args)
  66792. ActiveSupport::Deprecation.warn 'local_time is deprecated. Use Time#local instead', caller
  66793. time_with_datetime_fallback(:local, *args)
  66794. end
  66795. # Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
  66796. def current
  66797. ::Time.zone ? ::Time.zone.now : ::Time.now
  66798. end
  66799. end
  66800. # Seconds since midnight: Time.now.seconds_since_midnight
  66801. def seconds_since_midnight
  66802. to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
  66803. end
  66804. # Returns the number of seconds until 23:59:59.
  66805. #
  66806. # Time.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
  66807. # Time.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
  66808. # Time.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
  66809. def seconds_until_end_of_day
  66810. end_of_day.to_i - to_i
  66811. end
  66812. # Returns a new Time where one or more of the elements have been changed according
  66813. # to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
  66814. # <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed,
  66815. # then minute, sec, and usec is set to 0. If the hour and minute is passed, then
  66816. # sec and usec is set to 0. The +options+ parameter takes a hash with any of these
  66817. # keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>,
  66818. # <tt>:sec</tt>, <tt>:usec</tt>.
  66819. #
  66820. # Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
  66821. # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
  66822. # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
  66823. def change(options)
  66824. new_year = options.fetch(:year, year)
  66825. new_month = options.fetch(:month, month)
  66826. new_day = options.fetch(:day, day)
  66827. new_hour = options.fetch(:hour, hour)
  66828. new_min = options.fetch(:min, options[:hour] ? 0 : min)
  66829. new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
  66830. new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
  66831. if utc?
  66832. ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
  66833. elsif zone
  66834. ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
  66835. else
  66836. ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
  66837. end
  66838. end
  66839. # Uses Date to provide precise Time calculations for years, months, and days.
  66840. # The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
  66841. # <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
  66842. # <tt>:minutes</tt>, <tt>:seconds</tt>.
  66843. def advance(options)
  66844. unless options[:weeks].nil?
  66845. options[:weeks], partial_weeks = options[:weeks].divmod(1)
  66846. options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
  66847. end
  66848. unless options[:days].nil?
  66849. options[:days], partial_days = options[:days].divmod(1)
  66850. options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
  66851. end
  66852. d = to_date.advance(options)
  66853. time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
  66854. seconds_to_advance = \
  66855. options.fetch(:seconds, 0) +
  66856. options.fetch(:minutes, 0) * 60 +
  66857. options.fetch(:hours, 0) * 3600
  66858. if seconds_to_advance.zero?
  66859. time_advanced_by_date
  66860. else
  66861. time_advanced_by_date.since(seconds_to_advance)
  66862. end
  66863. end
  66864. # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
  66865. def ago(seconds)
  66866. since(-seconds)
  66867. end
  66868. # Returns a new Time representing the time a number of seconds since the instance time
  66869. def since(seconds)
  66870. self + seconds
  66871. rescue
  66872. to_datetime.since(seconds)
  66873. end
  66874. alias :in :since
  66875. # Returns a new Time representing the start of the day (0:00)
  66876. def beginning_of_day
  66877. #(self - seconds_since_midnight).change(usec: 0)
  66878. change(:hour => 0)
  66879. end
  66880. alias :midnight :beginning_of_day
  66881. alias :at_midnight :beginning_of_day
  66882. alias :at_beginning_of_day :beginning_of_day
  66883. # Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
  66884. def end_of_day
  66885. change(
  66886. :hour => 23,
  66887. :min => 59,
  66888. :sec => 59,
  66889. :usec => Rational(999999999, 1000)
  66890. )
  66891. end
  66892. alias :at_end_of_day :end_of_day
  66893. # Returns a new Time representing the start of the hour (x:00)
  66894. def beginning_of_hour
  66895. change(:min => 0)
  66896. end
  66897. alias :at_beginning_of_hour :beginning_of_hour
  66898. # Returns a new Time representing the end of the hour, x:59:59.999999 (.999999999 in ruby1.9)
  66899. def end_of_hour
  66900. change(
  66901. :min => 59,
  66902. :sec => 59,
  66903. :usec => Rational(999999999, 1000)
  66904. )
  66905. end
  66906. alias :at_end_of_hour :end_of_hour
  66907. # Returns a Range representing the whole day of the current time.
  66908. def all_day
  66909. beginning_of_day..end_of_day
  66910. end
  66911. # Returns a Range representing the whole week of the current time.
  66912. # Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
  66913. def all_week(start_day = Date.beginning_of_week)
  66914. beginning_of_week(start_day)..end_of_week(start_day)
  66915. end
  66916. # Returns a Range representing the whole month of the current time.
  66917. def all_month
  66918. beginning_of_month..end_of_month
  66919. end
  66920. # Returns a Range representing the whole quarter of the current time.
  66921. def all_quarter
  66922. beginning_of_quarter..end_of_quarter
  66923. end
  66924. # Returns a Range representing the whole year of the current time.
  66925. def all_year
  66926. beginning_of_year..end_of_year
  66927. end
  66928. def plus_with_duration(other) #:nodoc:
  66929. if ActiveSupport::Duration === other
  66930. other.since(self)
  66931. else
  66932. plus_without_duration(other)
  66933. end
  66934. end
  66935. alias_method :plus_without_duration, :+
  66936. alias_method :+, :plus_with_duration
  66937. def minus_with_duration(other) #:nodoc:
  66938. if ActiveSupport::Duration === other
  66939. other.until(self)
  66940. else
  66941. minus_without_duration(other)
  66942. end
  66943. end
  66944. alias_method :minus_without_duration, :-
  66945. alias_method :-, :minus_with_duration
  66946. # Time#- can also be used to determine the number of seconds between two Time instances.
  66947. # We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
  66948. # are coerced into values that Time#- will recognize
  66949. def minus_with_coercion(other)
  66950. other = other.comparable_time if other.respond_to?(:comparable_time)
  66951. other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
  66952. end
  66953. alias_method :minus_without_coercion, :-
  66954. alias_method :-, :minus_with_coercion
  66955. # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
  66956. # can be chronologically compared with a Time
  66957. def compare_with_coercion(other)
  66958. # we're avoiding Time#to_datetime cause it's expensive
  66959. if other.is_a?(Time)
  66960. compare_without_coercion(other.to_time)
  66961. else
  66962. to_datetime <=> other
  66963. end
  66964. end
  66965. alias_method :compare_without_coercion, :<=>
  66966. alias_method :<=>, :compare_with_coercion
  66967. # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
  66968. # can be eql? to an equivalent Time
  66969. def eql_with_coercion(other)
  66970. # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
  66971. other = other.comparable_time if other.respond_to?(:comparable_time)
  66972. eql_without_coercion(other)
  66973. end
  66974. alias_method :eql_without_coercion, :eql?
  66975. alias_method :eql?, :eql_with_coercion
  66976. end
  66977. require 'active_support/inflector/methods'
  66978. require 'active_support/values/time_zone'
  66979. class Time
  66980. DATE_FORMATS = {
  66981. :db => '%Y-%m-%d %H:%M:%S',
  66982. :number => '%Y%m%d%H%M%S',
  66983. :nsec => '%Y%m%d%H%M%S%9N',
  66984. :time => '%H:%M',
  66985. :short => '%d %b %H:%M',
  66986. :long => '%B %d, %Y %H:%M',
  66987. :long_ordinal => lambda { |time|
  66988. day_format = ActiveSupport::Inflector.ordinalize(time.day)
  66989. time.strftime("%B #{day_format}, %Y %H:%M")
  66990. },
  66991. :rfc822 => lambda { |time|
  66992. offset_format = time.formatted_offset(false)
  66993. time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
  66994. }
  66995. }
  66996. # Converts to a formatted string. See DATE_FORMATS for builtin formats.
  66997. #
  66998. # This method is aliased to <tt>to_s</tt>.
  66999. #
  67000. # time = Time.now # => Thu Jan 18 06:10:17 CST 2007
  67001. #
  67002. # time.to_formatted_s(:time) # => "06:10"
  67003. # time.to_s(:time) # => "06:10"
  67004. #
  67005. # time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
  67006. # time.to_formatted_s(:number) # => "20070118061017"
  67007. # time.to_formatted_s(:short) # => "18 Jan 06:10"
  67008. # time.to_formatted_s(:long) # => "January 18, 2007 06:10"
  67009. # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
  67010. # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
  67011. #
  67012. # == Adding your own time formats to +to_formatted_s+
  67013. # You can add your own formats to the Time::DATE_FORMATS hash.
  67014. # Use the format name as the hash key and either a strftime string
  67015. # or Proc instance that takes a time argument as the value.
  67016. #
  67017. # # config/initializers/time_formats.rb
  67018. # Time::DATE_FORMATS[:month_and_year] = '%B %Y'
  67019. # Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") }
  67020. def to_formatted_s(format = :default)
  67021. if formatter = DATE_FORMATS[format]
  67022. formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
  67023. else
  67024. to_default_s
  67025. end
  67026. end
  67027. alias_method :to_default_s, :to_s
  67028. alias_method :to_s, :to_formatted_s
  67029. # Returns the UTC offset as an +HH:MM formatted string.
  67030. #
  67031. # Time.local(2000).formatted_offset # => "-06:00"
  67032. # Time.local(2000).formatted_offset(false) # => "-0600"
  67033. def formatted_offset(colon = true, alternate_utc_string = nil)
  67034. utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
  67035. end
  67036. end
  67037. require 'active_support/core_ext/infinite_comparable'
  67038. class Time
  67039. include InfiniteComparable
  67040. end
  67041. # Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
  67042. # preserves utc_offset. Preserve zone also, even though it may not
  67043. # work in some edge cases.
  67044. if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
  67045. class Time
  67046. class << self
  67047. alias_method :_load_without_zone, :_load
  67048. def _load(marshaled_time)
  67049. time = _load_without_zone(marshaled_time)
  67050. time.instance_eval do
  67051. if zone = defined?(@_zone) && remove_instance_variable('@_zone')
  67052. ary = to_a
  67053. ary[0] += subsec if ary[0] == sec
  67054. ary[-1] = zone
  67055. utc? ? Time.utc(*ary) : Time.local(*ary)
  67056. else
  67057. self
  67058. end
  67059. end
  67060. end
  67061. end
  67062. alias_method :_dump_without_zone, :_dump
  67063. def _dump(*args)
  67064. obj = dup
  67065. obj.instance_variable_set('@_zone', zone)
  67066. obj.send :_dump_without_zone, *args
  67067. end
  67068. end
  67069. end
  67070. require 'active_support/time_with_zone'
  67071. class Time
  67072. class << self
  67073. attr_accessor :zone_default
  67074. # Returns the TimeZone for the current request, if this has been set (via Time.zone=).
  67075. # If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
  67076. def zone
  67077. Thread.current[:time_zone] || zone_default
  67078. end
  67079. # Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
  67080. #
  67081. # This method accepts any of the following:
  67082. #
  67083. # * A Rails TimeZone object.
  67084. # * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
  67085. # * A TZInfo::Timezone object.
  67086. # * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
  67087. #
  67088. # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done.
  67089. # <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
  67090. #
  67091. # class ApplicationController < ActionController::Base
  67092. # around_filter :set_time_zone
  67093. #
  67094. # def set_time_zone
  67095. # if logged_in?
  67096. # Time.use_zone(current_user.time_zone) { yield }
  67097. # else
  67098. # yield
  67099. # end
  67100. # end
  67101. # end
  67102. def zone=(time_zone)
  67103. Thread.current[:time_zone] = find_zone!(time_zone)
  67104. end
  67105. # Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done.
  67106. def use_zone(time_zone)
  67107. new_zone = find_zone!(time_zone)
  67108. begin
  67109. old_zone, ::Time.zone = ::Time.zone, new_zone
  67110. yield
  67111. ensure
  67112. ::Time.zone = old_zone
  67113. end
  67114. end
  67115. # Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones.
  67116. def find_zone!(time_zone)
  67117. if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
  67118. time_zone
  67119. else
  67120. # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
  67121. unless time_zone.respond_to?(:period_for_local)
  67122. time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
  67123. end
  67124. # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
  67125. if time_zone.is_a?(ActiveSupport::TimeZone)
  67126. time_zone
  67127. else
  67128. ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
  67129. end
  67130. end
  67131. rescue TZInfo::InvalidTimezoneIdentifier
  67132. raise ArgumentError, "Invalid Timezone: #{time_zone}"
  67133. end
  67134. def find_zone(time_zone)
  67135. find_zone!(time_zone) rescue nil
  67136. end
  67137. end
  67138. # Returns the simultaneous time in <tt>Time.zone</tt>.
  67139. #
  67140. # Time.zone = 'Hawaii' # => 'Hawaii'
  67141. # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
  67142. #
  67143. # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
  67144. # instead of the operating system's time zone.
  67145. #
  67146. # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
  67147. # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
  67148. #
  67149. # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
  67150. def in_time_zone(zone = ::Time.zone)
  67151. if zone
  67152. ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
  67153. else
  67154. self
  67155. end
  67156. end
  67157. end
  67158. require 'active_support/core_ext/time/acts_like'
  67159. require 'active_support/core_ext/time/calculations'
  67160. require 'active_support/core_ext/time/conversions'
  67161. require 'active_support/core_ext/time/marshal'
  67162. require 'active_support/core_ext/time/zones'
  67163. require 'active_support/core_ext/time/infinite_comparable'
  67164. # encoding: utf-8
  67165. require 'uri'
  67166. str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
  67167. parser = URI::Parser.new
  67168. unless str == parser.unescape(parser.escape(str))
  67169. URI::Parser.class_eval do
  67170. remove_method :unescape
  67171. def unescape(str, escaped = /%[a-fA-F\d]{2}/)
  67172. # TODO: Are we actually sure that ASCII == UTF-8?
  67173. # YK: My initial experiments say yes, but let's be sure please
  67174. enc = str.encoding
  67175. enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
  67176. str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
  67177. end
  67178. end
  67179. end
  67180. module URI
  67181. class << self
  67182. def parser
  67183. @parser ||= URI::Parser.new
  67184. end
  67185. end
  67186. end
  67187. Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
  67188. next if File.basename(path, '.rb') == 'logger'
  67189. require "active_support/core_ext/#{File.basename(path, '.rb')}"
  67190. end
  67191. require "active_support/inflector/methods"
  67192. module ActiveSupport
  67193. # Autoload and eager load conveniences for your library.
  67194. #
  67195. # This module allows you to define autoloads based on
  67196. # Rails conventions (i.e. no need to define the path
  67197. # it is automatically guessed based on the filename)
  67198. # and also define a set of constants that needs to be
  67199. # eager loaded:
  67200. #
  67201. # module MyLib
  67202. # extend ActiveSupport::Autoload
  67203. #
  67204. # autoload :Model
  67205. #
  67206. # eager_autoload do
  67207. # autoload :Cache
  67208. # end
  67209. # end
  67210. #
  67211. # Then your library can be eager loaded by simply calling:
  67212. #
  67213. # MyLib.eager_load!
  67214. module Autoload
  67215. def self.extended(base) # :nodoc:
  67216. base.class_eval do
  67217. @_autoloads = {}
  67218. @_under_path = nil
  67219. @_at_path = nil
  67220. @_eager_autoload = false
  67221. end
  67222. end
  67223. def autoload(const_name, path = @_at_path)
  67224. unless path
  67225. full = [name, @_under_path, const_name.to_s].compact.join("::")
  67226. path = Inflector.underscore(full)
  67227. end
  67228. if @_eager_autoload
  67229. @_autoloads[const_name] = path
  67230. end
  67231. super const_name, path
  67232. end
  67233. def autoload_under(path)
  67234. @_under_path, old_path = path, @_under_path
  67235. yield
  67236. ensure
  67237. @_under_path = old_path
  67238. end
  67239. def autoload_at(path)
  67240. @_at_path, old_path = path, @_at_path
  67241. yield
  67242. ensure
  67243. @_at_path = old_path
  67244. end
  67245. def eager_autoload
  67246. old_eager, @_eager_autoload = @_eager_autoload, true
  67247. yield
  67248. ensure
  67249. @_eager_autoload = old_eager
  67250. end
  67251. def eager_load!
  67252. @_autoloads.values.each { |file| require file }
  67253. end
  67254. def autoloads
  67255. @_autoloads
  67256. end
  67257. end
  67258. end
  67259. require 'set'
  67260. require 'thread'
  67261. require 'thread_safe'
  67262. require 'pathname'
  67263. require 'active_support/core_ext/module/aliasing'
  67264. require 'active_support/core_ext/module/attribute_accessors'
  67265. require 'active_support/core_ext/module/introspection'
  67266. require 'active_support/core_ext/module/anonymous'
  67267. require 'active_support/core_ext/module/qualified_const'
  67268. require 'active_support/core_ext/object/blank'
  67269. require 'active_support/core_ext/load_error'
  67270. require 'active_support/core_ext/name_error'
  67271. require 'active_support/core_ext/string/starts_ends_with'
  67272. require 'active_support/inflector'
  67273. module ActiveSupport #:nodoc:
  67274. module Dependencies #:nodoc:
  67275. extend self
  67276. # Should we turn on Ruby warnings on the first load of dependent files?
  67277. mattr_accessor :warnings_on_first_load
  67278. self.warnings_on_first_load = false
  67279. # All files ever loaded.
  67280. mattr_accessor :history
  67281. self.history = Set.new
  67282. # All files currently loaded.
  67283. mattr_accessor :loaded
  67284. self.loaded = Set.new
  67285. # Should we load files or require them?
  67286. mattr_accessor :mechanism
  67287. self.mechanism = ENV['NO_RELOAD'] ? :require : :load
  67288. # The set of directories from which we may automatically load files. Files
  67289. # under these directories will be reloaded on each request in development mode,
  67290. # unless the directory also appears in autoload_once_paths.
  67291. mattr_accessor :autoload_paths
  67292. self.autoload_paths = []
  67293. # The set of directories from which automatically loaded constants are loaded
  67294. # only once. All directories in this set must also be present in +autoload_paths+.
  67295. mattr_accessor :autoload_once_paths
  67296. self.autoload_once_paths = []
  67297. # An array of qualified constant names that have been loaded. Adding a name
  67298. # to this array will cause it to be unloaded the next time Dependencies are
  67299. # cleared.
  67300. mattr_accessor :autoloaded_constants
  67301. self.autoloaded_constants = []
  67302. # An array of constant names that need to be unloaded on every request. Used
  67303. # to allow arbitrary constants to be marked for unloading.
  67304. mattr_accessor :explicitly_unloadable_constants
  67305. self.explicitly_unloadable_constants = []
  67306. # The logger is used for generating information on the action run-time
  67307. # (including benchmarking) if available. Can be set to nil for no logging.
  67308. # Compatible with both Ruby's own Logger and Log4r loggers.
  67309. mattr_accessor :logger
  67310. # Set to +true+ to enable logging of const_missing and file loads.
  67311. mattr_accessor :log_activity
  67312. self.log_activity = false
  67313. # The WatchStack keeps a stack of the modules being watched as files are
  67314. # loaded. If a file in the process of being loaded (parent.rb) triggers the
  67315. # load of another file (child.rb) the stack will ensure that child.rb
  67316. # handles the new constants.
  67317. #
  67318. # If child.rb is being autoloaded, its constants will be added to
  67319. # autoloaded_constants. If it was being `require`d, they will be discarded.
  67320. #
  67321. # This is handled by walking back up the watch stack and adding the constants
  67322. # found by child.rb to the list of original constants in parent.rb.
  67323. class WatchStack
  67324. include Enumerable
  67325. # @watching is a stack of lists of constants being watched. For instance,
  67326. # if parent.rb is autoloaded, the stack will look like [[Object]]. If
  67327. # parent.rb then requires namespace/child.rb, the stack will look like
  67328. # [[Object], [Namespace]].
  67329. def initialize
  67330. @watching = []
  67331. @stack = Hash.new { |h,k| h[k] = [] }
  67332. end
  67333. def each(&block)
  67334. @stack.each(&block)
  67335. end
  67336. def watching?
  67337. !@watching.empty?
  67338. end
  67339. # Returns a list of new constants found since the last call to
  67340. # <tt>watch_namespaces</tt>.
  67341. def new_constants
  67342. constants = []
  67343. # Grab the list of namespaces that we're looking for new constants under
  67344. @watching.last.each do |namespace|
  67345. # Retrieve the constants that were present under the namespace when watch_namespaces
  67346. # was originally called
  67347. original_constants = @stack[namespace].last
  67348. mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
  67349. next unless mod.is_a?(Module)
  67350. # Get a list of the constants that were added
  67351. new_constants = mod.local_constants - original_constants
  67352. # self[namespace] returns an Array of the constants that are being evaluated
  67353. # for that namespace. For instance, if parent.rb requires child.rb, the first
  67354. # element of self[Object] will be an Array of the constants that were present
  67355. # before parent.rb was required. The second element will be an Array of the
  67356. # constants that were present before child.rb was required.
  67357. @stack[namespace].each do |namespace_constants|
  67358. namespace_constants.concat(new_constants)
  67359. end
  67360. # Normalize the list of new constants, and add them to the list we will return
  67361. new_constants.each do |suffix|
  67362. constants << ([namespace, suffix] - ["Object"]).join("::")
  67363. end
  67364. end
  67365. constants
  67366. ensure
  67367. # A call to new_constants is always called after a call to watch_namespaces
  67368. pop_modules(@watching.pop)
  67369. end
  67370. # Add a set of modules to the watch stack, remembering the initial
  67371. # constants.
  67372. def watch_namespaces(namespaces)
  67373. @watching << namespaces.map do |namespace|
  67374. module_name = Dependencies.to_constant_name(namespace)
  67375. original_constants = Dependencies.qualified_const_defined?(module_name) ?
  67376. Inflector.constantize(module_name).local_constants : []
  67377. @stack[module_name] << original_constants
  67378. module_name
  67379. end
  67380. end
  67381. private
  67382. def pop_modules(modules)
  67383. modules.each { |mod| @stack[mod].pop }
  67384. end
  67385. end
  67386. # An internal stack used to record which constants are loaded by any block.
  67387. mattr_accessor :constant_watch_stack
  67388. self.constant_watch_stack = WatchStack.new
  67389. # Module includes this module.
  67390. module ModuleConstMissing #:nodoc:
  67391. def self.append_features(base)
  67392. base.class_eval do
  67393. # Emulate #exclude via an ivar
  67394. return if defined?(@_const_missing) && @_const_missing
  67395. @_const_missing = instance_method(:const_missing)
  67396. remove_method(:const_missing)
  67397. end
  67398. super
  67399. end
  67400. def self.exclude_from(base)
  67401. base.class_eval do
  67402. define_method :const_missing, @_const_missing
  67403. @_const_missing = nil
  67404. end
  67405. end
  67406. def const_missing(const_name)
  67407. # The interpreter does not pass nesting information, and in the
  67408. # case of anonymous modules we cannot even make the trade-off of
  67409. # assuming their name reflects the nesting. Resort to Object as
  67410. # the only meaningful guess we can make.
  67411. from_mod = anonymous? ? ::Object : self
  67412. Dependencies.load_missing_constant(from_mod, const_name)
  67413. end
  67414. def unloadable(const_desc = self)
  67415. super(const_desc)
  67416. end
  67417. end
  67418. # Object includes this module.
  67419. module Loadable #:nodoc:
  67420. def self.exclude_from(base)
  67421. base.class_eval { define_method(:load, Kernel.instance_method(:load)) }
  67422. end
  67423. def require_or_load(file_name)
  67424. Dependencies.require_or_load(file_name)
  67425. end
  67426. def require_dependency(file_name, message = "No such file to load -- %s")
  67427. unless file_name.is_a?(String)
  67428. raise ArgumentError, "the file name must be a String -- you passed #{file_name.inspect}"
  67429. end
  67430. Dependencies.depend_on(file_name, message)
  67431. end
  67432. def load_dependency(file)
  67433. if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
  67434. Dependencies.new_constants_in(Object) { yield }
  67435. else
  67436. yield
  67437. end
  67438. rescue Exception => exception # errors from loading file
  67439. exception.blame_file! file
  67440. raise
  67441. end
  67442. def load(file, wrap = false)
  67443. result = false
  67444. load_dependency(file) { result = super }
  67445. result
  67446. end
  67447. def require(file)
  67448. result = false
  67449. load_dependency(file) { result = super }
  67450. result
  67451. end
  67452. # Mark the given constant as unloadable. Unloadable constants are removed
  67453. # each time dependencies are cleared.
  67454. #
  67455. # Note that marking a constant for unloading need only be done once. Setup
  67456. # or init scripts may list each unloadable constant that may need unloading;
  67457. # each constant will be removed for every subsequent clear, as opposed to
  67458. # for the first clear.
  67459. #
  67460. # The provided constant descriptor may be a (non-anonymous) module or class,
  67461. # or a qualified constant name as a string or symbol.
  67462. #
  67463. # Returns +true+ if the constant was not previously marked for unloading,
  67464. # +false+ otherwise.
  67465. def unloadable(const_desc)
  67466. Dependencies.mark_for_unload const_desc
  67467. end
  67468. end
  67469. # Exception file-blaming.
  67470. module Blamable #:nodoc:
  67471. def blame_file!(file)
  67472. (@blamed_files ||= []).unshift file
  67473. end
  67474. def blamed_files
  67475. @blamed_files ||= []
  67476. end
  67477. def describe_blame
  67478. return nil if blamed_files.empty?
  67479. "This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
  67480. end
  67481. def copy_blame!(exc)
  67482. @blamed_files = exc.blamed_files.clone
  67483. self
  67484. end
  67485. end
  67486. def hook!
  67487. Object.class_eval { include Loadable }
  67488. Module.class_eval { include ModuleConstMissing }
  67489. Exception.class_eval { include Blamable }
  67490. end
  67491. def unhook!
  67492. ModuleConstMissing.exclude_from(Module)
  67493. Loadable.exclude_from(Object)
  67494. end
  67495. def load?
  67496. mechanism == :load
  67497. end
  67498. def depend_on(file_name, message = "No such file to load -- %s.rb")
  67499. path = search_for_file(file_name)
  67500. require_or_load(path || file_name)
  67501. rescue LoadError => load_error
  67502. if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
  67503. load_error.message.replace(message % file_name)
  67504. load_error.copy_blame!(load_error)
  67505. end
  67506. raise
  67507. end
  67508. def clear
  67509. log_call
  67510. loaded.clear
  67511. remove_unloadable_constants!
  67512. end
  67513. def require_or_load(file_name, const_path = nil)
  67514. log_call file_name, const_path
  67515. file_name = $` if file_name =~ /\.rb\z/
  67516. expanded = File.expand_path(file_name)
  67517. return if loaded.include?(expanded)
  67518. # Record that we've seen this file *before* loading it to avoid an
  67519. # infinite loop with mutual dependencies.
  67520. loaded << expanded
  67521. begin
  67522. if load?
  67523. log "loading #{file_name}"
  67524. # Enable warnings if this file has not been loaded before and
  67525. # warnings_on_first_load is set.
  67526. load_args = ["#{file_name}.rb"]
  67527. load_args << const_path unless const_path.nil?
  67528. if !warnings_on_first_load or history.include?(expanded)
  67529. result = load_file(*load_args)
  67530. else
  67531. enable_warnings { result = load_file(*load_args) }
  67532. end
  67533. else
  67534. log "requiring #{file_name}"
  67535. result = require file_name
  67536. end
  67537. rescue Exception
  67538. loaded.delete expanded
  67539. raise
  67540. end
  67541. # Record history *after* loading so first load gets warnings.
  67542. history << expanded
  67543. result
  67544. end
  67545. # Is the provided constant path defined?
  67546. def qualified_const_defined?(path)
  67547. Object.qualified_const_defined?(path.sub(/^::/, ''), false)
  67548. end
  67549. # Given +path+, a filesystem path to a ruby file, return an array of
  67550. # constant paths which would cause Dependencies to attempt to load this
  67551. # file.
  67552. def loadable_constants_for_path(path, bases = autoload_paths)
  67553. path = $` if path =~ /\.rb\z/
  67554. expanded_path = File.expand_path(path)
  67555. paths = []
  67556. bases.each do |root|
  67557. expanded_root = File.expand_path(root)
  67558. next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
  67559. nesting = expanded_path[(expanded_root.size)..-1]
  67560. nesting = nesting[1..-1] if nesting && nesting[0] == ?/
  67561. next if nesting.blank?
  67562. paths << nesting.camelize
  67563. end
  67564. paths.uniq!
  67565. paths
  67566. end
  67567. # Search for a file in autoload_paths matching the provided suffix.
  67568. def search_for_file(path_suffix)
  67569. path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb")
  67570. autoload_paths.each do |root|
  67571. path = File.join(root, path_suffix)
  67572. return path if File.file? path
  67573. end
  67574. nil # Gee, I sure wish we had first_match ;-)
  67575. end
  67576. # Does the provided path_suffix correspond to an autoloadable module?
  67577. # Instead of returning a boolean, the autoload base for this module is
  67578. # returned.
  67579. def autoloadable_module?(path_suffix)
  67580. autoload_paths.each do |load_path|
  67581. return load_path if File.directory? File.join(load_path, path_suffix)
  67582. end
  67583. nil
  67584. end
  67585. def load_once_path?(path)
  67586. # to_s works around a ruby1.9 issue where #starts_with?(Pathname) will always return false
  67587. autoload_once_paths.any? { |base| path.starts_with? base.to_s }
  67588. end
  67589. # Attempt to autoload the provided module name by searching for a directory
  67590. # matching the expected path suffix. If found, the module is created and
  67591. # assigned to +into+'s constants with the name +const_name+. Provided that
  67592. # the directory was loaded from a reloadable base path, it is added to the
  67593. # set of constants that are to be unloaded.
  67594. def autoload_module!(into, const_name, qualified_name, path_suffix)
  67595. return nil unless base_path = autoloadable_module?(path_suffix)
  67596. mod = Module.new
  67597. into.const_set const_name, mod
  67598. autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
  67599. mod
  67600. end
  67601. # Load the file at the provided path. +const_paths+ is a set of qualified
  67602. # constant names. When loading the file, Dependencies will watch for the
  67603. # addition of these constants. Each that is defined will be marked as
  67604. # autoloaded, and will be removed when Dependencies.clear is next called.
  67605. #
  67606. # If the second parameter is left off, then Dependencies will construct a
  67607. # set of names that the file at +path+ may define. See
  67608. # +loadable_constants_for_path+ for more details.
  67609. def load_file(path, const_paths = loadable_constants_for_path(path))
  67610. log_call path, const_paths
  67611. const_paths = [const_paths].compact unless const_paths.is_a? Array
  67612. parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || :Object }
  67613. result = nil
  67614. newly_defined_paths = new_constants_in(*parent_paths) do
  67615. result = Kernel.load path
  67616. end
  67617. autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
  67618. autoloaded_constants.uniq!
  67619. log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
  67620. result
  67621. end
  67622. # Returns the constant path for the provided parent and constant name.
  67623. def qualified_name_for(mod, name)
  67624. mod_name = to_constant_name mod
  67625. mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
  67626. end
  67627. # Load the constant named +const_name+ which is missing from +from_mod+. If
  67628. # it is not possible to load the constant into from_mod, try its parent
  67629. # module using +const_missing+.
  67630. def load_missing_constant(from_mod, const_name)
  67631. log_call from_mod, const_name
  67632. unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
  67633. raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
  67634. end
  67635. raise NameError, "#{from_mod} is not missing constant #{const_name}!" if from_mod.const_defined?(const_name, false)
  67636. qualified_name = qualified_name_for from_mod, const_name
  67637. path_suffix = qualified_name.underscore
  67638. file_path = search_for_file(path_suffix)
  67639. if file_path
  67640. expanded = File.expand_path(file_path)
  67641. expanded.sub!(/\.rb\z/, '')
  67642. if loaded.include?(expanded)
  67643. raise "Circular dependency detected while autoloading constant #{qualified_name}"
  67644. else
  67645. require_or_load(expanded)
  67646. raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
  67647. return from_mod.const_get(const_name)
  67648. end
  67649. elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
  67650. return mod
  67651. elsif (parent = from_mod.parent) && parent != from_mod &&
  67652. ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
  67653. # If our parents do not have a constant named +const_name+ then we are free
  67654. # to attempt to load upwards. If they do have such a constant, then this
  67655. # const_missing must be due to from_mod::const_name, which should not
  67656. # return constants from from_mod's parents.
  67657. begin
  67658. # Since Ruby does not pass the nesting at the point the unknown
  67659. # constant triggered the callback we cannot fully emulate constant
  67660. # name lookup and need to make a trade-off: we are going to assume
  67661. # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
  67662. # though it might not be. Counterexamples are
  67663. #
  67664. # class Foo::Bar
  67665. # Module.nesting # => [Foo::Bar]
  67666. # end
  67667. #
  67668. # or
  67669. #
  67670. # module M::N
  67671. # module S::T
  67672. # Module.nesting # => [S::T, M::N]
  67673. # end
  67674. # end
  67675. #
  67676. # for example.
  67677. return parent.const_missing(const_name)
  67678. rescue NameError => e
  67679. raise unless e.missing_name? qualified_name_for(parent, const_name)
  67680. end
  67681. end
  67682. raise NameError,
  67683. "uninitialized constant #{qualified_name}",
  67684. caller.reject { |l| l.starts_with? __FILE__ }
  67685. end
  67686. # Remove the constants that have been autoloaded, and those that have been
  67687. # marked for unloading. Before each constant is removed a callback is sent
  67688. # to its class/module if it implements +before_remove_const+.
  67689. #
  67690. # The callback implementation should be restricted to cleaning up caches, etc.
  67691. # as the environment will be in an inconsistent state, e.g. other constants
  67692. # may have already been unloaded and not accessible.
  67693. def remove_unloadable_constants!
  67694. autoloaded_constants.each { |const| remove_constant const }
  67695. autoloaded_constants.clear
  67696. Reference.clear!
  67697. explicitly_unloadable_constants.each { |const| remove_constant const }
  67698. end
  67699. class ClassCache
  67700. def initialize
  67701. @store = ThreadSafe::Cache.new
  67702. end
  67703. def empty?
  67704. @store.empty?
  67705. end
  67706. def key?(key)
  67707. @store.key?(key)
  67708. end
  67709. def get(key)
  67710. key = key.name if key.respond_to?(:name)
  67711. @store[key] ||= Inflector.constantize(key)
  67712. end
  67713. alias :[] :get
  67714. def safe_get(key)
  67715. key = key.name if key.respond_to?(:name)
  67716. @store[key] ||= Inflector.safe_constantize(key)
  67717. end
  67718. def store(klass)
  67719. return self unless klass.respond_to?(:name)
  67720. raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty?
  67721. @store[klass.name] = klass
  67722. self
  67723. end
  67724. def clear!
  67725. @store.clear
  67726. end
  67727. end
  67728. Reference = ClassCache.new
  67729. # Store a reference to a class +klass+.
  67730. def reference(klass)
  67731. Reference.store klass
  67732. end
  67733. # Get the reference for class named +name+.
  67734. # Raises an exception if referenced class does not exist.
  67735. def constantize(name)
  67736. Reference.get(name)
  67737. end
  67738. # Get the reference for class named +name+ if one exists.
  67739. # Otherwise returns +nil+.
  67740. def safe_constantize(name)
  67741. Reference.safe_get(name)
  67742. end
  67743. # Determine if the given constant has been automatically loaded.
  67744. def autoloaded?(desc)
  67745. return false if desc.is_a?(Module) && desc.anonymous?
  67746. name = to_constant_name desc
  67747. return false unless qualified_const_defined? name
  67748. return autoloaded_constants.include?(name)
  67749. end
  67750. # Will the provided constant descriptor be unloaded?
  67751. def will_unload?(const_desc)
  67752. autoloaded?(const_desc) ||
  67753. explicitly_unloadable_constants.include?(to_constant_name(const_desc))
  67754. end
  67755. # Mark the provided constant name for unloading. This constant will be
  67756. # unloaded on each request, not just the next one.
  67757. def mark_for_unload(const_desc)
  67758. name = to_constant_name const_desc
  67759. if explicitly_unloadable_constants.include? name
  67760. false
  67761. else
  67762. explicitly_unloadable_constants << name
  67763. true
  67764. end
  67765. end
  67766. # Run the provided block and detect the new constants that were loaded during
  67767. # its execution. Constants may only be regarded as 'new' once -- so if the
  67768. # block calls +new_constants_in+ again, then the constants defined within the
  67769. # inner call will not be reported in this one.
  67770. #
  67771. # If the provided block does not run to completion, and instead raises an
  67772. # exception, any new constants are regarded as being only partially defined
  67773. # and will be removed immediately.
  67774. def new_constants_in(*descs)
  67775. log_call(*descs)
  67776. constant_watch_stack.watch_namespaces(descs)
  67777. aborting = true
  67778. begin
  67779. yield # Now yield to the code that is to define new constants.
  67780. aborting = false
  67781. ensure
  67782. new_constants = constant_watch_stack.new_constants
  67783. log "New constants: #{new_constants * ', '}"
  67784. return new_constants unless aborting
  67785. log "Error during loading, removing partially loaded constants "
  67786. new_constants.each { |c| remove_constant(c) }.clear
  67787. end
  67788. []
  67789. end
  67790. # Convert the provided const desc to a qualified constant name (as a string).
  67791. # A module, class, symbol, or string may be provided.
  67792. def to_constant_name(desc) #:nodoc:
  67793. case desc
  67794. when String then desc.sub(/^::/, '')
  67795. when Symbol then desc.to_s
  67796. when Module
  67797. desc.name.presence ||
  67798. raise(ArgumentError, "Anonymous modules have no name to be referenced by")
  67799. else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
  67800. end
  67801. end
  67802. def remove_constant(const) #:nodoc:
  67803. # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
  67804. normalized = const.to_s.sub(/\A::/, '')
  67805. normalized.sub!(/\A(Object::)+/, '')
  67806. constants = normalized.split('::')
  67807. to_remove = constants.pop
  67808. if constants.empty?
  67809. parent = Object
  67810. else
  67811. # This method is robust to non-reachable constants.
  67812. #
  67813. # Non-reachable constants may be passed if some of the parents were
  67814. # autoloaded and already removed. It is easier to do a sanity check
  67815. # here than require the caller to be clever. We check the parent
  67816. # rather than the very const argument because we do not want to
  67817. # trigger Kernel#autoloads, see the comment below.
  67818. parent_name = constants.join('::')
  67819. return unless qualified_const_defined?(parent_name)
  67820. parent = constantize(parent_name)
  67821. end
  67822. log "removing constant #{const}"
  67823. # In an autoloaded user.rb like this
  67824. #
  67825. # autoload :Foo, 'foo'
  67826. #
  67827. # class User < ActiveRecord::Base
  67828. # end
  67829. #
  67830. # we correctly register "Foo" as being autoloaded. But if the app does
  67831. # not use the "Foo" constant we need to be careful not to trigger
  67832. # loading "foo.rb" ourselves. While #const_defined? and #const_get? do
  67833. # require the file, #autoload? and #remove_const don't.
  67834. #
  67835. # We are going to remove the constant nonetheless ---which exists as
  67836. # far as Ruby is concerned--- because if the user removes the macro
  67837. # call from a class or module that were not autoloaded, as in the
  67838. # example above with Object, accessing to that constant must err.
  67839. unless parent.autoload?(to_remove)
  67840. begin
  67841. constantized = parent.const_get(to_remove, false)
  67842. rescue NameError
  67843. log "the constant #{const} is not reachable anymore, skipping"
  67844. return
  67845. else
  67846. constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
  67847. end
  67848. end
  67849. begin
  67850. parent.instance_eval { remove_const to_remove }
  67851. rescue NameError
  67852. log "the constant #{const} is not reachable anymore, skipping"
  67853. end
  67854. end
  67855. protected
  67856. def log_call(*args)
  67857. if log_activity?
  67858. arg_str = args.collect { |arg| arg.inspect } * ', '
  67859. /in `([a-z_\?\!]+)'/ =~ caller(1).first
  67860. selector = $1 || '<unknown>'
  67861. log "called #{selector}(#{arg_str})"
  67862. end
  67863. end
  67864. def log(msg)
  67865. logger.debug "Dependencies: #{msg}" if log_activity?
  67866. end
  67867. def log_activity?
  67868. logger && log_activity
  67869. end
  67870. end
  67871. end
  67872. ActiveSupport::Dependencies.hook!
  67873. require "active_support/notifications"
  67874. module ActiveSupport
  67875. class Deprecation
  67876. # Default warning behaviors per Rails.env.
  67877. DEFAULT_BEHAVIORS = {
  67878. :stderr => Proc.new { |message, callstack|
  67879. $stderr.puts(message)
  67880. $stderr.puts callstack.join("\n ") if debug
  67881. },
  67882. :log => Proc.new { |message, callstack|
  67883. logger =
  67884. if defined?(Rails) && Rails.logger
  67885. Rails.logger
  67886. else
  67887. require 'active_support/logger'
  67888. ActiveSupport::Logger.new($stderr)
  67889. end
  67890. logger.warn message
  67891. logger.debug callstack.join("\n ") if debug
  67892. },
  67893. :notify => Proc.new { |message, callstack|
  67894. ActiveSupport::Notifications.instrument("deprecation.rails",
  67895. :message => message, :callstack => callstack)
  67896. },
  67897. :silence => Proc.new { |message, callstack| }
  67898. }
  67899. module Behavior
  67900. # Whether to print a backtrace along with the warning.
  67901. attr_accessor :debug
  67902. # Returns the current behavior or if one isn't set, defaults to +:stderr+.
  67903. def behavior
  67904. @behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
  67905. end
  67906. # Sets the behavior to the specified value. Can be a single value, array,
  67907. # or an object that responds to +call+.
  67908. #
  67909. # Available behaviors:
  67910. #
  67911. # [+stderr+] Log all deprecation warnings to +$stderr+.
  67912. # [+log+] Log all deprecation warnings to +Rails.logger+.
  67913. # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
  67914. # [+silence+] Do nothing.
  67915. #
  67916. # Setting behaviors only affects deprecations that happen after boot time.
  67917. # Deprecation warnings raised by gems are not affected by this setting
  67918. # because they happen before Rails boots up.
  67919. #
  67920. # ActiveSupport::Deprecation.behavior = :stderr
  67921. # ActiveSupport::Deprecation.behavior = [:stderr, :log]
  67922. # ActiveSupport::Deprecation.behavior = MyCustomHandler
  67923. # ActiveSupport::Deprecation.behavior = proc { |message, callstack|
  67924. # # custom stuff
  67925. # }
  67926. def behavior=(behavior)
  67927. @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
  67928. end
  67929. end
  67930. end
  67931. end
  67932. require 'active_support/core_ext/kernel/singleton_class'
  67933. require 'active_support/core_ext/module/delegation'
  67934. module ActiveSupport
  67935. class Deprecation
  67936. module InstanceDelegator # :nodoc:
  67937. def self.included(base)
  67938. base.extend(ClassMethods)
  67939. base.public_class_method :new
  67940. end
  67941. module ClassMethods # :nodoc:
  67942. def include(included_module)
  67943. included_module.instance_methods.each { |m| method_added(m) }
  67944. super
  67945. end
  67946. def method_added(method_name)
  67947. singleton_class.delegate(method_name, to: :instance)
  67948. end
  67949. end
  67950. end
  67951. end
  67952. end
  67953. require 'active_support/core_ext/module/aliasing'
  67954. require 'active_support/core_ext/array/extract_options'
  67955. module ActiveSupport
  67956. class Deprecation
  67957. module MethodWrapper
  67958. # Declare that a method has been deprecated.
  67959. #
  67960. # module Fred
  67961. # extend self
  67962. #
  67963. # def foo; end
  67964. # def bar; end
  67965. # def baz; end
  67966. # end
  67967. #
  67968. # ActiveSupport::Deprecation.deprecate_methods(Fred, :foo, bar: :qux, baz: 'use Bar#baz instead')
  67969. # # => [:foo, :bar, :baz]
  67970. #
  67971. # Fred.foo
  67972. # # => "DEPRECATION WARNING: foo is deprecated and will be removed from Rails 4.1."
  67973. #
  67974. # Fred.bar
  67975. # # => "DEPRECATION WARNING: bar is deprecated and will be removed from Rails 4.1 (use qux instead)."
  67976. #
  67977. # Fred.baz
  67978. # # => "DEPRECATION WARNING: baz is deprecated and will be removed from Rails 4.1 (use Bar#baz instead)."
  67979. def deprecate_methods(target_module, *method_names)
  67980. options = method_names.extract_options!
  67981. deprecator = options.delete(:deprecator) || ActiveSupport::Deprecation.instance
  67982. method_names += options.keys
  67983. method_names.each do |method_name|
  67984. target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation|
  67985. target_module.send(:define_method, "#{target}_with_deprecation#{punctuation}") do |*args, &block|
  67986. deprecator.deprecation_warning(method_name, options[method_name])
  67987. send(:"#{target}_without_deprecation#{punctuation}", *args, &block)
  67988. end
  67989. end
  67990. end
  67991. end
  67992. end
  67993. end
  67994. end
  67995. require 'active_support/inflector/methods'
  67996. module ActiveSupport
  67997. class Deprecation
  67998. class DeprecationProxy #:nodoc:
  67999. def self.new(*args, &block)
  68000. object = args.first
  68001. return object unless object
  68002. super
  68003. end
  68004. instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ }
  68005. # Don't give a deprecation warning on inspect since test/unit and error
  68006. # logs rely on it for diagnostics.
  68007. def inspect
  68008. target.inspect
  68009. end
  68010. private
  68011. def method_missing(called, *args, &block)
  68012. warn caller, called, args
  68013. target.__send__(called, *args, &block)
  68014. end
  68015. end
  68016. # This DeprecatedObjectProxy transforms object to depracated object.
  68017. #
  68018. # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!")
  68019. # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance)
  68020. #
  68021. # When someone executes any method except +inspect+ on proxy object this will
  68022. # trigger +warn+ method on +deprecator_instance+.
  68023. #
  68024. # Default deprecator is <tt>ActiveSupport::Deprecation</tt>
  68025. class DeprecatedObjectProxy < DeprecationProxy
  68026. def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
  68027. @object = object
  68028. @message = message
  68029. @deprecator = deprecator
  68030. end
  68031. private
  68032. def target
  68033. @object
  68034. end
  68035. def warn(callstack, called, args)
  68036. @deprecator.warn(@message, callstack)
  68037. end
  68038. end
  68039. # This DeprecatedInstanceVariableProxy transforms instance variable to
  68040. # depracated instance variable.
  68041. #
  68042. # class Example
  68043. # def initialize(deprecator)
  68044. # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
  68045. # @_request = :a_request
  68046. # end
  68047. #
  68048. # def request
  68049. # @_request
  68050. # end
  68051. #
  68052. # def old_request
  68053. # @request
  68054. # end
  68055. # end
  68056. #
  68057. # When someone execute any method on @request variable this will trigger
  68058. # +warn+ method on +deprecator_instance+ and will fetch <tt>@_request</tt>
  68059. # variable via +request+ method and execute the same method on non-proxy
  68060. # instance variable.
  68061. #
  68062. # Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
  68063. class DeprecatedInstanceVariableProxy < DeprecationProxy
  68064. def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
  68065. @instance = instance
  68066. @method = method
  68067. @var = var
  68068. @deprecator = deprecator
  68069. end
  68070. private
  68071. def target
  68072. @instance.__send__(@method)
  68073. end
  68074. def warn(callstack, called, args)
  68075. @deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
  68076. end
  68077. end
  68078. # This DeprecatedConstantProxy transforms constant to depracated constant.
  68079. #
  68080. # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
  68081. # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)
  68082. #
  68083. # When someone use old constant this will trigger +warn+ method on
  68084. # +deprecator_instance+.
  68085. #
  68086. # Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
  68087. class DeprecatedConstantProxy < DeprecationProxy
  68088. def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance)
  68089. @old_const = old_const
  68090. @new_const = new_const
  68091. @deprecator = deprecator
  68092. end
  68093. def class
  68094. target.class
  68095. end
  68096. private
  68097. def target
  68098. ActiveSupport::Inflector.constantize(@new_const.to_s)
  68099. end
  68100. def warn(callstack, called, args)
  68101. @deprecator.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack)
  68102. end
  68103. end
  68104. end
  68105. end
  68106. module ActiveSupport
  68107. class Deprecation
  68108. module Reporting
  68109. # Whether to print a message (silent mode)
  68110. attr_accessor :silenced
  68111. # Name of gem where method is deprecated
  68112. attr_accessor :gem_name
  68113. # Outputs a deprecation warning to the output configured by
  68114. # <tt>ActiveSupport::Deprecation.behavior</tt>.
  68115. #
  68116. # ActiveSupport::Deprecation.warn('something broke!')
  68117. # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
  68118. def warn(message = nil, callstack = nil)
  68119. return if silenced
  68120. callstack ||= caller(2)
  68121. deprecation_message(callstack, message).tap do |m|
  68122. behavior.each { |b| b.call(m, callstack) }
  68123. end
  68124. end
  68125. # Silence deprecation warnings within the block.
  68126. #
  68127. # ActiveSupport::Deprecation.warn('something broke!')
  68128. # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
  68129. #
  68130. # ActiveSupport::Deprecation.silence do
  68131. # ActiveSupport::Deprecation.warn('something broke!')
  68132. # end
  68133. # # => nil
  68134. def silence
  68135. old_silenced, @silenced = @silenced, true
  68136. yield
  68137. ensure
  68138. @silenced = old_silenced
  68139. end
  68140. def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
  68141. caller_backtrace ||= caller(2)
  68142. deprecated_method_warning(deprecated_method_name, message).tap do |msg|
  68143. warn(msg, caller_backtrace)
  68144. end
  68145. end
  68146. private
  68147. # Outputs a deprecation warning message
  68148. #
  68149. # ActiveSupport::Deprecation.deprecated_method_warning(:method_name)
  68150. # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
  68151. # ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method)
  68152. # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
  68153. # ActiveSupport::Deprecation.deprecated_method_warning(:method_name, "Optional message")
  68154. # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
  68155. def deprecated_method_warning(method_name, message = nil)
  68156. warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
  68157. case message
  68158. when Symbol then "#{warning} (use #{message} instead)"
  68159. when String then "#{warning} (#{message})"
  68160. else warning
  68161. end
  68162. end
  68163. def deprecation_message(callstack, message = nil)
  68164. message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
  68165. message += '.' unless message =~ /\.$/
  68166. "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
  68167. end
  68168. def deprecation_caller_message(callstack)
  68169. file, line, method = extract_callstack(callstack)
  68170. if file
  68171. if line && method
  68172. "(called from #{method} at #{file}:#{line})"
  68173. else
  68174. "(called from #{file}:#{line})"
  68175. end
  68176. end
  68177. end
  68178. def extract_callstack(callstack)
  68179. rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
  68180. offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first
  68181. if offending_line
  68182. if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
  68183. md.captures
  68184. else
  68185. offending_line
  68186. end
  68187. end
  68188. end
  68189. end
  68190. end
  68191. end
  68192. require 'singleton'
  68193. module ActiveSupport
  68194. # \Deprecation specifies the API used by Rails to deprecate methods, instance
  68195. # variables, objects and constants.
  68196. class Deprecation
  68197. # active_support.rb sets an autoload for ActiveSupport::Deprecation.
  68198. #
  68199. # If these requires were at the top of the file the constant would not be
  68200. # defined by the time their files were loaded. Since some of them reopen
  68201. # ActiveSupport::Deprecation its autoload would be triggered, resulting in
  68202. # a circular require warning for active_support/deprecation.rb.
  68203. #
  68204. # So, we define the constant first, and load dependencies later.
  68205. require 'active_support/deprecation/instance_delegator'
  68206. require 'active_support/deprecation/behaviors'
  68207. require 'active_support/deprecation/reporting'
  68208. require 'active_support/deprecation/method_wrappers'
  68209. require 'active_support/deprecation/proxy_wrappers'
  68210. require 'active_support/core_ext/module/deprecation'
  68211. include Singleton
  68212. include InstanceDelegator
  68213. include Behavior
  68214. include Reporting
  68215. include MethodWrapper
  68216. # The version the deprecated behavior will be removed, by default.
  68217. attr_accessor :deprecation_horizon
  68218. # It accepts two parameters on initialization. The first is an version of library
  68219. # and the second is an library name
  68220. #
  68221. # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
  68222. def initialize(deprecation_horizon = '4.1', gem_name = 'Rails')
  68223. self.gem_name = gem_name
  68224. self.deprecation_horizon = deprecation_horizon
  68225. # By default, warnings are not silenced and debugging is off.
  68226. self.silenced = false
  68227. self.debug = false
  68228. end
  68229. end
  68230. end
  68231. module ActiveSupport
  68232. # This module provides an internal implementation to track descendants
  68233. # which is faster than iterating through ObjectSpace.
  68234. module DescendantsTracker
  68235. @@direct_descendants = {}
  68236. class << self
  68237. def direct_descendants(klass)
  68238. @@direct_descendants[klass] || []
  68239. end
  68240. def descendants(klass)
  68241. arr = []
  68242. accumulate_descendants(klass, arr)
  68243. arr
  68244. end
  68245. def clear
  68246. if defined? ActiveSupport::Dependencies
  68247. @@direct_descendants.each do |klass, descendants|
  68248. if ActiveSupport::Dependencies.autoloaded?(klass)
  68249. @@direct_descendants.delete(klass)
  68250. else
  68251. descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
  68252. end
  68253. end
  68254. else
  68255. @@direct_descendants.clear
  68256. end
  68257. end
  68258. # This is the only method that is not thread safe, but is only ever called
  68259. # during the eager loading phase.
  68260. def store_inherited(klass, descendant)
  68261. (@@direct_descendants[klass] ||= []) << descendant
  68262. end
  68263. private
  68264. def accumulate_descendants(klass, acc)
  68265. if direct_descendants = @@direct_descendants[klass]
  68266. acc.concat(direct_descendants)
  68267. direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
  68268. end
  68269. end
  68270. end
  68271. def inherited(base)
  68272. DescendantsTracker.store_inherited(self, base)
  68273. super
  68274. end
  68275. def direct_descendants
  68276. DescendantsTracker.direct_descendants(self)
  68277. end
  68278. def descendants
  68279. DescendantsTracker.descendants(self)
  68280. end
  68281. end
  68282. end
  68283. require 'active_support/proxy_object'
  68284. require 'active_support/core_ext/array/conversions'
  68285. require 'active_support/core_ext/object/acts_like'
  68286. module ActiveSupport
  68287. # Provides accurate date and time measurements using Date#advance and
  68288. # Time#advance, respectively. It mainly supports the methods on Numeric.
  68289. #
  68290. # 1.month.ago # equivalent to Time.now.advance(months: -1)
  68291. class Duration < ProxyObject
  68292. attr_accessor :value, :parts
  68293. def initialize(value, parts) #:nodoc:
  68294. @value, @parts = value, parts
  68295. end
  68296. # Adds another Duration or a Numeric to this Duration. Numeric values
  68297. # are treated as seconds.
  68298. def +(other)
  68299. if Duration === other
  68300. Duration.new(value + other.value, @parts + other.parts)
  68301. else
  68302. Duration.new(value + other, @parts + [[:seconds, other]])
  68303. end
  68304. end
  68305. # Subtracts another Duration or a Numeric from this Duration. Numeric
  68306. # values are treated as seconds.
  68307. def -(other)
  68308. self + (-other)
  68309. end
  68310. def -@ #:nodoc:
  68311. Duration.new(-value, parts.map { |type,number| [type, -number] })
  68312. end
  68313. def is_a?(klass) #:nodoc:
  68314. Duration == klass || value.is_a?(klass)
  68315. end
  68316. alias :kind_of? :is_a?
  68317. # Returns +true+ if +other+ is also a Duration instance with the
  68318. # same +value+, or if <tt>other == value</tt>.
  68319. def ==(other)
  68320. if Duration === other
  68321. other.value == value
  68322. else
  68323. other == value
  68324. end
  68325. end
  68326. def self.===(other) #:nodoc:
  68327. other.is_a?(Duration)
  68328. rescue ::NoMethodError
  68329. false
  68330. end
  68331. # Calculates a new Time or Date that is as far in the future
  68332. # as this Duration represents.
  68333. def since(time = ::Time.current)
  68334. sum(1, time)
  68335. end
  68336. alias :from_now :since
  68337. # Calculates a new Time or Date that is as far in the past
  68338. # as this Duration represents.
  68339. def ago(time = ::Time.current)
  68340. sum(-1, time)
  68341. end
  68342. alias :until :ago
  68343. def inspect #:nodoc:
  68344. consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
  68345. parts = [:years, :months, :days, :minutes, :seconds].map do |length|
  68346. n = consolidated[length]
  68347. "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
  68348. end.compact
  68349. parts = ["0 seconds"] if parts.empty?
  68350. parts.to_sentence(:locale => :en)
  68351. end
  68352. def as_json(options = nil) #:nodoc:
  68353. to_i
  68354. end
  68355. protected
  68356. def sum(sign, time = ::Time.current) #:nodoc:
  68357. parts.inject(time) do |t,(type,number)|
  68358. if t.acts_like?(:time) || t.acts_like?(:date)
  68359. if type == :seconds
  68360. t.since(sign * number)
  68361. else
  68362. t.advance(type => sign * number)
  68363. end
  68364. else
  68365. raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
  68366. end
  68367. end
  68368. end
  68369. private
  68370. def method_missing(method, *args, &block) #:nodoc:
  68371. value.send(method, *args, &block)
  68372. end
  68373. end
  68374. end
  68375. module ActiveSupport
  68376. # FileUpdateChecker specifies the API used by Rails to watch files
  68377. # and control reloading. The API depends on four methods:
  68378. #
  68379. # * +initialize+ which expects two parameters and one block as
  68380. # described below.
  68381. #
  68382. # * +updated?+ which returns a boolean if there were updates in
  68383. # the filesystem or not.
  68384. #
  68385. # * +execute+ which executes the given block on initialization
  68386. # and updates the latest watched files and timestamp.
  68387. #
  68388. # * +execute_if_updated+ which just executes the block if it was updated.
  68389. #
  68390. # After initialization, a call to +execute_if_updated+ must execute
  68391. # the block only if there was really a change in the filesystem.
  68392. #
  68393. # This class is used by Rails to reload the I18n framework whenever
  68394. # they are changed upon a new request.
  68395. #
  68396. # i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
  68397. # I18n.reload!
  68398. # end
  68399. #
  68400. # ActionDispatch::Reloader.to_prepare do
  68401. # i18n_reloader.execute_if_updated
  68402. # end
  68403. class FileUpdateChecker
  68404. # It accepts two parameters on initialization. The first is an array
  68405. # of files and the second is an optional hash of directories. The hash must
  68406. # have directories as keys and the value is an array of extensions to be
  68407. # watched under that directory.
  68408. #
  68409. # This method must also receive a block that will be called once a path
  68410. # changes. The array of files and list of directories cannot be changed
  68411. # after FileUpdateChecker has been initialized.
  68412. def initialize(files, dirs={}, &block)
  68413. @files = files.freeze
  68414. @glob = compile_glob(dirs)
  68415. @block = block
  68416. @watched = nil
  68417. @updated_at = nil
  68418. @last_watched = watched
  68419. @last_update_at = updated_at(@last_watched)
  68420. end
  68421. # Check if any of the entries were updated. If so, the watched and/or
  68422. # updated_at values are cached until the block is executed via +execute+
  68423. # or +execute_if_updated+.
  68424. def updated?
  68425. current_watched = watched
  68426. if @last_watched.size != current_watched.size
  68427. @watched = current_watched
  68428. true
  68429. else
  68430. current_updated_at = updated_at(current_watched)
  68431. if @last_update_at < current_updated_at
  68432. @watched = current_watched
  68433. @updated_at = current_updated_at
  68434. true
  68435. else
  68436. false
  68437. end
  68438. end
  68439. end
  68440. # Executes the given block and updates the latest watched files and
  68441. # timestamp.
  68442. def execute
  68443. @last_watched = watched
  68444. @last_update_at = updated_at(@last_watched)
  68445. @block.call
  68446. ensure
  68447. @watched = nil
  68448. @updated_at = nil
  68449. end
  68450. # Execute the block given if updated.
  68451. def execute_if_updated
  68452. if updated?
  68453. execute
  68454. true
  68455. else
  68456. false
  68457. end
  68458. end
  68459. private
  68460. def watched
  68461. @watched || begin
  68462. all = @files.select { |f| File.exists?(f) }
  68463. all.concat(Dir[@glob]) if @glob
  68464. all
  68465. end
  68466. end
  68467. def updated_at(paths)
  68468. @updated_at || max_mtime(paths) || Time.at(0)
  68469. end
  68470. # This method returns the maximum mtime of the files in +paths+, or +nil+
  68471. # if the array is empty.
  68472. #
  68473. # Files with a mtime in the future are ignored. Such abnormal situation
  68474. # can happen for example if the user changes the clock by hand. It is
  68475. # healthy to consider this edge case because with mtimes in the future
  68476. # reloading is not triggered.
  68477. def max_mtime(paths)
  68478. time_now = Time.now
  68479. paths.map {|path| File.mtime(path)}.reject {|mtime| time_now < mtime}.max
  68480. end
  68481. def compile_glob(hash)
  68482. hash.freeze # Freeze so changes aren't accidently pushed
  68483. return if hash.empty?
  68484. globs = hash.map do |key, value|
  68485. "#{escape(key)}/**/*#{compile_ext(value)}"
  68486. end
  68487. "{#{globs.join(",")}}"
  68488. end
  68489. def escape(key)
  68490. key.gsub(',','\,')
  68491. end
  68492. def compile_ext(array)
  68493. array = Array(array)
  68494. return if array.empty?
  68495. ".{#{array.join(",")}}"
  68496. end
  68497. end
  68498. end
  68499. module ActiveSupport
  68500. class FileWatcher
  68501. class Backend
  68502. def initialize(path, watcher)
  68503. @watcher = watcher
  68504. @path = path
  68505. end
  68506. def trigger(files)
  68507. @watcher.trigger(files)
  68508. end
  68509. end
  68510. def initialize
  68511. @regex_matchers = {}
  68512. end
  68513. def watch(pattern, &block)
  68514. @regex_matchers[pattern] = block
  68515. end
  68516. def trigger(files)
  68517. trigger_files = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
  68518. files.each do |file, state|
  68519. @regex_matchers.each do |pattern, block|
  68520. trigger_files[block][state] << file if pattern === file
  68521. end
  68522. end
  68523. trigger_files.each do |block, payload|
  68524. block.call payload
  68525. end
  68526. end
  68527. end
  68528. end
  68529. require 'zlib'
  68530. require 'stringio'
  68531. module ActiveSupport
  68532. # A convenient wrapper for the zlib standard library that allows
  68533. # compression/decompression of strings with gzip.
  68534. #
  68535. # gzip = ActiveSupport::Gzip.compress('compress me!')
  68536. # # => "\x1F\x8B\b\x00o\x8D\xCDO\x00\x03K\xCE\xCF-(J-.V\xC8MU\x04\x00R>n\x83\f\x00\x00\x00"
  68537. #
  68538. # ActiveSupport::Gzip.decompress(gzip)
  68539. # # => "compress me!"
  68540. module Gzip
  68541. class Stream < StringIO
  68542. def initialize(*)
  68543. super
  68544. set_encoding "BINARY"
  68545. end
  68546. def close; rewind; end
  68547. end
  68548. # Decompresses a gzipped string.
  68549. def self.decompress(source)
  68550. Zlib::GzipReader.new(StringIO.new(source)).read
  68551. end
  68552. # Compresses a string using gzip.
  68553. def self.compress(source, level=Zlib::DEFAULT_COMPRESSION, strategy=Zlib::DEFAULT_STRATEGY)
  68554. output = Stream.new
  68555. gz = Zlib::GzipWriter.new(output, level, strategy)
  68556. gz.write(source)
  68557. gz.close
  68558. output.string
  68559. end
  68560. end
  68561. end
  68562. require 'active_support/core_ext/hash/keys'
  68563. module ActiveSupport
  68564. # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
  68565. # to be the same.
  68566. #
  68567. # rgb = ActiveSupport::HashWithIndifferentAccess.new
  68568. #
  68569. # rgb[:black] = '#000000'
  68570. # rgb[:black] # => '#000000'
  68571. # rgb['black'] # => '#000000'
  68572. #
  68573. # rgb['white'] = '#FFFFFF'
  68574. # rgb[:white] # => '#FFFFFF'
  68575. # rgb['white'] # => '#FFFFFF'
  68576. #
  68577. # Internally symbols are mapped to strings when used as keys in the entire
  68578. # writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
  68579. # mapping belongs to the public interface. For example, given:
  68580. #
  68581. # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
  68582. #
  68583. # You are guaranteed that the key is returned as a string:
  68584. #
  68585. # hash.keys # => ["a"]
  68586. #
  68587. # Technically other types of keys are accepted:
  68588. #
  68589. # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
  68590. # hash[0] = 0
  68591. # hash # => {"a"=>1, 0=>0}
  68592. #
  68593. # but this class is intended for use cases where strings or symbols are the
  68594. # expected keys and it is convenient to understand both as the same. For
  68595. # example the +params+ hash in Ruby on Rails.
  68596. #
  68597. # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
  68598. #
  68599. # rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
  68600. #
  68601. # which may be handy.
  68602. class HashWithIndifferentAccess < Hash
  68603. # Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
  68604. # this class.
  68605. def extractable_options?
  68606. true
  68607. end
  68608. def with_indifferent_access
  68609. dup
  68610. end
  68611. def nested_under_indifferent_access
  68612. self
  68613. end
  68614. def initialize(constructor = {})
  68615. if constructor.is_a?(Hash)
  68616. super()
  68617. update(constructor)
  68618. else
  68619. super(constructor)
  68620. end
  68621. end
  68622. def default(key = nil)
  68623. if key.is_a?(Symbol) && include?(key = key.to_s)
  68624. self[key]
  68625. else
  68626. super
  68627. end
  68628. end
  68629. def self.new_from_hash_copying_default(hash)
  68630. new(hash).tap do |new_hash|
  68631. new_hash.default = hash.default
  68632. end
  68633. end
  68634. def self.[](*args)
  68635. new.merge(Hash[*args])
  68636. end
  68637. alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
  68638. alias_method :regular_update, :update unless method_defined?(:regular_update)
  68639. # Assigns a new value to the hash:
  68640. #
  68641. # hash = ActiveSupport::HashWithIndifferentAccess.new
  68642. # hash[:key] = 'value'
  68643. #
  68644. # This value can be later fetched using either +:key+ or +'key'+.
  68645. def []=(key, value)
  68646. regular_writer(convert_key(key), convert_value(value))
  68647. end
  68648. alias_method :store, :[]=
  68649. # Updates the receiver in-place, merging in the hash passed as argument:
  68650. #
  68651. # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
  68652. # hash_1[:key] = 'value'
  68653. #
  68654. # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
  68655. # hash_2[:key] = 'New Value!'
  68656. #
  68657. # hash_1.update(hash_2) # => {"key"=>"New Value!"}
  68658. #
  68659. # The argument can be either an
  68660. # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
  68661. # In either case the merge respects the semantics of indifferent access.
  68662. #
  68663. # If the argument is a regular hash with keys +:key+ and +"key"+ only one
  68664. # of the values end up in the receiver, but which one is unspecified.
  68665. #
  68666. # When given a block, the value for duplicated keys will be determined
  68667. # by the result of invoking the block with the duplicated key, the value
  68668. # in the receiver, and the value in +other_hash+. The rules for duplicated
  68669. # keys follow the semantics of indifferent access:
  68670. #
  68671. # hash_1[:key] = 10
  68672. # hash_2['key'] = 12
  68673. # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
  68674. def update(other_hash)
  68675. if other_hash.is_a? HashWithIndifferentAccess
  68676. super(other_hash)
  68677. else
  68678. other_hash.each_pair do |key, value|
  68679. if block_given? && key?(key)
  68680. value = yield(convert_key(key), self[key], value)
  68681. end
  68682. regular_writer(convert_key(key), convert_value(value))
  68683. end
  68684. self
  68685. end
  68686. end
  68687. alias_method :merge!, :update
  68688. # Checks the hash for a key matching the argument passed in:
  68689. #
  68690. # hash = ActiveSupport::HashWithIndifferentAccess.new
  68691. # hash['key'] = 'value'
  68692. # hash.key?(:key) # => true
  68693. # hash.key?('key') # => true
  68694. def key?(key)
  68695. super(convert_key(key))
  68696. end
  68697. alias_method :include?, :key?
  68698. alias_method :has_key?, :key?
  68699. alias_method :member?, :key?
  68700. # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
  68701. # either a string or a symbol:
  68702. #
  68703. # counters = ActiveSupport::HashWithIndifferentAccess.new
  68704. # counters[:foo] = 1
  68705. #
  68706. # counters.fetch('foo') # => 1
  68707. # counters.fetch(:bar, 0) # => 0
  68708. # counters.fetch(:bar) {|key| 0} # => 0
  68709. # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
  68710. def fetch(key, *extras)
  68711. super(convert_key(key), *extras)
  68712. end
  68713. # Returns an array of the values at the specified indices:
  68714. #
  68715. # hash = ActiveSupport::HashWithIndifferentAccess.new
  68716. # hash[:a] = 'x'
  68717. # hash[:b] = 'y'
  68718. # hash.values_at('a', 'b') # => ["x", "y"]
  68719. def values_at(*indices)
  68720. indices.collect {|key| self[convert_key(key)]}
  68721. end
  68722. # Returns an exact copy of the hash.
  68723. def dup
  68724. self.class.new(self).tap do |new_hash|
  68725. new_hash.default = default
  68726. end
  68727. end
  68728. # This method has the same semantics of +update+, except it does not
  68729. # modify the receiver but rather returns a new hash with indifferent
  68730. # access with the result of the merge.
  68731. def merge(hash, &block)
  68732. self.dup.update(hash, &block)
  68733. end
  68734. # Like +merge+ but the other way around: Merges the receiver into the
  68735. # argument and returns a new hash with indifferent access as result:
  68736. #
  68737. # hash = ActiveSupport::HashWithIndifferentAccess.new
  68738. # hash['a'] = nil
  68739. # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
  68740. def reverse_merge(other_hash)
  68741. super(self.class.new_from_hash_copying_default(other_hash))
  68742. end
  68743. # Same semantics as +reverse_merge+ but modifies the receiver in-place.
  68744. def reverse_merge!(other_hash)
  68745. replace(reverse_merge( other_hash ))
  68746. end
  68747. # Replaces the contents of this hash with other_hash.
  68748. #
  68749. # h = { "a" => 100, "b" => 200 }
  68750. # h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
  68751. def replace(other_hash)
  68752. super(self.class.new_from_hash_copying_default(other_hash))
  68753. end
  68754. # Removes the specified key from the hash.
  68755. def delete(key)
  68756. super(convert_key(key))
  68757. end
  68758. def stringify_keys!; self end
  68759. def deep_stringify_keys!; self end
  68760. def stringify_keys; dup end
  68761. def deep_stringify_keys; dup end
  68762. undef :symbolize_keys!
  68763. undef :deep_symbolize_keys!
  68764. def symbolize_keys; to_hash.symbolize_keys end
  68765. def deep_symbolize_keys; to_hash.deep_symbolize_keys end
  68766. def to_options!; self end
  68767. # Convert to a regular hash with string keys.
  68768. def to_hash
  68769. Hash.new(default).merge!(self)
  68770. end
  68771. protected
  68772. def convert_key(key)
  68773. key.kind_of?(Symbol) ? key.to_s : key
  68774. end
  68775. def convert_value(value)
  68776. if value.is_a? Hash
  68777. value.nested_under_indifferent_access
  68778. elsif value.is_a?(Array)
  68779. value = value.dup if value.frozen?
  68780. value.map! { |e| convert_value(e) }
  68781. else
  68782. value
  68783. end
  68784. end
  68785. end
  68786. end
  68787. HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
  68788. begin
  68789. require 'active_support/core_ext/hash/deep_merge'
  68790. require 'active_support/core_ext/hash/except'
  68791. require 'active_support/core_ext/hash/slice'
  68792. require 'i18n'
  68793. require 'active_support/lazy_load_hooks'
  68794. rescue LoadError => e
  68795. $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
  68796. raise e
  68797. end
  68798. ActiveSupport.run_load_hooks(:i18n)
  68799. I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
  68800. require "active_support"
  68801. require "active_support/file_update_checker"
  68802. require "active_support/core_ext/array/wrap"
  68803. module I18n
  68804. class Railtie < Rails::Railtie
  68805. config.i18n = ActiveSupport::OrderedOptions.new
  68806. config.i18n.railties_load_path = []
  68807. config.i18n.load_path = []
  68808. config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
  68809. # Set the i18n configuration after initialization since a lot of
  68810. # configuration is still usually done in application initializers.
  68811. config.after_initialize do |app|
  68812. I18n::Railtie.initialize_i18n(app)
  68813. end
  68814. # Trigger i18n config before any eager loading has happened
  68815. # so it's ready if any classes require it when eager loaded.
  68816. config.before_eager_load do |app|
  68817. I18n::Railtie.initialize_i18n(app)
  68818. end
  68819. protected
  68820. @i18n_inited = false
  68821. # Setup i18n configuration.
  68822. def self.initialize_i18n(app)
  68823. return if @i18n_inited
  68824. fallbacks = app.config.i18n.delete(:fallbacks)
  68825. app.config.i18n.each do |setting, value|
  68826. case setting
  68827. when :railties_load_path
  68828. app.config.i18n.load_path.unshift(*value)
  68829. when :load_path
  68830. I18n.load_path += value
  68831. else
  68832. I18n.send("#{setting}=", value)
  68833. end
  68834. end
  68835. init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
  68836. reloader = ActiveSupport::FileUpdateChecker.new(I18n.load_path.dup){ I18n.reload! }
  68837. app.reloaders << reloader
  68838. ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
  68839. reloader.execute
  68840. @i18n_inited = true
  68841. end
  68842. def self.include_fallbacks_module
  68843. I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
  68844. end
  68845. def self.init_fallbacks(fallbacks)
  68846. include_fallbacks_module
  68847. args = case fallbacks
  68848. when ActiveSupport::OrderedOptions
  68849. [*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
  68850. when Hash, Array
  68851. Array.wrap(fallbacks)
  68852. else # TrueClass
  68853. []
  68854. end
  68855. I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
  68856. end
  68857. def self.validate_fallbacks(fallbacks)
  68858. case fallbacks
  68859. when ActiveSupport::OrderedOptions
  68860. !fallbacks.empty?
  68861. when TrueClass, Array, Hash
  68862. true
  68863. else
  68864. raise "Unexpected fallback type #{fallbacks.inspect}"
  68865. end
  68866. end
  68867. end
  68868. end
  68869. require 'active_support/inflector/inflections'
  68870. module ActiveSupport
  68871. Inflector.inflections(:en) do |inflect|
  68872. inflect.plural(/$/, 's')
  68873. inflect.plural(/s$/i, 's')
  68874. inflect.plural(/^(ax|test)is$/i, '\1es')
  68875. inflect.plural(/(octop|vir)us$/i, '\1i')
  68876. inflect.plural(/(octop|vir)i$/i, '\1i')
  68877. inflect.plural(/(alias|status)$/i, '\1es')
  68878. inflect.plural(/(bu)s$/i, '\1ses')
  68879. inflect.plural(/(buffal|tomat)o$/i, '\1oes')
  68880. inflect.plural(/([ti])um$/i, '\1a')
  68881. inflect.plural(/([ti])a$/i, '\1a')
  68882. inflect.plural(/sis$/i, 'ses')
  68883. inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
  68884. inflect.plural(/(hive)$/i, '\1s')
  68885. inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
  68886. inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
  68887. inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
  68888. inflect.plural(/^(m|l)ouse$/i, '\1ice')
  68889. inflect.plural(/^(m|l)ice$/i, '\1ice')
  68890. inflect.plural(/^(ox)$/i, '\1en')
  68891. inflect.plural(/^(oxen)$/i, '\1')
  68892. inflect.plural(/(quiz)$/i, '\1zes')
  68893. inflect.singular(/s$/i, '')
  68894. inflect.singular(/(ss)$/i, '\1')
  68895. inflect.singular(/(n)ews$/i, '\1ews')
  68896. inflect.singular(/([ti])a$/i, '\1um')
  68897. inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
  68898. inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
  68899. inflect.singular(/([^f])ves$/i, '\1fe')
  68900. inflect.singular(/(hive)s$/i, '\1')
  68901. inflect.singular(/(tive)s$/i, '\1')
  68902. inflect.singular(/([lr])ves$/i, '\1f')
  68903. inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
  68904. inflect.singular(/(s)eries$/i, '\1eries')
  68905. inflect.singular(/(m)ovies$/i, '\1ovie')
  68906. inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
  68907. inflect.singular(/^(m|l)ice$/i, '\1ouse')
  68908. inflect.singular(/(bus)(es)?$/i, '\1')
  68909. inflect.singular(/(o)es$/i, '\1')
  68910. inflect.singular(/(shoe)s$/i, '\1')
  68911. inflect.singular(/(cris|test)(is|es)$/i, '\1is')
  68912. inflect.singular(/^(a)x[ie]s$/i, '\1xis')
  68913. inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
  68914. inflect.singular(/(alias|status)(es)?$/i, '\1')
  68915. inflect.singular(/^(ox)en/i, '\1')
  68916. inflect.singular(/(vert|ind)ices$/i, '\1ex')
  68917. inflect.singular(/(matr)ices$/i, '\1ix')
  68918. inflect.singular(/(quiz)zes$/i, '\1')
  68919. inflect.singular(/(database)s$/i, '\1')
  68920. inflect.irregular('person', 'people')
  68921. inflect.irregular('man', 'men')
  68922. inflect.irregular('child', 'children')
  68923. inflect.irregular('sex', 'sexes')
  68924. inflect.irregular('move', 'moves')
  68925. inflect.irregular('cow', 'kine')
  68926. inflect.irregular('zombie', 'zombies')
  68927. inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
  68928. end
  68929. end
  68930. require 'thread_safe'
  68931. require 'active_support/core_ext/array/prepend_and_append'
  68932. require 'active_support/i18n'
  68933. module ActiveSupport
  68934. module Inflector
  68935. extend self
  68936. # A singleton instance of this class is yielded by Inflector.inflections,
  68937. # which can then be used to specify additional inflection rules. If passed
  68938. # an optional locale, rules for other languages can be specified. The
  68939. # default locale is <tt>:en</tt>. Only rules for English are provided.
  68940. #
  68941. # ActiveSupport::Inflector.inflections(:en) do |inflect|
  68942. # inflect.plural /^(ox)$/i, '\1\2en'
  68943. # inflect.singular /^(ox)en/i, '\1'
  68944. #
  68945. # inflect.irregular 'octopus', 'octopi'
  68946. #
  68947. # inflect.uncountable 'equipment'
  68948. # end
  68949. #
  68950. # New rules are added at the top. So in the example above, the irregular
  68951. # rule for octopus will now be the first of the pluralization and
  68952. # singularization rules that is runs. This guarantees that your rules run
  68953. # before any of the rules that may already have been loaded.
  68954. class Inflections
  68955. @__instance__ = ThreadSafe::Cache.new
  68956. def self.instance(locale = :en)
  68957. @__instance__[locale] ||= new
  68958. end
  68959. attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
  68960. def initialize
  68961. @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
  68962. end
  68963. # Private, for the test suite.
  68964. def initialize_dup(orig) # :nodoc:
  68965. %w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope|
  68966. instance_variable_set("@#{scope}", orig.send(scope).dup)
  68967. end
  68968. end
  68969. # Specifies a new acronym. An acronym must be specified as it will appear
  68970. # in a camelized string. An underscore string that contains the acronym
  68971. # will retain the acronym when passed to +camelize+, +humanize+, or
  68972. # +titleize+. A camelized string that contains the acronym will maintain
  68973. # the acronym when titleized or humanized, and will convert the acronym
  68974. # into a non-delimited single lowercase word when passed to +underscore+.
  68975. #
  68976. # acronym 'HTML'
  68977. # titleize 'html' #=> 'HTML'
  68978. # camelize 'html' #=> 'HTML'
  68979. # underscore 'MyHTML' #=> 'my_html'
  68980. #
  68981. # The acronym, however, must occur as a delimited unit and not be part of
  68982. # another word for conversions to recognize it:
  68983. #
  68984. # acronym 'HTTP'
  68985. # camelize 'my_http_delimited' #=> 'MyHTTPDelimited'
  68986. # camelize 'https' #=> 'Https', not 'HTTPs'
  68987. # underscore 'HTTPS' #=> 'http_s', not 'https'
  68988. #
  68989. # acronym 'HTTPS'
  68990. # camelize 'https' #=> 'HTTPS'
  68991. # underscore 'HTTPS' #=> 'https'
  68992. #
  68993. # Note: Acronyms that are passed to +pluralize+ will no longer be
  68994. # recognized, since the acronym will not occur as a delimited unit in the
  68995. # pluralized result. To work around this, you must specify the pluralized
  68996. # form as an acronym as well:
  68997. #
  68998. # acronym 'API'
  68999. # camelize(pluralize('api')) #=> 'Apis'
  69000. #
  69001. # acronym 'APIs'
  69002. # camelize(pluralize('api')) #=> 'APIs'
  69003. #
  69004. # +acronym+ may be used to specify any word that contains an acronym or
  69005. # otherwise needs to maintain a non-standard capitalization. The only
  69006. # restriction is that the word must begin with a capital letter.
  69007. #
  69008. # acronym 'RESTful'
  69009. # underscore 'RESTful' #=> 'restful'
  69010. # underscore 'RESTfulController' #=> 'restful_controller'
  69011. # titleize 'RESTfulController' #=> 'RESTful Controller'
  69012. # camelize 'restful' #=> 'RESTful'
  69013. # camelize 'restful_controller' #=> 'RESTfulController'
  69014. #
  69015. # acronym 'McDonald'
  69016. # underscore 'McDonald' #=> 'mcdonald'
  69017. # camelize 'mcdonald' #=> 'McDonald'
  69018. def acronym(word)
  69019. @acronyms[word.downcase] = word
  69020. @acronym_regex = /#{@acronyms.values.join("|")}/
  69021. end
  69022. # Specifies a new pluralization rule and its replacement. The rule can
  69023. # either be a string or a regular expression. The replacement should
  69024. # always be a string that may include references to the matched data from
  69025. # the rule.
  69026. def plural(rule, replacement)
  69027. @uncountables.delete(rule) if rule.is_a?(String)
  69028. @uncountables.delete(replacement)
  69029. @plurals.prepend([rule, replacement])
  69030. end
  69031. # Specifies a new singularization rule and its replacement. The rule can
  69032. # either be a string or a regular expression. The replacement should
  69033. # always be a string that may include references to the matched data from
  69034. # the rule.
  69035. def singular(rule, replacement)
  69036. @uncountables.delete(rule) if rule.is_a?(String)
  69037. @uncountables.delete(replacement)
  69038. @singulars.prepend([rule, replacement])
  69039. end
  69040. # Specifies a new irregular that applies to both pluralization and
  69041. # singularization at the same time. This can only be used for strings, not
  69042. # regular expressions. You simply pass the irregular in singular and
  69043. # plural form.
  69044. #
  69045. # irregular 'octopus', 'octopi'
  69046. # irregular 'person', 'people'
  69047. def irregular(singular, plural)
  69048. @uncountables.delete(singular)
  69049. @uncountables.delete(plural)
  69050. s0 = singular[0]
  69051. srest = singular[1..-1]
  69052. p0 = plural[0]
  69053. prest = plural[1..-1]
  69054. if s0.upcase == p0.upcase
  69055. plural(/(#{s0})#{srest}$/i, '\1' + prest)
  69056. plural(/(#{p0})#{prest}$/i, '\1' + prest)
  69057. singular(/(#{s0})#{srest}$/i, '\1' + srest)
  69058. singular(/(#{p0})#{prest}$/i, '\1' + srest)
  69059. else
  69060. plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
  69061. plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
  69062. plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
  69063. plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
  69064. singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
  69065. singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
  69066. singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
  69067. singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
  69068. end
  69069. end
  69070. # Add uncountable words that shouldn't be attempted inflected.
  69071. #
  69072. # uncountable 'money'
  69073. # uncountable 'money', 'information'
  69074. # uncountable %w( money information rice )
  69075. def uncountable(*words)
  69076. (@uncountables << words).flatten!
  69077. end
  69078. # Specifies a humanized form of a string by a regular expression rule or
  69079. # by a string mapping. When using a regular expression based replacement,
  69080. # the normal humanize formatting is called after the replacement. When a
  69081. # string is used, the human form should be specified as desired (example:
  69082. # 'The name', not 'the_name').
  69083. #
  69084. # human /_cnt$/i, '\1_count'
  69085. # human 'legacy_col_person_name', 'Name'
  69086. def human(rule, replacement)
  69087. @humans.prepend([rule, replacement])
  69088. end
  69089. # Clears the loaded inflections within a given scope (default is
  69090. # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
  69091. # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
  69092. # <tt>:humans</tt>.
  69093. #
  69094. # clear :all
  69095. # clear :plurals
  69096. def clear(scope = :all)
  69097. case scope
  69098. when :all
  69099. @plurals, @singulars, @uncountables, @humans = [], [], [], []
  69100. else
  69101. instance_variable_set "@#{scope}", []
  69102. end
  69103. end
  69104. end
  69105. # Yields a singleton instance of Inflector::Inflections so you can specify
  69106. # additional inflector rules. If passed an optional locale, rules for other
  69107. # languages can be specified. If not specified, defaults to <tt>:en</tt>.
  69108. # Only rules for English are provided.
  69109. #
  69110. # ActiveSupport::Inflector.inflections(:en) do |inflect|
  69111. # inflect.uncountable 'rails'
  69112. # end
  69113. def inflections(locale = :en)
  69114. if block_given?
  69115. yield Inflections.instance(locale)
  69116. else
  69117. Inflections.instance(locale)
  69118. end
  69119. end
  69120. end
  69121. end
  69122. # encoding: utf-8
  69123. require 'active_support/inflector/inflections'
  69124. require 'active_support/inflections'
  69125. module ActiveSupport
  69126. # The Inflector transforms words from singular to plural, class names to table
  69127. # names, modularized class names to ones without, and class names to foreign
  69128. # keys. The default inflections for pluralization, singularization, and
  69129. # uncountable words are kept in inflections.rb.
  69130. #
  69131. # The Rails core team has stated patches for the inflections library will not
  69132. # be accepted in order to avoid breaking legacy applications which may be
  69133. # relying on errant inflections. If you discover an incorrect inflection and
  69134. # require it for your application or wish to define rules for languages other
  69135. # than English, please correct or add them yourself (explained below).
  69136. module Inflector
  69137. extend self
  69138. # Returns the plural form of the word in the string.
  69139. #
  69140. # If passed an optional +locale+ parameter, the word will be
  69141. # pluralized using rules defined for that language. By default,
  69142. # this parameter is set to <tt>:en</tt>.
  69143. #
  69144. # 'post'.pluralize # => "posts"
  69145. # 'octopus'.pluralize # => "octopi"
  69146. # 'sheep'.pluralize # => "sheep"
  69147. # 'words'.pluralize # => "words"
  69148. # 'CamelOctopus'.pluralize # => "CamelOctopi"
  69149. # 'ley'.pluralize(:es) # => "leyes"
  69150. def pluralize(word, locale = :en)
  69151. apply_inflections(word, inflections(locale).plurals)
  69152. end
  69153. # The reverse of +pluralize+, returns the singular form of a word in a
  69154. # string.
  69155. #
  69156. # If passed an optional +locale+ parameter, the word will be
  69157. # pluralized using rules defined for that language. By default,
  69158. # this parameter is set to <tt>:en</tt>.
  69159. #
  69160. # 'posts'.singularize # => "post"
  69161. # 'octopi'.singularize # => "octopus"
  69162. # 'sheep'.singularize # => "sheep"
  69163. # 'word'.singularize # => "word"
  69164. # 'CamelOctopi'.singularize # => "CamelOctopus"
  69165. # 'leyes'.singularize(:es) # => "ley"
  69166. def singularize(word, locale = :en)
  69167. apply_inflections(word, inflections(locale).singulars)
  69168. end
  69169. # By default, +camelize+ converts strings to UpperCamelCase. If the argument
  69170. # to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
  69171. # lowerCamelCase.
  69172. #
  69173. # +camelize+ will also convert '/' to '::' which is useful for converting
  69174. # paths to namespaces.
  69175. #
  69176. # 'active_model'.camelize # => "ActiveModel"
  69177. # 'active_model'.camelize(:lower) # => "activeModel"
  69178. # 'active_model/errors'.camelize # => "ActiveModel::Errors"
  69179. # 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
  69180. #
  69181. # As a rule of thumb you can think of +camelize+ as the inverse of
  69182. # +underscore+, though there are cases where that does not hold:
  69183. #
  69184. # 'SSLError'.underscore.camelize # => "SslError"
  69185. def camelize(term, uppercase_first_letter = true)
  69186. string = term.to_s
  69187. if uppercase_first_letter
  69188. string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
  69189. else
  69190. string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
  69191. end
  69192. string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
  69193. end
  69194. # Makes an underscored, lowercase form from the expression in the string.
  69195. #
  69196. # Changes '::' to '/' to convert namespaces to paths.
  69197. #
  69198. # 'ActiveModel'.underscore # => "active_model"
  69199. # 'ActiveModel::Errors'.underscore # => "active_model/errors"
  69200. #
  69201. # As a rule of thumb you can think of +underscore+ as the inverse of
  69202. # +camelize+, though there are cases where that does not hold:
  69203. #
  69204. # 'SSLError'.underscore.camelize # => "SslError"
  69205. def underscore(camel_cased_word)
  69206. word = camel_cased_word.to_s.dup
  69207. word.gsub!('::', '/')
  69208. word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
  69209. word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
  69210. word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
  69211. word.tr!("-", "_")
  69212. word.downcase!
  69213. word
  69214. end
  69215. # Capitalizes the first word and turns underscores into spaces and strips a
  69216. # trailing "_id", if any. Like +titleize+, this is meant for creating pretty
  69217. # output.
  69218. #
  69219. # 'employee_salary'.humanize # => "Employee salary"
  69220. # 'author_id'.humanize # => "Author"
  69221. def humanize(lower_case_and_underscored_word)
  69222. result = lower_case_and_underscored_word.to_s.dup
  69223. inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
  69224. result.gsub!(/_id$/, "")
  69225. result.tr!('_', ' ')
  69226. result.gsub(/([a-z\d]*)/i) { |match|
  69227. "#{inflections.acronyms[match] || match.downcase}"
  69228. }.gsub(/^\w/) { $&.upcase }
  69229. end
  69230. # Capitalizes all the words and replaces some characters in the string to
  69231. # create a nicer looking title. +titleize+ is meant for creating pretty
  69232. # output. It is not used in the Rails internals.
  69233. #
  69234. # +titleize+ is also aliased as +titlecase+.
  69235. #
  69236. # 'man from the boondocks'.titleize # => "Man From The Boondocks"
  69237. # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
  69238. # 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
  69239. # 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
  69240. def titleize(word)
  69241. humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
  69242. end
  69243. # Create the name of a table like Rails does for models to table names. This
  69244. # method uses the +pluralize+ method on the last word in the string.
  69245. #
  69246. # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
  69247. # 'egg_and_ham'.tableize # => "egg_and_hams"
  69248. # 'fancyCategory'.tableize # => "fancy_categories"
  69249. def tableize(class_name)
  69250. pluralize(underscore(class_name))
  69251. end
  69252. # Create a class name from a plural table name like Rails does for table
  69253. # names to models. Note that this returns a string and not a Class (To
  69254. # convert to an actual class follow +classify+ with +constantize+).
  69255. #
  69256. # 'egg_and_hams'.classify # => "EggAndHam"
  69257. # 'posts'.classify # => "Post"
  69258. #
  69259. # Singular names are not handled correctly:
  69260. #
  69261. # 'business'.classify # => "Busines"
  69262. def classify(table_name)
  69263. # strip out any leading schema name
  69264. camelize(singularize(table_name.to_s.sub(/.*\./, '')))
  69265. end
  69266. # Replaces underscores with dashes in the string.
  69267. #
  69268. # 'puni_puni'.dasherize # => "puni-puni"
  69269. def dasherize(underscored_word)
  69270. underscored_word.tr('_', '-')
  69271. end
  69272. # Removes the module part from the expression in the string.
  69273. #
  69274. # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
  69275. # 'Inflections'.demodulize # => "Inflections"
  69276. #
  69277. # See also +deconstantize+.
  69278. def demodulize(path)
  69279. path = path.to_s
  69280. if i = path.rindex('::')
  69281. path[(i+2)..-1]
  69282. else
  69283. path
  69284. end
  69285. end
  69286. # Removes the rightmost segment from the constant expression in the string.
  69287. #
  69288. # 'Net::HTTP'.deconstantize # => "Net"
  69289. # '::Net::HTTP'.deconstantize # => "::Net"
  69290. # 'String'.deconstantize # => ""
  69291. # '::String'.deconstantize # => ""
  69292. # ''.deconstantize # => ""
  69293. #
  69294. # See also +demodulize+.
  69295. def deconstantize(path)
  69296. path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
  69297. end
  69298. # Creates a foreign key name from a class name.
  69299. # +separate_class_name_and_id_with_underscore+ sets whether
  69300. # the method should put '_' between the name and 'id'.
  69301. #
  69302. # 'Message'.foreign_key # => "message_id"
  69303. # 'Message'.foreign_key(false) # => "messageid"
  69304. # 'Admin::Post'.foreign_key # => "post_id"
  69305. def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
  69306. underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
  69307. end
  69308. # Tries to find a constant with the name specified in the argument string.
  69309. #
  69310. # 'Module'.constantize # => Module
  69311. # 'Test::Unit'.constantize # => Test::Unit
  69312. #
  69313. # The name is assumed to be the one of a top-level constant, no matter
  69314. # whether it starts with "::" or not. No lexical context is taken into
  69315. # account:
  69316. #
  69317. # C = 'outside'
  69318. # module M
  69319. # C = 'inside'
  69320. # C # => 'inside'
  69321. # 'C'.constantize # => 'outside', same as ::C
  69322. # end
  69323. #
  69324. # NameError is raised when the name is not in CamelCase or the constant is
  69325. # unknown.
  69326. def constantize(camel_cased_word)
  69327. names = camel_cased_word.split('::')
  69328. names.shift if names.empty? || names.first.empty?
  69329. names.inject(Object) do |constant, name|
  69330. if constant == Object
  69331. constant.const_get(name)
  69332. else
  69333. candidate = constant.const_get(name)
  69334. next candidate if constant.const_defined?(name, false)
  69335. next candidate unless Object.const_defined?(name)
  69336. # Go down the ancestors to check it it's owned
  69337. # directly before we reach Object or the end of ancestors.
  69338. constant = constant.ancestors.inject do |const, ancestor|
  69339. break const if ancestor == Object
  69340. break ancestor if ancestor.const_defined?(name, false)
  69341. const
  69342. end
  69343. # owner is in Object, so raise
  69344. constant.const_get(name, false)
  69345. end
  69346. end
  69347. end
  69348. # Tries to find a constant with the name specified in the argument string.
  69349. #
  69350. # 'Module'.safe_constantize # => Module
  69351. # 'Test::Unit'.safe_constantize # => Test::Unit
  69352. #
  69353. # The name is assumed to be the one of a top-level constant, no matter
  69354. # whether it starts with "::" or not. No lexical context is taken into
  69355. # account:
  69356. #
  69357. # C = 'outside'
  69358. # module M
  69359. # C = 'inside'
  69360. # C # => 'inside'
  69361. # 'C'.safe_constantize # => 'outside', same as ::C
  69362. # end
  69363. #
  69364. # +nil+ is returned when the name is not in CamelCase or the constant (or
  69365. # part of it) is unknown.
  69366. #
  69367. # 'blargle'.safe_constantize # => nil
  69368. # 'UnknownModule'.safe_constantize # => nil
  69369. # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
  69370. def safe_constantize(camel_cased_word)
  69371. constantize(camel_cased_word)
  69372. rescue NameError => e
  69373. raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
  69374. e.name.to_s == camel_cased_word.to_s
  69375. rescue ArgumentError => e
  69376. raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
  69377. end
  69378. # Returns the suffix that should be added to a number to denote the position
  69379. # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
  69380. #
  69381. # ordinal(1) # => "st"
  69382. # ordinal(2) # => "nd"
  69383. # ordinal(1002) # => "nd"
  69384. # ordinal(1003) # => "rd"
  69385. # ordinal(-11) # => "th"
  69386. # ordinal(-1021) # => "st"
  69387. def ordinal(number)
  69388. abs_number = number.to_i.abs
  69389. if (11..13).include?(abs_number % 100)
  69390. "th"
  69391. else
  69392. case abs_number % 10
  69393. when 1; "st"
  69394. when 2; "nd"
  69395. when 3; "rd"
  69396. else "th"
  69397. end
  69398. end
  69399. end
  69400. # Turns a number into an ordinal string used to denote the position in an
  69401. # ordered sequence such as 1st, 2nd, 3rd, 4th.
  69402. #
  69403. # ordinalize(1) # => "1st"
  69404. # ordinalize(2) # => "2nd"
  69405. # ordinalize(1002) # => "1002nd"
  69406. # ordinalize(1003) # => "1003rd"
  69407. # ordinalize(-11) # => "-11th"
  69408. # ordinalize(-1021) # => "-1021st"
  69409. def ordinalize(number)
  69410. "#{number}#{ordinal(number)}"
  69411. end
  69412. private
  69413. # Mount a regular expression that will match part by part of the constant.
  69414. # For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
  69415. def const_regexp(camel_cased_word) #:nodoc:
  69416. parts = camel_cased_word.split("::")
  69417. last = parts.pop
  69418. parts.reverse.inject(last) do |acc, part|
  69419. part.empty? ? acc : "#{part}(::#{acc})?"
  69420. end
  69421. end
  69422. # Applies inflection rules for +singularize+ and +pluralize+.
  69423. #
  69424. # apply_inflections('post', inflections.plurals) # => "posts"
  69425. # apply_inflections('posts', inflections.singulars) # => "post"
  69426. def apply_inflections(word, rules)
  69427. result = word.to_s.dup
  69428. if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
  69429. result
  69430. else
  69431. rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
  69432. result
  69433. end
  69434. end
  69435. end
  69436. end
  69437. # encoding: utf-8
  69438. require 'active_support/core_ext/string/multibyte'
  69439. require 'active_support/i18n'
  69440. module ActiveSupport
  69441. module Inflector
  69442. # Replaces non-ASCII characters with an ASCII approximation, or if none
  69443. # exists, a replacement character which defaults to "?".
  69444. #
  69445. # transliterate('Ærøskøbing')
  69446. # # => "AEroskobing"
  69447. #
  69448. # Default approximations are provided for Western/Latin characters,
  69449. # e.g, "ø", "ñ", "é", "ß", etc.
  69450. #
  69451. # This method is I18n aware, so you can set up custom approximations for a
  69452. # locale. This can be useful, for example, to transliterate German's "ü"
  69453. # and "ö" to "ue" and "oe", or to add support for transliterating Russian
  69454. # to ASCII.
  69455. #
  69456. # In order to make your custom transliterations available, you must set
  69457. # them as the <tt>i18n.transliterate.rule</tt> i18n key:
  69458. #
  69459. # # Store the transliterations in locales/de.yml
  69460. # i18n:
  69461. # transliterate:
  69462. # rule:
  69463. # ü: "ue"
  69464. # ö: "oe"
  69465. #
  69466. # # Or set them using Ruby
  69467. # I18n.backend.store_translations(:de, i18n: {
  69468. # transliterate: {
  69469. # rule: {
  69470. # 'ü' => 'ue',
  69471. # 'ö' => 'oe'
  69472. # }
  69473. # }
  69474. # })
  69475. #
  69476. # The value for <tt>i18n.transliterate.rule</tt> can be a simple Hash that
  69477. # maps characters to ASCII approximations as shown above, or, for more
  69478. # complex requirements, a Proc:
  69479. #
  69480. # I18n.backend.store_translations(:de, i18n: {
  69481. # transliterate: {
  69482. # rule: ->(string) { MyTransliterator.transliterate(string) }
  69483. # }
  69484. # })
  69485. #
  69486. # Now you can have different transliterations for each locale:
  69487. #
  69488. # I18n.locale = :en
  69489. # transliterate('Jürgen')
  69490. # # => "Jurgen"
  69491. #
  69492. # I18n.locale = :de
  69493. # transliterate('Jürgen')
  69494. # # => "Juergen"
  69495. def transliterate(string, replacement = "?")
  69496. I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
  69497. ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
  69498. :replacement => replacement)
  69499. end
  69500. # Replaces special characters in a string so that it may be used as part of
  69501. # a 'pretty' URL.
  69502. #
  69503. # class Person
  69504. # def to_param
  69505. # "#{id}-#{name.parameterize}"
  69506. # end
  69507. # end
  69508. #
  69509. # @person = Person.find(1)
  69510. # # => #<Person id: 1, name: "Donald E. Knuth">
  69511. #
  69512. # <%= link_to(@person.name, person_path(@person)) %>
  69513. # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
  69514. def parameterize(string, sep = '-')
  69515. # replace accented chars with their ascii equivalents
  69516. parameterized_string = transliterate(string)
  69517. # Turn unwanted chars into the separator
  69518. parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep)
  69519. unless sep.nil? || sep.empty?
  69520. re_sep = Regexp.escape(sep)
  69521. # No more than one of the separator in a row.
  69522. parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
  69523. # Remove leading/trailing separator.
  69524. parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
  69525. end
  69526. parameterized_string.downcase
  69527. end
  69528. end
  69529. end
  69530. # in case active_support/inflector is required without the rest of active_support
  69531. require 'active_support/inflector/inflections'
  69532. require 'active_support/inflector/transliterate'
  69533. require 'active_support/inflector/methods'
  69534. require 'active_support/inflections'
  69535. require 'active_support/core_ext/string/inflections'
  69536. require 'active_support/core_ext/module/attribute_accessors'
  69537. require 'active_support/core_ext/module/delegation'
  69538. require 'multi_json'
  69539. module ActiveSupport
  69540. # Look for and parse json strings that look like ISO 8601 times.
  69541. mattr_accessor :parse_json_times
  69542. module JSON
  69543. class << self
  69544. # Parses a JSON string (JavaScript Object Notation) into a hash.
  69545. # See www.json.org for more info.
  69546. #
  69547. # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
  69548. # => {"team" => "rails", "players" => "36"}
  69549. def decode(json, options ={})
  69550. data = MultiJson.load(json, options)
  69551. if ActiveSupport.parse_json_times
  69552. convert_dates_from(data)
  69553. else
  69554. data
  69555. end
  69556. end
  69557. def engine
  69558. MultiJson.adapter
  69559. end
  69560. alias :backend :engine
  69561. def engine=(name)
  69562. MultiJson.use(name)
  69563. end
  69564. alias :backend= :engine=
  69565. def with_backend(name)
  69566. old_backend, self.backend = backend, name
  69567. yield
  69568. ensure
  69569. self.backend = old_backend
  69570. end
  69571. # Returns the class of the error that will be raised when there is an
  69572. # error in decoding JSON. Using this method means you won't directly
  69573. # depend on the ActiveSupport's JSON implementation, in case it changes
  69574. # in the future.
  69575. #
  69576. # begin
  69577. # obj = ActiveSupport::JSON.decode(some_string)
  69578. # rescue ActiveSupport::JSON.parse_error
  69579. # Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}")
  69580. # end
  69581. def parse_error
  69582. MultiJson::DecodeError
  69583. end
  69584. private
  69585. def convert_dates_from(data)
  69586. case data
  69587. when nil
  69588. nil
  69589. when DATE_REGEX
  69590. begin
  69591. DateTime.parse(data)
  69592. rescue ArgumentError
  69593. data
  69594. end
  69595. when Array
  69596. data.map! { |d| convert_dates_from(d) }
  69597. when Hash
  69598. data.each do |key, value|
  69599. data[key] = convert_dates_from(value)
  69600. end
  69601. else
  69602. data
  69603. end
  69604. end
  69605. end
  69606. end
  69607. end
  69608. require 'active_support/core_ext/object/to_json'
  69609. require 'active_support/core_ext/module/delegation'
  69610. require 'active_support/json/variable'
  69611. require 'bigdecimal'
  69612. require 'active_support/core_ext/big_decimal/conversions' # for #to_s
  69613. require 'active_support/core_ext/hash/except'
  69614. require 'active_support/core_ext/hash/slice'
  69615. require 'active_support/core_ext/object/instance_variables'
  69616. require 'time'
  69617. require 'active_support/core_ext/time/conversions'
  69618. require 'active_support/core_ext/date_time/conversions'
  69619. require 'active_support/core_ext/date/conversions'
  69620. require 'set'
  69621. module ActiveSupport
  69622. class << self
  69623. delegate :use_standard_json_time_format, :use_standard_json_time_format=,
  69624. :escape_html_entities_in_json, :escape_html_entities_in_json=,
  69625. :encode_big_decimal_as_string, :encode_big_decimal_as_string=,
  69626. :to => :'ActiveSupport::JSON::Encoding'
  69627. end
  69628. module JSON
  69629. # matches YAML-formatted dates
  69630. DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
  69631. # Dumps objects in JSON (JavaScript Object Notation).
  69632. # See www.json.org for more info.
  69633. #
  69634. # ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
  69635. # # => "{\"team\":\"rails\",\"players\":\"36\"}"
  69636. def self.encode(value, options = nil)
  69637. Encoding::Encoder.new(options).encode(value)
  69638. end
  69639. module Encoding #:nodoc:
  69640. class CircularReferenceError < StandardError; end
  69641. class Encoder
  69642. attr_reader :options
  69643. def initialize(options = nil)
  69644. @options = options || {}
  69645. @seen = Set.new
  69646. end
  69647. def encode(value, use_options = true)
  69648. check_for_circular_references(value) do
  69649. jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
  69650. jsonified.encode_json(self)
  69651. end
  69652. end
  69653. # like encode, but only calls as_json, without encoding to string.
  69654. def as_json(value, use_options = true)
  69655. check_for_circular_references(value) do
  69656. use_options ? value.as_json(options_for(value)) : value.as_json
  69657. end
  69658. end
  69659. def options_for(value)
  69660. if value.is_a?(Array) || value.is_a?(Hash)
  69661. # hashes and arrays need to get encoder in the options, so that
  69662. # they can detect circular references.
  69663. options.merge(:encoder => self)
  69664. else
  69665. options.dup
  69666. end
  69667. end
  69668. def escape(string)
  69669. Encoding.escape(string)
  69670. end
  69671. private
  69672. def check_for_circular_references(value)
  69673. unless @seen.add?(value.__id__)
  69674. raise CircularReferenceError, 'object references itself'
  69675. end
  69676. yield
  69677. ensure
  69678. @seen.delete(value.__id__)
  69679. end
  69680. end
  69681. ESCAPED_CHARS = {
  69682. "\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002',
  69683. "\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005',
  69684. "\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B',
  69685. "\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010',
  69686. "\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013',
  69687. "\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016',
  69688. "\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019',
  69689. "\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C',
  69690. "\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F',
  69691. "\010" => '\b',
  69692. "\f" => '\f',
  69693. "\n" => '\n',
  69694. "\r" => '\r',
  69695. "\t" => '\t',
  69696. '"' => '\"',
  69697. '\\' => '\\\\',
  69698. '>' => '\u003E',
  69699. '<' => '\u003C',
  69700. '&' => '\u0026' }
  69701. class << self
  69702. # If true, use ISO 8601 format for dates and times. Otherwise, fall back
  69703. # to the Active Support legacy format.
  69704. attr_accessor :use_standard_json_time_format
  69705. # If false, serializes BigDecimal objects as numeric instead of wrapping
  69706. # them in a string.
  69707. attr_accessor :encode_big_decimal_as_string
  69708. attr_accessor :escape_regex
  69709. attr_reader :escape_html_entities_in_json
  69710. def escape_html_entities_in_json=(value)
  69711. self.escape_regex = \
  69712. if @escape_html_entities_in_json = value
  69713. /[\x00-\x1F"\\><&]/
  69714. else
  69715. /[\x00-\x1F"\\]/
  69716. end
  69717. end
  69718. def escape(string)
  69719. string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
  69720. json = string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] }
  69721. json = %("#{json}")
  69722. json.force_encoding(::Encoding::UTF_8)
  69723. json
  69724. end
  69725. end
  69726. self.use_standard_json_time_format = true
  69727. self.escape_html_entities_in_json = true
  69728. self.encode_big_decimal_as_string = true
  69729. end
  69730. end
  69731. end
  69732. class Object
  69733. def as_json(options = nil) #:nodoc:
  69734. if respond_to?(:to_hash)
  69735. to_hash
  69736. else
  69737. instance_values
  69738. end
  69739. end
  69740. end
  69741. class Struct #:nodoc:
  69742. def as_json(options = nil)
  69743. Hash[members.zip(values)]
  69744. end
  69745. end
  69746. class TrueClass
  69747. def as_json(options = nil) #:nodoc:
  69748. self
  69749. end
  69750. def encode_json(encoder) #:nodoc:
  69751. to_s
  69752. end
  69753. end
  69754. class FalseClass
  69755. def as_json(options = nil) #:nodoc:
  69756. self
  69757. end
  69758. def encode_json(encoder) #:nodoc:
  69759. to_s
  69760. end
  69761. end
  69762. class NilClass
  69763. def as_json(options = nil) #:nodoc:
  69764. self
  69765. end
  69766. def encode_json(encoder) #:nodoc:
  69767. 'null'
  69768. end
  69769. end
  69770. class String
  69771. def as_json(options = nil) #:nodoc:
  69772. self
  69773. end
  69774. def encode_json(encoder) #:nodoc:
  69775. encoder.escape(self)
  69776. end
  69777. end
  69778. class Symbol
  69779. def as_json(options = nil) #:nodoc:
  69780. to_s
  69781. end
  69782. end
  69783. class Numeric
  69784. def as_json(options = nil) #:nodoc:
  69785. self
  69786. end
  69787. def encode_json(encoder) #:nodoc:
  69788. to_s
  69789. end
  69790. end
  69791. class Float
  69792. # Encoding Infinity or NaN to JSON should return "null". The default returns
  69793. # "Infinity" or "NaN" which breaks parsing the JSON. E.g. JSON.parse('[NaN]').
  69794. def as_json(options = nil) #:nodoc:
  69795. finite? ? self : nil
  69796. end
  69797. end
  69798. class BigDecimal
  69799. # A BigDecimal would be naturally represented as a JSON number. Most libraries,
  69800. # however, parse non-integer JSON numbers directly as floats. Clients using
  69801. # those libraries would get in general a wrong number and no way to recover
  69802. # other than manually inspecting the string with the JSON code itself.
  69803. #
  69804. # That's why a JSON string is returned. The JSON literal is not numeric, but
  69805. # if the other end knows by contract that the data is supposed to be a
  69806. # BigDecimal, it still has the chance to post-process the string and get the
  69807. # real value.
  69808. #
  69809. # Use <tt>ActiveSupport.use_standard_json_big_decimal_format = true</tt> to
  69810. # override this behavior.
  69811. def as_json(options = nil) #:nodoc:
  69812. if finite?
  69813. ActiveSupport.encode_big_decimal_as_string ? to_s : self
  69814. else
  69815. nil
  69816. end
  69817. end
  69818. end
  69819. class Regexp
  69820. def as_json(options = nil) #:nodoc:
  69821. to_s
  69822. end
  69823. end
  69824. module Enumerable
  69825. def as_json(options = nil) #:nodoc:
  69826. to_a.as_json(options)
  69827. end
  69828. end
  69829. class Range
  69830. def as_json(options = nil) #:nodoc:
  69831. to_s
  69832. end
  69833. end
  69834. class Array
  69835. def as_json(options = nil) #:nodoc:
  69836. # use encoder as a proxy to call as_json on all elements, to protect from circular references
  69837. encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
  69838. map { |v| encoder.as_json(v, options) }
  69839. end
  69840. def encode_json(encoder) #:nodoc:
  69841. # we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
  69842. "[#{map { |v| v.encode_json(encoder) } * ','}]"
  69843. end
  69844. end
  69845. class Hash
  69846. def as_json(options = nil) #:nodoc:
  69847. # create a subset of the hash by applying :only or :except
  69848. subset = if options
  69849. if attrs = options[:only]
  69850. slice(*Array(attrs))
  69851. elsif attrs = options[:except]
  69852. except(*Array(attrs))
  69853. else
  69854. self
  69855. end
  69856. else
  69857. self
  69858. end
  69859. # use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
  69860. encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
  69861. Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
  69862. end
  69863. def encode_json(encoder) #:nodoc:
  69864. # values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
  69865. # processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
  69866. # on the other hand, we need to run as_json on the elements, because the model representation may contain fields
  69867. # like Time/Date in their original (not jsonified) form, etc.
  69868. "{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}"
  69869. end
  69870. end
  69871. class Time
  69872. def as_json(options = nil) #:nodoc:
  69873. if ActiveSupport.use_standard_json_time_format
  69874. xmlschema
  69875. else
  69876. %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
  69877. end
  69878. end
  69879. end
  69880. class Date
  69881. def as_json(options = nil) #:nodoc:
  69882. if ActiveSupport.use_standard_json_time_format
  69883. strftime("%Y-%m-%d")
  69884. else
  69885. strftime("%Y/%m/%d")
  69886. end
  69887. end
  69888. end
  69889. class DateTime
  69890. def as_json(options = nil) #:nodoc:
  69891. if ActiveSupport.use_standard_json_time_format
  69892. xmlschema
  69893. else
  69894. strftime('%Y/%m/%d %H:%M:%S %z')
  69895. end
  69896. end
  69897. end
  69898. require 'active_support/deprecation'
  69899. module ActiveSupport
  69900. module JSON
  69901. # Deprecated: A string that returns itself as its JSON-encoded form.
  69902. class Variable < String
  69903. def initialize(*args)
  69904. message = 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \
  69905. 'For your own custom JSON literals, define #as_json and #encode_json yourself.'
  69906. ActiveSupport::Deprecation.warn message
  69907. super
  69908. end
  69909. def as_json(options = nil) self end #:nodoc:
  69910. def encode_json(encoder) self end #:nodoc:
  69911. end
  69912. end
  69913. end
  69914. require 'active_support/json/decoding'
  69915. require 'active_support/json/encoding'
  69916. require 'thread_safe'
  69917. require 'openssl'
  69918. module ActiveSupport
  69919. # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
  69920. # It can be used to derive a number of keys for various purposes from a given secret.
  69921. # This lets rails applications have a single secure secret, but avoid reusing that
  69922. # key in multiple incompatible contexts.
  69923. class KeyGenerator
  69924. def initialize(secret, options = {})
  69925. @secret = secret
  69926. # The default iterations are higher than required for our key derivation uses
  69927. # on the off chance someone uses this for password storage
  69928. @iterations = options[:iterations] || 2**16
  69929. end
  69930. # Returns a derived key suitable for use. The default key_size is chosen
  69931. # to be compatible with the default settings of ActiveSupport::MessageVerifier.
  69932. # i.e. OpenSSL::Digest::SHA1#block_length
  69933. def generate_key(salt, key_size=64)
  69934. OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
  69935. end
  69936. end
  69937. # CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid
  69938. # re-executing the key generation process when it's called using the same salt and
  69939. # key_size
  69940. class CachingKeyGenerator
  69941. def initialize(key_generator)
  69942. @key_generator = key_generator
  69943. @cache_keys = ThreadSafe::Cache.new
  69944. end
  69945. # Returns a derived key suitable for use. The default key_size is chosen
  69946. # to be compatible with the default settings of ActiveSupport::MessageVerifier.
  69947. # i.e. OpenSSL::Digest::SHA1#block_length
  69948. def generate_key(salt, key_size=64)
  69949. @cache_keys["#{salt}#{key_size}"] ||= @key_generator.generate_key(salt, key_size)
  69950. end
  69951. end
  69952. class DummyKeyGenerator # :nodoc:
  69953. SECRET_MIN_LENGTH = 30 # Characters
  69954. def initialize(secret)
  69955. ensure_secret_secure(secret)
  69956. @secret = secret
  69957. end
  69958. def generate_key(salt)
  69959. @secret
  69960. end
  69961. private
  69962. # To prevent users from using something insecure like "Password" we make sure that the
  69963. # secret they've provided is at least 30 characters in length.
  69964. def ensure_secret_secure(secret)
  69965. if secret.blank?
  69966. raise ArgumentError, "A secret is required to generate an " +
  69967. "integrity hash for cookie session data. Use " +
  69968. "config.secret_key_base = \"some secret phrase of at " +
  69969. "least #{SECRET_MIN_LENGTH} characters\"" +
  69970. "in config/initializers/secret_token.rb"
  69971. end
  69972. if secret.length < SECRET_MIN_LENGTH
  69973. raise ArgumentError, "Secret should be something secure, " +
  69974. "like \"#{SecureRandom.hex(16)}\". The value you " +
  69975. "provided, \"#{secret}\", is shorter than the minimum length " +
  69976. "of #{SECRET_MIN_LENGTH} characters"
  69977. end
  69978. end
  69979. end
  69980. end
  69981. module ActiveSupport
  69982. # lazy_load_hooks allows rails to lazily load a lot of components and thus
  69983. # making the app boot faster. Because of this feature now there is no need to
  69984. # require <tt>ActiveRecord::Base</tt> at boot time purely to apply
  69985. # configuration. Instead a hook is registered that applies configuration once
  69986. # <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
  69987. # used as example but this feature can be applied elsewhere too.
  69988. #
  69989. # Here is an example where +on_load+ method is called to register a hook.
  69990. #
  69991. # initializer 'active_record.initialize_timezone' do
  69992. # ActiveSupport.on_load(:active_record) do
  69993. # self.time_zone_aware_attributes = true
  69994. # self.default_timezone = :utc
  69995. # end
  69996. # end
  69997. #
  69998. # When the entirety of +activerecord/lib/active_record/base.rb+ has been
  69999. # evaluated then +run_load_hooks+ is invoked. The very last line of
  70000. # +activerecord/lib/active_record/base.rb+ is:
  70001. #
  70002. # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
  70003. @load_hooks = Hash.new { |h,k| h[k] = [] }
  70004. @loaded = Hash.new { |h,k| h[k] = [] }
  70005. def self.on_load(name, options = {}, &block)
  70006. @loaded[name].each do |base|
  70007. execute_hook(base, options, block)
  70008. end
  70009. @load_hooks[name] << [block, options]
  70010. end
  70011. def self.execute_hook(base, options, block)
  70012. if options[:yield]
  70013. block.call(base)
  70014. else
  70015. base.instance_eval(&block)
  70016. end
  70017. end
  70018. def self.run_load_hooks(name, base = Object)
  70019. @loaded[name] << base
  70020. @load_hooks[name].each do |hook, options|
  70021. execute_hook(base, options, hook)
  70022. end
  70023. end
  70024. end
  70025. require 'active_support/log_subscriber'
  70026. require 'active_support/buffered_logger'
  70027. require 'active_support/notifications'
  70028. module ActiveSupport
  70029. class LogSubscriber
  70030. # Provides some helpers to deal with testing log subscribers by setting up
  70031. # notifications. Take for instance Active Record subscriber tests:
  70032. #
  70033. # class SyncLogSubscriberTest < ActiveSupport::TestCase
  70034. # include ActiveSupport::LogSubscriber::TestHelper
  70035. #
  70036. # def setup
  70037. # ActiveRecord::LogSubscriber.attach_to(:active_record)
  70038. # end
  70039. #
  70040. # def test_basic_query_logging
  70041. # Developer.all.to_a
  70042. # wait
  70043. # assert_equal 1, @logger.logged(:debug).size
  70044. # assert_match(/Developer Load/, @logger.logged(:debug).last)
  70045. # assert_match(/SELECT \* FROM "developers"/, @logger.logged(:debug).last)
  70046. # end
  70047. # end
  70048. #
  70049. # All you need to do is to ensure that your log subscriber is added to
  70050. # Rails::Subscriber, as in the second line of the code above. The test
  70051. # helpers are responsible for setting up the queue, subscriptions and
  70052. # turning colors in logs off.
  70053. #
  70054. # The messages are available in the @logger instance, which is a logger with
  70055. # limited powers (it actually does not send anything to your output), and
  70056. # you can collect them doing @logger.logged(level), where level is the level
  70057. # used in logging, like info, debug, warn and so on.
  70058. module TestHelper
  70059. def setup
  70060. @logger = MockLogger.new
  70061. @notifier = ActiveSupport::Notifications::Fanout.new
  70062. ActiveSupport::LogSubscriber.colorize_logging = false
  70063. @old_notifier = ActiveSupport::Notifications.notifier
  70064. set_logger(@logger)
  70065. ActiveSupport::Notifications.notifier = @notifier
  70066. end
  70067. def teardown
  70068. set_logger(nil)
  70069. ActiveSupport::Notifications.notifier = @old_notifier
  70070. end
  70071. class MockLogger
  70072. include ActiveSupport::Logger::Severity
  70073. attr_reader :flush_count
  70074. attr_accessor :level
  70075. def initialize(level = DEBUG)
  70076. @flush_count = 0
  70077. @level = level
  70078. @logged = Hash.new { |h,k| h[k] = [] }
  70079. end
  70080. def method_missing(level, message = nil)
  70081. if block_given?
  70082. @logged[level] << yield
  70083. else
  70084. @logged[level] << message
  70085. end
  70086. end
  70087. def logged(level)
  70088. @logged[level].compact.map { |l| l.to_s.strip }
  70089. end
  70090. def flush
  70091. @flush_count += 1
  70092. end
  70093. ActiveSupport::Logger::Severity.constants.each do |severity|
  70094. class_eval <<-EOT, __FILE__, __LINE__ + 1
  70095. def #{severity.downcase}?
  70096. #{severity} >= @level
  70097. end
  70098. EOT
  70099. end
  70100. end
  70101. # Wait notifications to be published.
  70102. def wait
  70103. @notifier.wait
  70104. end
  70105. # Overwrite if you use another logger in your log subscriber.
  70106. #
  70107. # def logger
  70108. # ActiveRecord::Base.logger = @logger
  70109. # end
  70110. def set_logger(logger)
  70111. ActiveSupport::LogSubscriber.logger = logger
  70112. end
  70113. end
  70114. end
  70115. end
  70116. require 'active_support/core_ext/module/attribute_accessors'
  70117. require 'active_support/core_ext/class/attribute'
  70118. module ActiveSupport
  70119. # ActiveSupport::LogSubscriber is an object set to consume
  70120. # ActiveSupport::Notifications with the sole purpose of logging them.
  70121. # The log subscriber dispatches notifications to a registered object based
  70122. # on its given namespace.
  70123. #
  70124. # An example would be Active Record log subscriber responsible for logging
  70125. # queries:
  70126. #
  70127. # module ActiveRecord
  70128. # class LogSubscriber < ActiveSupport::LogSubscriber
  70129. # def sql(event)
  70130. # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
  70131. # end
  70132. # end
  70133. # end
  70134. #
  70135. # And it's finally registered as:
  70136. #
  70137. # ActiveRecord::LogSubscriber.attach_to :active_record
  70138. #
  70139. # Since we need to know all instance methods before attaching the log
  70140. # subscriber, the line above should be called after your
  70141. # <tt>ActiveRecord::LogSubscriber</tt> definition.
  70142. #
  70143. # After configured, whenever a "sql.active_record" notification is published,
  70144. # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
  70145. # the sql method.
  70146. #
  70147. # Log subscriber also has some helpers to deal with logging and automatically
  70148. # flushes all logs when the request finishes (via action_dispatch.callback
  70149. # notification) in a Rails environment.
  70150. class LogSubscriber
  70151. # Embed in a String to clear all previous ANSI sequences.
  70152. CLEAR = "\e[0m"
  70153. BOLD = "\e[1m"
  70154. # Colors
  70155. BLACK = "\e[30m"
  70156. RED = "\e[31m"
  70157. GREEN = "\e[32m"
  70158. YELLOW = "\e[33m"
  70159. BLUE = "\e[34m"
  70160. MAGENTA = "\e[35m"
  70161. CYAN = "\e[36m"
  70162. WHITE = "\e[37m"
  70163. mattr_accessor :colorize_logging
  70164. self.colorize_logging = true
  70165. class << self
  70166. def logger
  70167. if defined?(Rails) && Rails.respond_to?(:logger)
  70168. @logger ||= Rails.logger
  70169. end
  70170. @logger
  70171. end
  70172. attr_writer :logger
  70173. def attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications)
  70174. log_subscribers << log_subscriber
  70175. log_subscriber.public_methods(false).each do |event|
  70176. next if %w{ start finish }.include?(event.to_s)
  70177. notifier.subscribe("#{event}.#{namespace}", log_subscriber)
  70178. end
  70179. end
  70180. def log_subscribers
  70181. @@log_subscribers ||= []
  70182. end
  70183. # Flush all log_subscribers' logger.
  70184. def flush_all!
  70185. logger.flush if logger.respond_to?(:flush)
  70186. end
  70187. end
  70188. def initialize
  70189. @queue_key = [self.class.name, object_id].join "-"
  70190. super
  70191. end
  70192. def logger
  70193. LogSubscriber.logger
  70194. end
  70195. def start(name, id, payload)
  70196. return unless logger
  70197. e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
  70198. parent = event_stack.last
  70199. parent << e if parent
  70200. event_stack.push e
  70201. end
  70202. def finish(name, id, payload)
  70203. return unless logger
  70204. finished = Time.now
  70205. event = event_stack.pop
  70206. event.end = finished
  70207. event.payload.merge!(payload)
  70208. method = name.split('.').first
  70209. begin
  70210. send(method, event)
  70211. rescue Exception => e
  70212. logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
  70213. end
  70214. end
  70215. protected
  70216. %w(info debug warn error fatal unknown).each do |level|
  70217. class_eval <<-METHOD, __FILE__, __LINE__ + 1
  70218. def #{level}(progname = nil, &block)
  70219. logger.#{level}(progname, &block) if logger
  70220. end
  70221. METHOD
  70222. end
  70223. # Set color by using a string or one of the defined constants. If a third
  70224. # option is set to +true+, it also adds bold to the string. This is based
  70225. # on the Highline implementation and will automatically append CLEAR to the
  70226. # end of the returned String.
  70227. def color(text, color, bold=false)
  70228. return text unless colorize_logging
  70229. color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
  70230. bold = bold ? BOLD : ""
  70231. "#{bold}#{color}#{text}#{CLEAR}"
  70232. end
  70233. private
  70234. def event_stack
  70235. Thread.current[@queue_key] ||= []
  70236. end
  70237. end
  70238. end
  70239. require 'active_support/core_ext/class/attribute_accessors'
  70240. require 'active_support/logger_silence'
  70241. require 'logger'
  70242. module ActiveSupport
  70243. class Logger < ::Logger
  70244. include LoggerSilence
  70245. # Broadcasts logs to multiple loggers.
  70246. def self.broadcast(logger) # :nodoc:
  70247. Module.new do
  70248. define_method(:add) do |*args, &block|
  70249. logger.add(*args, &block)
  70250. super(*args, &block)
  70251. end
  70252. define_method(:<<) do |x|
  70253. logger << x
  70254. super(x)
  70255. end
  70256. define_method(:close) do
  70257. logger.close
  70258. super()
  70259. end
  70260. define_method(:progname=) do |name|
  70261. logger.progname = name
  70262. super(name)
  70263. end
  70264. define_method(:formatter=) do |formatter|
  70265. logger.formatter = formatter
  70266. super(formatter)
  70267. end
  70268. define_method(:level=) do |level|
  70269. logger.level = level
  70270. super(level)
  70271. end
  70272. end
  70273. end
  70274. def initialize(*args)
  70275. super
  70276. @formatter = SimpleFormatter.new
  70277. end
  70278. # Simple formatter which only displays the message.
  70279. class SimpleFormatter < ::Logger::Formatter
  70280. # This method is invoked when a log event occurs
  70281. def call(severity, timestamp, progname, msg)
  70282. "#{String === msg ? msg : msg.inspect}\n"
  70283. end
  70284. end
  70285. end
  70286. end
  70287. require 'active_support/concern'
  70288. module LoggerSilence
  70289. extend ActiveSupport::Concern
  70290. included do
  70291. cattr_accessor :silencer
  70292. self.silencer = true
  70293. end
  70294. # Silences the logger for the duration of the block.
  70295. def silence(temporary_level = Logger::ERROR)
  70296. if silencer
  70297. begin
  70298. old_logger_level, self.level = level, temporary_level
  70299. yield self
  70300. ensure
  70301. self.level = old_logger_level
  70302. end
  70303. else
  70304. yield self
  70305. end
  70306. end
  70307. endrequire 'openssl'
  70308. require 'base64'
  70309. require 'active_support/core_ext/array/extract_options'
  70310. module ActiveSupport
  70311. # MessageEncryptor is a simple way to encrypt values which get stored
  70312. # somewhere you don't trust.
  70313. #
  70314. # The cipher text and initialization vector are base64 encoded and returned
  70315. # to you.
  70316. #
  70317. # This can be used in situations similar to the <tt>MessageVerifier</tt>, but
  70318. # where you don't want users to be able to determine the value of the payload.
  70319. #
  70320. # key = OpenSSL::Digest::SHA256.new('password').digest # => "\x89\xE0\x156\xAC..."
  70321. # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
  70322. # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
  70323. # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
  70324. class MessageEncryptor
  70325. module NullSerializer #:nodoc:
  70326. def self.load(value)
  70327. value
  70328. end
  70329. def self.dump(value)
  70330. value
  70331. end
  70332. end
  70333. class InvalidMessage < StandardError; end
  70334. OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
  70335. # Initialize a new MessageEncryptor. +secret+ must be at least as long as
  70336. # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
  70337. # bits. If you are using a user-entered secret, you can generate a suitable
  70338. # key with <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or
  70339. # similar.
  70340. #
  70341. # Options:
  70342. # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
  70343. # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
  70344. # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
  70345. def initialize(secret, *signature_key_or_options)
  70346. options = signature_key_or_options.extract_options!
  70347. sign_secret = signature_key_or_options.first
  70348. @secret = secret
  70349. @sign_secret = sign_secret
  70350. @cipher = options[:cipher] || 'aes-256-cbc'
  70351. @verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer)
  70352. @serializer = options[:serializer] || Marshal
  70353. end
  70354. # Encrypt and sign a message. We need to sign the message in order to avoid
  70355. # padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
  70356. def encrypt_and_sign(value)
  70357. verifier.generate(_encrypt(value))
  70358. end
  70359. # Decrypt and verify a message. We need to verify the message in order to
  70360. # avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
  70361. def decrypt_and_verify(value)
  70362. _decrypt(verifier.verify(value))
  70363. end
  70364. private
  70365. def _encrypt(value)
  70366. cipher = new_cipher
  70367. # Rely on OpenSSL for the initialization vector
  70368. iv = cipher.random_iv
  70369. cipher.encrypt
  70370. cipher.key = @secret
  70371. cipher.iv = iv
  70372. encrypted_data = cipher.update(@serializer.dump(value))
  70373. encrypted_data << cipher.final
  70374. [encrypted_data, iv].map {|v| ::Base64.strict_encode64(v)}.join("--")
  70375. end
  70376. def _decrypt(encrypted_message)
  70377. cipher = new_cipher
  70378. encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.decode64(v)}
  70379. cipher.decrypt
  70380. cipher.key = @secret
  70381. cipher.iv = iv
  70382. decrypted_data = cipher.update(encrypted_data)
  70383. decrypted_data << cipher.final
  70384. @serializer.load(decrypted_data)
  70385. rescue OpenSSLCipherError, TypeError
  70386. raise InvalidMessage
  70387. end
  70388. def new_cipher
  70389. OpenSSL::Cipher::Cipher.new(@cipher)
  70390. end
  70391. def verifier
  70392. @verifier
  70393. end
  70394. end
  70395. end
  70396. require 'base64'
  70397. require 'active_support/core_ext/object/blank'
  70398. module ActiveSupport
  70399. # +MessageVerifier+ makes it easy to generate and verify messages which are
  70400. # signed to prevent tampering.
  70401. #
  70402. # This is useful for cases like remember-me tokens and auto-unsubscribe links
  70403. # where the session store isn't suitable or available.
  70404. #
  70405. # Remember Me:
  70406. # cookies[:remember_me] = @verifier.generate([@user.id, 2.weeks.from_now])
  70407. #
  70408. # In the authentication filter:
  70409. #
  70410. # id, time = @verifier.verify(cookies[:remember_me])
  70411. # if time < Time.now
  70412. # self.current_user = User.find(id)
  70413. # end
  70414. #
  70415. # By default it uses Marshal to serialize the message. If you want to use
  70416. # another serialization method, you can set the serializer attribute to
  70417. # something that responds to dump and load, e.g.:
  70418. #
  70419. # @verifier.serializer = YAML
  70420. class MessageVerifier
  70421. class InvalidSignature < StandardError; end
  70422. def initialize(secret, options = {})
  70423. @secret = secret
  70424. @digest = options[:digest] || 'SHA1'
  70425. @serializer = options[:serializer] || Marshal
  70426. end
  70427. def verify(signed_message)
  70428. raise InvalidSignature if signed_message.blank?
  70429. data, digest = signed_message.split("--")
  70430. if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
  70431. @serializer.load(::Base64.decode64(data))
  70432. else
  70433. raise InvalidSignature
  70434. end
  70435. end
  70436. def generate(value)
  70437. data = ::Base64.strict_encode64(@serializer.dump(value))
  70438. "#{data}--#{generate_digest(data)}"
  70439. end
  70440. private
  70441. # constant-time comparison algorithm to prevent timing attacks
  70442. def secure_compare(a, b)
  70443. return false unless a.bytesize == b.bytesize
  70444. l = a.unpack "C#{a.bytesize}"
  70445. res = 0
  70446. b.each_byte { |byte| res |= byte ^ l.shift }
  70447. res == 0
  70448. end
  70449. def generate_digest(data)
  70450. require 'openssl' unless defined?(OpenSSL)
  70451. OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data)
  70452. end
  70453. end
  70454. end
  70455. # encoding: utf-8
  70456. require 'active_support/json'
  70457. require 'active_support/core_ext/string/access'
  70458. require 'active_support/core_ext/string/behavior'
  70459. require 'active_support/core_ext/module/delegation'
  70460. module ActiveSupport #:nodoc:
  70461. module Multibyte #:nodoc:
  70462. # Chars enables you to work transparently with UTF-8 encoding in the Ruby
  70463. # String class without having extensive knowledge about the encoding. A
  70464. # Chars object accepts a string upon initialization and proxies String
  70465. # methods in an encoding safe manner. All the normal String methods are also
  70466. # implemented on the proxy.
  70467. #
  70468. # String methods are proxied through the Chars object, and can be accessed
  70469. # through the +mb_chars+ method. Methods which would normally return a
  70470. # String object now return a Chars object so methods can be chained.
  70471. #
  70472. # 'The Perfect String '.mb_chars.downcase.strip.normalize # => "the perfect string"
  70473. #
  70474. # Chars objects are perfectly interchangeable with String objects as long as
  70475. # no explicit class checks are made. If certain methods do explicitly check
  70476. # the class, call +to_s+ before you pass chars objects to them.
  70477. #
  70478. # bad.explicit_checking_method 'T'.mb_chars.downcase.to_s
  70479. #
  70480. # The default Chars implementation assumes that the encoding of the string
  70481. # is UTF-8, if you want to handle different encodings you can write your own
  70482. # multibyte string handler and configure it through
  70483. # ActiveSupport::Multibyte.proxy_class.
  70484. #
  70485. # class CharsForUTF32
  70486. # def size
  70487. # @wrapped_string.size / 4
  70488. # end
  70489. #
  70490. # def self.accepts?(string)
  70491. # string.length % 4 == 0
  70492. # end
  70493. # end
  70494. #
  70495. # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
  70496. class Chars
  70497. include Comparable
  70498. attr_reader :wrapped_string
  70499. alias to_s wrapped_string
  70500. alias to_str wrapped_string
  70501. delegate :<=>, :=~, :acts_like_string?, :to => :wrapped_string
  70502. # Creates a new Chars instance by wrapping _string_.
  70503. def initialize(string)
  70504. @wrapped_string = string
  70505. @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
  70506. end
  70507. # Forward all undefined methods to the wrapped string.
  70508. def method_missing(method, *args, &block)
  70509. if method.to_s =~ /!$/
  70510. result = @wrapped_string.__send__(method, *args, &block)
  70511. self if result
  70512. else
  70513. result = @wrapped_string.__send__(method, *args, &block)
  70514. result.kind_of?(String) ? chars(result) : result
  70515. end
  70516. end
  70517. # Returns +true+ if _obj_ responds to the given method. Private methods
  70518. # are included in the search only if the optional second parameter
  70519. # evaluates to +true+.
  70520. def respond_to_missing?(method, include_private)
  70521. @wrapped_string.respond_to?(method, include_private)
  70522. end
  70523. # Returns +true+ when the proxy class can handle the string. Returns
  70524. # +false+ otherwise.
  70525. def self.consumes?(string)
  70526. string.encoding == Encoding::UTF_8
  70527. end
  70528. # Works just like <tt>String#split</tt>, with the exception that the items
  70529. # in the resulting list are Chars instances instead of String. This makes
  70530. # chaining methods easier.
  70531. #
  70532. # 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
  70533. def split(*args)
  70534. @wrapped_string.split(*args).map { |i| self.class.new(i) }
  70535. end
  70536. # Works like like <tt>String#slice!</tt>, but returns an instance of
  70537. # Chars, or nil if the string was not modified.
  70538. def slice!(*args)
  70539. chars(@wrapped_string.slice!(*args))
  70540. end
  70541. # Reverses all characters in the string.
  70542. #
  70543. # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
  70544. def reverse
  70545. chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack('U*'))
  70546. end
  70547. # Limits the byte size of the string to a number of bytes without breaking
  70548. # characters. Usable when the storage for a string is limited for some
  70549. # reason.
  70550. #
  70551. # 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
  70552. def limit(limit)
  70553. slice(0...translate_offset(limit))
  70554. end
  70555. # Converts characters in the string to uppercase.
  70556. #
  70557. # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
  70558. def upcase
  70559. chars Unicode.upcase(@wrapped_string)
  70560. end
  70561. # Converts characters in the string to lowercase.
  70562. #
  70563. # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
  70564. def downcase
  70565. chars Unicode.downcase(@wrapped_string)
  70566. end
  70567. # Converts characters in the string to the opposite case.
  70568. #
  70569. # 'El Cañón".mb_chars.swapcase.to_s # => "eL cAÑÓN"
  70570. def swapcase
  70571. chars Unicode.swapcase(@wrapped_string)
  70572. end
  70573. # Converts the first character to uppercase and the remainder to lowercase.
  70574. #
  70575. # 'über'.mb_chars.capitalize.to_s # => "Über"
  70576. def capitalize
  70577. (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
  70578. end
  70579. # Capitalizes the first letter of every word, when possible.
  70580. #
  70581. # "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
  70582. # "日本語".mb_chars.titleize # => "日本語"
  70583. def titleize
  70584. chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1)})
  70585. end
  70586. alias_method :titlecase, :titleize
  70587. # Returns the KC normalization of the string by default. NFKC is
  70588. # considered the best normalization form for passing strings to databases
  70589. # and validations.
  70590. #
  70591. # * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
  70592. # <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
  70593. # ActiveSupport::Multibyte::Unicode.default_normalization_form
  70594. def normalize(form = nil)
  70595. chars(Unicode.normalize(@wrapped_string, form))
  70596. end
  70597. # Performs canonical decomposition on all the characters.
  70598. #
  70599. # 'é'.length # => 2
  70600. # 'é'.mb_chars.decompose.to_s.length # => 3
  70601. def decompose
  70602. chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack('U*'))
  70603. end
  70604. # Performs composition on all the characters.
  70605. #
  70606. # 'é'.length # => 3
  70607. # 'é'.mb_chars.compose.to_s.length # => 2
  70608. def compose
  70609. chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack('U*'))
  70610. end
  70611. # Returns the number of grapheme clusters in the string.
  70612. #
  70613. # 'क्षि'.mb_chars.length # => 4
  70614. # 'क्षि'.mb_chars.grapheme_length # => 3
  70615. def grapheme_length
  70616. Unicode.unpack_graphemes(@wrapped_string).length
  70617. end
  70618. # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
  70619. # resulting in a valid UTF-8 string.
  70620. #
  70621. # Passing +true+ will forcibly tidy all bytes, assuming that the string's
  70622. # encoding is entirely CP1252 or ISO-8859-1.
  70623. def tidy_bytes(force = false)
  70624. chars(Unicode.tidy_bytes(@wrapped_string, force))
  70625. end
  70626. def as_json(options = nil) #:nodoc:
  70627. to_s.as_json(options)
  70628. end
  70629. %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
  70630. define_method("#{method}!") do |*args|
  70631. @wrapped_string = send(method, *args).to_s
  70632. self
  70633. end
  70634. end
  70635. protected
  70636. def translate_offset(byte_offset) #:nodoc:
  70637. return nil if byte_offset.nil?
  70638. return 0 if @wrapped_string == ''
  70639. begin
  70640. @wrapped_string.byteslice(0...byte_offset).unpack('U*').length
  70641. rescue ArgumentError
  70642. byte_offset -= 1
  70643. retry
  70644. end
  70645. end
  70646. def chars(string) #:nodoc:
  70647. self.class.new(string)
  70648. end
  70649. end
  70650. end
  70651. end
  70652. # encoding: utf-8
  70653. module ActiveSupport
  70654. module Multibyte
  70655. module Unicode
  70656. extend self
  70657. # A list of all available normalization forms.
  70658. # See http://www.unicode.org/reports/tr15/tr15-29.html for more
  70659. # information about normalization.
  70660. NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
  70661. # The Unicode version that is supported by the implementation
  70662. UNICODE_VERSION = '6.2.0'
  70663. # The default normalization used for operations that require
  70664. # normalization. It can be set to any of the normalizations
  70665. # in NORMALIZATION_FORMS.
  70666. #
  70667. # ActiveSupport::Multibyte::Unicode.default_normalization_form = :c
  70668. attr_accessor :default_normalization_form
  70669. @default_normalization_form = :kc
  70670. # Hangul character boundaries and properties
  70671. HANGUL_SBASE = 0xAC00
  70672. HANGUL_LBASE = 0x1100
  70673. HANGUL_VBASE = 0x1161
  70674. HANGUL_TBASE = 0x11A7
  70675. HANGUL_LCOUNT = 19
  70676. HANGUL_VCOUNT = 21
  70677. HANGUL_TCOUNT = 28
  70678. HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
  70679. HANGUL_SCOUNT = 11172
  70680. HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
  70681. HANGUL_JAMO_FIRST = 0x1100
  70682. HANGUL_JAMO_LAST = 0x11FF
  70683. # All the unicode whitespace
  70684. WHITESPACE = [
  70685. (0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
  70686. 0x0020, # White_Space # Zs SPACE
  70687. 0x0085, # White_Space # Cc <control-0085>
  70688. 0x00A0, # White_Space # Zs NO-BREAK SPACE
  70689. 0x1680, # White_Space # Zs OGHAM SPACE MARK
  70690. 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
  70691. (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
  70692. 0x2028, # White_Space # Zl LINE SEPARATOR
  70693. 0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
  70694. 0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
  70695. 0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
  70696. 0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
  70697. ].flatten.freeze
  70698. # BOM (byte order mark) can also be seen as whitespace, it's a
  70699. # non-rendering character used to distinguish between little and big
  70700. # endian. This is not an issue in utf-8, so it must be ignored.
  70701. LEADERS_AND_TRAILERS = WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
  70702. # Returns a regular expression pattern that matches the passed Unicode
  70703. # codepoints.
  70704. def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
  70705. array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
  70706. end
  70707. TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
  70708. LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
  70709. # Detect whether the codepoint is in a certain character class. Returns
  70710. # +true+ when it's in the specified character class and +false+ otherwise.
  70711. # Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
  70712. # <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
  70713. #
  70714. # Primarily used by the grapheme cluster support.
  70715. def in_char_class?(codepoint, classes)
  70716. classes.detect { |c| database.boundary[c] === codepoint } ? true : false
  70717. end
  70718. # Unpack the string at grapheme boundaries. Returns a list of character
  70719. # lists.
  70720. #
  70721. # Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
  70722. # Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
  70723. def unpack_graphemes(string)
  70724. codepoints = string.codepoints.to_a
  70725. unpacked = []
  70726. pos = 0
  70727. marker = 0
  70728. eoc = codepoints.length
  70729. while(pos < eoc)
  70730. pos += 1
  70731. previous = codepoints[pos-1]
  70732. current = codepoints[pos]
  70733. if (
  70734. # CR X LF
  70735. ( previous == database.boundary[:cr] and current == database.boundary[:lf] ) or
  70736. # L X (L|V|LV|LVT)
  70737. ( database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or
  70738. # (LV|V) X (V|T)
  70739. ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or
  70740. # (LVT|T) X (T)
  70741. ( in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current ) or
  70742. # X Extend
  70743. (database.boundary[:extend] === current)
  70744. )
  70745. else
  70746. unpacked << codepoints[marker..pos-1]
  70747. marker = pos
  70748. end
  70749. end
  70750. unpacked
  70751. end
  70752. # Reverse operation of unpack_graphemes.
  70753. #
  70754. # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
  70755. def pack_graphemes(unpacked)
  70756. unpacked.flatten.pack('U*')
  70757. end
  70758. # Re-order codepoints so the string becomes canonical.
  70759. def reorder_characters(codepoints)
  70760. length = codepoints.length- 1
  70761. pos = 0
  70762. while pos < length do
  70763. cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos+1]]
  70764. if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
  70765. codepoints[pos..pos+1] = cp2.code, cp1.code
  70766. pos += (pos > 0 ? -1 : 1)
  70767. else
  70768. pos += 1
  70769. end
  70770. end
  70771. codepoints
  70772. end
  70773. # Decompose composed characters to the decomposed form.
  70774. def decompose(type, codepoints)
  70775. codepoints.inject([]) do |decomposed, cp|
  70776. # if it's a hangul syllable starter character
  70777. if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
  70778. sindex = cp - HANGUL_SBASE
  70779. ncp = [] # new codepoints
  70780. ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
  70781. ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
  70782. tindex = sindex % HANGUL_TCOUNT
  70783. ncp << (HANGUL_TBASE + tindex) unless tindex == 0
  70784. decomposed.concat ncp
  70785. # if the codepoint is decomposable in with the current decomposition type
  70786. elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatability)
  70787. decomposed.concat decompose(type, ncp.dup)
  70788. else
  70789. decomposed << cp
  70790. end
  70791. end
  70792. end
  70793. # Compose decomposed characters to the composed form.
  70794. def compose(codepoints)
  70795. pos = 0
  70796. eoa = codepoints.length - 1
  70797. starter_pos = 0
  70798. starter_char = codepoints[0]
  70799. previous_combining_class = -1
  70800. while pos < eoa
  70801. pos += 1
  70802. lindex = starter_char - HANGUL_LBASE
  70803. # -- Hangul
  70804. if 0 <= lindex and lindex < HANGUL_LCOUNT
  70805. vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
  70806. if 0 <= vindex and vindex < HANGUL_VCOUNT
  70807. tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
  70808. if 0 <= tindex and tindex < HANGUL_TCOUNT
  70809. j = starter_pos + 2
  70810. eoa -= 2
  70811. else
  70812. tindex = 0
  70813. j = starter_pos + 1
  70814. eoa -= 1
  70815. end
  70816. codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
  70817. end
  70818. starter_pos += 1
  70819. starter_char = codepoints[starter_pos]
  70820. # -- Other characters
  70821. else
  70822. current_char = codepoints[pos]
  70823. current = database.codepoints[current_char]
  70824. if current.combining_class > previous_combining_class
  70825. if ref = database.composition_map[starter_char]
  70826. composition = ref[current_char]
  70827. else
  70828. composition = nil
  70829. end
  70830. unless composition.nil?
  70831. codepoints[starter_pos] = composition
  70832. starter_char = composition
  70833. codepoints.delete_at pos
  70834. eoa -= 1
  70835. pos -= 1
  70836. previous_combining_class = -1
  70837. else
  70838. previous_combining_class = current.combining_class
  70839. end
  70840. else
  70841. previous_combining_class = current.combining_class
  70842. end
  70843. if current.combining_class == 0
  70844. starter_pos = pos
  70845. starter_char = codepoints[pos]
  70846. end
  70847. end
  70848. end
  70849. codepoints
  70850. end
  70851. # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
  70852. # resulting in a valid UTF-8 string.
  70853. #
  70854. # Passing +true+ will forcibly tidy all bytes, assuming that the string's
  70855. # encoding is entirely CP1252 or ISO-8859-1.
  70856. def tidy_bytes(string, force = false)
  70857. if force
  70858. return string.unpack("C*").map do |b|
  70859. tidy_byte(b)
  70860. end.flatten.compact.pack("C*").unpack("U*").pack("U*")
  70861. end
  70862. bytes = string.unpack("C*")
  70863. conts_expected = 0
  70864. last_lead = 0
  70865. bytes.each_index do |i|
  70866. byte = bytes[i]
  70867. is_cont = byte > 127 && byte < 192
  70868. is_lead = byte > 191 && byte < 245
  70869. is_unused = byte > 240
  70870. is_restricted = byte > 244
  70871. # Impossible or highly unlikely byte? Clean it.
  70872. if is_unused || is_restricted
  70873. bytes[i] = tidy_byte(byte)
  70874. elsif is_cont
  70875. # Not expecting continuation byte? Clean up. Otherwise, now expect one less.
  70876. conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1
  70877. else
  70878. if conts_expected > 0
  70879. # Expected continuation, but got ASCII or leading? Clean backwards up to
  70880. # the leading byte.
  70881. (1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])}
  70882. conts_expected = 0
  70883. end
  70884. if is_lead
  70885. # Final byte is leading? Clean it.
  70886. if i == bytes.length - 1
  70887. bytes[i] = tidy_byte(bytes.last)
  70888. else
  70889. # Valid leading byte? Expect continuations determined by position of
  70890. # first zero bit, with max of 3.
  70891. conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3
  70892. last_lead = i
  70893. end
  70894. end
  70895. end
  70896. end
  70897. bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
  70898. end
  70899. # Returns the KC normalization of the string by default. NFKC is
  70900. # considered the best normalization form for passing strings to databases
  70901. # and validations.
  70902. #
  70903. # * <tt>string</tt> - The string to perform normalization on.
  70904. # * <tt>form</tt> - The form you want to normalize in. Should be one of
  70905. # the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
  70906. # Default is ActiveSupport::Multibyte.default_normalization_form.
  70907. def normalize(string, form=nil)
  70908. form ||= @default_normalization_form
  70909. # See http://www.unicode.org/reports/tr15, Table 1
  70910. codepoints = string.codepoints.to_a
  70911. case form
  70912. when :d
  70913. reorder_characters(decompose(:canonical, codepoints))
  70914. when :c
  70915. compose(reorder_characters(decompose(:canonical, codepoints)))
  70916. when :kd
  70917. reorder_characters(decompose(:compatability, codepoints))
  70918. when :kc
  70919. compose(reorder_characters(decompose(:compatability, codepoints)))
  70920. else
  70921. raise ArgumentError, "#{form} is not a valid normalization variant", caller
  70922. end.pack('U*')
  70923. end
  70924. def downcase(string)
  70925. apply_mapping string, :lowercase_mapping
  70926. end
  70927. def upcase(string)
  70928. apply_mapping string, :uppercase_mapping
  70929. end
  70930. def swapcase(string)
  70931. apply_mapping string, :swapcase_mapping
  70932. end
  70933. # Holds data about a codepoint in the Unicode database.
  70934. class Codepoint
  70935. attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
  70936. def swapcase_mapping
  70937. uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
  70938. end
  70939. end
  70940. # Holds static data from the Unicode database.
  70941. class UnicodeDatabase
  70942. ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
  70943. attr_writer(*ATTRIBUTES)
  70944. def initialize
  70945. @codepoints = Hash.new(Codepoint.new)
  70946. @composition_exclusion = []
  70947. @composition_map = {}
  70948. @boundary = {}
  70949. @cp1252 = {}
  70950. end
  70951. # Lazy load the Unicode database so it's only loaded when it's actually used
  70952. ATTRIBUTES.each do |attr_name|
  70953. class_eval(<<-EOS, __FILE__, __LINE__ + 1)
  70954. def #{attr_name} # def codepoints
  70955. load # load
  70956. @#{attr_name} # @codepoints
  70957. end # end
  70958. EOS
  70959. end
  70960. # Loads the Unicode database and returns all the internal objects of
  70961. # UnicodeDatabase.
  70962. def load
  70963. begin
  70964. @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
  70965. rescue => e
  70966. raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
  70967. end
  70968. # Redefine the === method so we can write shorter rules for grapheme cluster breaks
  70969. @boundary.each do |k,_|
  70970. @boundary[k].instance_eval do
  70971. def ===(other)
  70972. detect { |i| i === other } ? true : false
  70973. end
  70974. end if @boundary[k].kind_of?(Array)
  70975. end
  70976. # define attr_reader methods for the instance variables
  70977. class << self
  70978. attr_reader(*ATTRIBUTES)
  70979. end
  70980. end
  70981. # Returns the directory in which the data files are stored.
  70982. def self.dirname
  70983. File.dirname(__FILE__) + '/../values/'
  70984. end
  70985. # Returns the filename for the data file for this version.
  70986. def self.filename
  70987. File.expand_path File.join(dirname, "unicode_tables.dat")
  70988. end
  70989. end
  70990. private
  70991. def apply_mapping(string, mapping) #:nodoc:
  70992. string.each_codepoint.map do |codepoint|
  70993. cp = database.codepoints[codepoint]
  70994. if cp and (ncp = cp.send(mapping)) and ncp > 0
  70995. ncp
  70996. else
  70997. codepoint
  70998. end
  70999. end.pack('U*')
  71000. end
  71001. def tidy_byte(byte)
  71002. if byte < 160
  71003. [database.cp1252[byte] || byte].pack("U").unpack("C*")
  71004. elsif byte < 192
  71005. [194, byte]
  71006. else
  71007. [195, byte - 64]
  71008. end
  71009. end
  71010. def database
  71011. @database ||= UnicodeDatabase.new
  71012. end
  71013. end
  71014. end
  71015. end
  71016. module ActiveSupport #:nodoc:
  71017. module Multibyte
  71018. autoload :Chars, 'active_support/multibyte/chars'
  71019. autoload :Unicode, 'active_support/multibyte/unicode'
  71020. # The proxy class returned when calling mb_chars. You can use this accessor
  71021. # to configure your own proxy class so you can support other encodings. See
  71022. # the ActiveSupport::Multibyte::Chars implementation for an example how to
  71023. # do this.
  71024. #
  71025. # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
  71026. def self.proxy_class=(klass)
  71027. @proxy_class = klass
  71028. end
  71029. # Returns the current proxy class.
  71030. def self.proxy_class
  71031. @proxy_class ||= ActiveSupport::Multibyte::Chars
  71032. end
  71033. end
  71034. end
  71035. require 'mutex_m'
  71036. require 'thread_safe'
  71037. module ActiveSupport
  71038. module Notifications
  71039. # This is a default queue implementation that ships with Notifications.
  71040. # It just pushes events to all registered log subscribers.
  71041. #
  71042. # This class is thread safe. All methods are reentrant.
  71043. class Fanout
  71044. include Mutex_m
  71045. def initialize
  71046. @subscribers = []
  71047. @listeners_for = ThreadSafe::Cache.new
  71048. super
  71049. end
  71050. def subscribe(pattern = nil, block = Proc.new)
  71051. subscriber = Subscribers.new pattern, block
  71052. synchronize do
  71053. @subscribers << subscriber
  71054. @listeners_for.clear
  71055. end
  71056. subscriber
  71057. end
  71058. def unsubscribe(subscriber)
  71059. synchronize do
  71060. @subscribers.reject! { |s| s.matches?(subscriber) }
  71061. @listeners_for.clear
  71062. end
  71063. end
  71064. def start(name, id, payload)
  71065. listeners_for(name).each { |s| s.start(name, id, payload) }
  71066. end
  71067. def finish(name, id, payload)
  71068. listeners_for(name).each { |s| s.finish(name, id, payload) }
  71069. end
  71070. def publish(name, *args)
  71071. listeners_for(name).each { |s| s.publish(name, *args) }
  71072. end
  71073. def listeners_for(name)
  71074. # this is correctly done double-checked locking (ThreadSafe::Cache's lookups have volatile semantics)
  71075. @listeners_for[name] || synchronize do
  71076. # use synchronisation when accessing @subscribers
  71077. @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
  71078. end
  71079. end
  71080. def listening?(name)
  71081. listeners_for(name).any?
  71082. end
  71083. # This is a sync queue, so there is no waiting.
  71084. def wait
  71085. end
  71086. module Subscribers # :nodoc:
  71087. def self.new(pattern, listener)
  71088. if listener.respond_to?(:start) and listener.respond_to?(:finish)
  71089. subscriber = Evented.new pattern, listener
  71090. else
  71091. subscriber = Timed.new pattern, listener
  71092. end
  71093. unless pattern
  71094. AllMessages.new(subscriber)
  71095. else
  71096. subscriber
  71097. end
  71098. end
  71099. class Evented #:nodoc:
  71100. def initialize(pattern, delegate)
  71101. @pattern = pattern
  71102. @delegate = delegate
  71103. end
  71104. def start(name, id, payload)
  71105. @delegate.start name, id, payload
  71106. end
  71107. def finish(name, id, payload)
  71108. @delegate.finish name, id, payload
  71109. end
  71110. def subscribed_to?(name)
  71111. @pattern === name.to_s
  71112. end
  71113. def matches?(subscriber_or_name)
  71114. self === subscriber_or_name ||
  71115. @pattern && @pattern === subscriber_or_name
  71116. end
  71117. end
  71118. class Timed < Evented
  71119. def initialize(pattern, delegate)
  71120. @timestack = []
  71121. super
  71122. end
  71123. def publish(name, *args)
  71124. @delegate.call name, *args
  71125. end
  71126. def start(name, id, payload)
  71127. @timestack.push Time.now
  71128. end
  71129. def finish(name, id, payload)
  71130. started = @timestack.pop
  71131. @delegate.call(name, started, Time.now, id, payload)
  71132. end
  71133. end
  71134. class AllMessages # :nodoc:
  71135. def initialize(delegate)
  71136. @delegate = delegate
  71137. end
  71138. def start(name, id, payload)
  71139. @delegate.start name, id, payload
  71140. end
  71141. def finish(name, id, payload)
  71142. @delegate.finish name, id, payload
  71143. end
  71144. def publish(name, *args)
  71145. @delegate.publish name, *args
  71146. end
  71147. def subscribed_to?(name)
  71148. true
  71149. end
  71150. alias :matches? :===
  71151. end
  71152. end
  71153. end
  71154. end
  71155. end
  71156. require 'securerandom'
  71157. module ActiveSupport
  71158. module Notifications
  71159. # Instrumentors are stored in a thread local.
  71160. class Instrumenter
  71161. attr_reader :id
  71162. def initialize(notifier)
  71163. @id = unique_id
  71164. @notifier = notifier
  71165. end
  71166. # Instrument the given block by measuring the time taken to execute it
  71167. # and publish it. Notice that events get sent even if an error occurs
  71168. # in the passed-in block.
  71169. def instrument(name, payload={})
  71170. start name, payload
  71171. begin
  71172. yield
  71173. rescue Exception => e
  71174. payload[:exception] = [e.class.name, e.message]
  71175. raise e
  71176. ensure
  71177. finish name, payload
  71178. end
  71179. end
  71180. # Send a start notification with +name+ and +payload+.
  71181. def start(name, payload)
  71182. @notifier.start name, @id, payload
  71183. end
  71184. # Send a finish notification with +name+ and +payload+.
  71185. def finish(name, payload)
  71186. @notifier.finish name, @id, payload
  71187. end
  71188. private
  71189. def unique_id
  71190. SecureRandom.hex(10)
  71191. end
  71192. end
  71193. class Event
  71194. attr_reader :name, :time, :transaction_id, :payload, :children
  71195. attr_accessor :end
  71196. def initialize(name, start, ending, transaction_id, payload)
  71197. @name = name
  71198. @payload = payload.dup
  71199. @time = start
  71200. @transaction_id = transaction_id
  71201. @end = ending
  71202. @children = []
  71203. end
  71204. def duration
  71205. 1000.0 * (self.end - time)
  71206. end
  71207. def <<(event)
  71208. @children << event
  71209. end
  71210. def parent_of?(event)
  71211. @children.include? event
  71212. end
  71213. end
  71214. end
  71215. end
  71216. require 'active_support/notifications/instrumenter'
  71217. require 'active_support/notifications/fanout'
  71218. module ActiveSupport
  71219. # = Notifications
  71220. #
  71221. # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
  71222. # Ruby.
  71223. #
  71224. # == Instrumenters
  71225. #
  71226. # To instrument an event you just need to do:
  71227. #
  71228. # ActiveSupport::Notifications.instrument('render', extra: :information) do
  71229. # render text: 'Foo'
  71230. # end
  71231. #
  71232. # That executes the block first and notifies all subscribers once done.
  71233. #
  71234. # In the example above +render+ is the name of the event, and the rest is called
  71235. # the _payload_. The payload is a mechanism that allows instrumenters to pass
  71236. # extra information to subscribers. Payloads consist of a hash whose contents
  71237. # are arbitrary and generally depend on the event.
  71238. #
  71239. # == Subscribers
  71240. #
  71241. # You can consume those events and the information they provide by registering
  71242. # a subscriber.
  71243. #
  71244. # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
  71245. # name # => String, name of the event (such as 'render' from above)
  71246. # start # => Time, when the instrumented block started execution
  71247. # finish # => Time, when the instrumented block ended execution
  71248. # id # => String, unique ID for this notification
  71249. # payload # => Hash, the payload
  71250. # end
  71251. #
  71252. # For instance, let's store all "render" events in an array:
  71253. #
  71254. # events = []
  71255. #
  71256. # ActiveSupport::Notifications.subscribe('render') do |*args|
  71257. # events << ActiveSupport::Notifications::Event.new(*args)
  71258. # end
  71259. #
  71260. # That code returns right away, you are just subscribing to "render" events.
  71261. # The block is saved and will be called whenever someone instruments "render":
  71262. #
  71263. # ActiveSupport::Notifications.instrument('render', extra: :information) do
  71264. # render text: 'Foo'
  71265. # end
  71266. #
  71267. # event = events.first
  71268. # event.name # => "render"
  71269. # event.duration # => 10 (in milliseconds)
  71270. # event.payload # => { extra: :information }
  71271. #
  71272. # The block in the <tt>subscribe</tt> call gets the name of the event, start
  71273. # timestamp, end timestamp, a string with a unique identifier for that event
  71274. # (something like "535801666f04d0298cd6"), and a hash with the payload, in
  71275. # that order.
  71276. #
  71277. # If an exception happens during that particular instrumentation the payload will
  71278. # have a key <tt>:exception</tt> with an array of two elements as value: a string with
  71279. # the name of the exception class, and the exception message.
  71280. #
  71281. # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
  71282. # is able to take the arguments as they come and provide an object-oriented
  71283. # interface to that data.
  71284. #
  71285. # It is also possible to pass an object as the second parameter passed to the
  71286. # <tt>subscribe</tt> method instead of a block:
  71287. #
  71288. # module ActionController
  71289. # class PageRequest
  71290. # def call(name, started, finished, unique_id, payload)
  71291. # Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
  71292. # end
  71293. # end
  71294. # end
  71295. #
  71296. # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
  71297. #
  71298. # resulting in the following output within the logs including a hash with the payload:
  71299. #
  71300. # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
  71301. # controller: "Devise::SessionsController",
  71302. # action: "new",
  71303. # params: {"action"=>"new", "controller"=>"devise/sessions"},
  71304. # format: :html,
  71305. # method: "GET",
  71306. # path: "/login/sign_in",
  71307. # status: 200,
  71308. # view_runtime: 279.3080806732178,
  71309. # db_runtime: 40.053
  71310. # }
  71311. #
  71312. # You can also subscribe to all events whose name matches a certain regexp:
  71313. #
  71314. # ActiveSupport::Notifications.subscribe(/render/) do |*args|
  71315. # ...
  71316. # end
  71317. #
  71318. # and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
  71319. # to all events.
  71320. #
  71321. # == Temporary Subscriptions
  71322. #
  71323. # Sometimes you do not want to subscribe to an event for the entire life of
  71324. # the application. There are two ways to unsubscribe.
  71325. #
  71326. # WARNING: The instrumentation framework is designed for long-running subscribers,
  71327. # use this feature sparingly because it wipes some internal caches and that has
  71328. # a negative impact on performance.
  71329. #
  71330. # === Subscribe While a Block Runs
  71331. #
  71332. # You can subscribe to some event temporarily while some block runs. For
  71333. # example, in
  71334. #
  71335. # callback = lambda {|*args| ... }
  71336. # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
  71337. # ...
  71338. # end
  71339. #
  71340. # the callback will be called for all "sql.active_record" events instrumented
  71341. # during the execution of the block. The callback is unsubscribed automatically
  71342. # after that.
  71343. #
  71344. # === Manual Unsubscription
  71345. #
  71346. # The +subscribe+ method returns a subscriber object:
  71347. #
  71348. # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
  71349. # ...
  71350. # end
  71351. #
  71352. # To prevent that block from being called anymore, just unsubscribe passing
  71353. # that reference:
  71354. #
  71355. # ActiveSupport::Notifications.unsubscribe(subscriber)
  71356. #
  71357. # == Default Queue
  71358. #
  71359. # Notifications ships with a queue implementation that consumes and publish events
  71360. # to log subscribers in a thread. You can use any queue implementation you want.
  71361. #
  71362. module Notifications
  71363. class << self
  71364. attr_accessor :notifier
  71365. def publish(name, *args)
  71366. notifier.publish(name, *args)
  71367. end
  71368. def instrument(name, payload = {})
  71369. if notifier.listening?(name)
  71370. instrumenter.instrument(name, payload) { yield payload if block_given? }
  71371. else
  71372. yield payload if block_given?
  71373. end
  71374. end
  71375. def subscribe(*args, &block)
  71376. notifier.subscribe(*args, &block)
  71377. end
  71378. def subscribed(callback, *args, &block)
  71379. subscriber = subscribe(*args, &callback)
  71380. yield
  71381. ensure
  71382. unsubscribe(subscriber)
  71383. end
  71384. def unsubscribe(args)
  71385. notifier.unsubscribe(args)
  71386. end
  71387. def instrumenter
  71388. Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier)
  71389. end
  71390. end
  71391. self.notifier = Fanout.new
  71392. end
  71393. end
  71394. require 'active_support/core_ext/big_decimal/conversions'
  71395. require 'active_support/core_ext/object/blank'
  71396. require 'active_support/core_ext/hash/keys'
  71397. require 'active_support/i18n'
  71398. module ActiveSupport
  71399. module NumberHelper
  71400. extend self
  71401. DEFAULTS = {
  71402. # Used in number_to_delimited
  71403. # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
  71404. format: {
  71405. # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
  71406. separator: ".",
  71407. # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
  71408. delimiter: ",",
  71409. # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
  71410. precision: 3,
  71411. # If set to true, precision will mean the number of significant digits instead
  71412. # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
  71413. significant: false,
  71414. # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
  71415. strip_insignificant_zeros: false
  71416. },
  71417. # Used in number_to_currency
  71418. currency: {
  71419. format: {
  71420. format: "%u%n",
  71421. negative_format: "-%u%n",
  71422. unit: "$",
  71423. # These five are to override number.format and are optional
  71424. separator: ".",
  71425. delimiter: ",",
  71426. precision: 2,
  71427. significant: false,
  71428. strip_insignificant_zeros: false
  71429. }
  71430. },
  71431. # Used in number_to_percentage
  71432. percentage: {
  71433. format: {
  71434. delimiter: "",
  71435. format: "%n%"
  71436. }
  71437. },
  71438. # Used in number_to_rounded
  71439. precision: {
  71440. format: {
  71441. delimiter: ""
  71442. }
  71443. },
  71444. # Used in number_to_human_size and number_to_human
  71445. human: {
  71446. format: {
  71447. # These five are to override number.format and are optional
  71448. delimiter: "",
  71449. precision: 3,
  71450. significant: true,
  71451. strip_insignificant_zeros: true
  71452. },
  71453. # Used in number_to_human_size
  71454. storage_units: {
  71455. # Storage units output formatting.
  71456. # %u is the storage unit, %n is the number (default: 2 MB)
  71457. format: "%n %u",
  71458. units: {
  71459. byte: "Bytes",
  71460. kb: "KB",
  71461. mb: "MB",
  71462. gb: "GB",
  71463. tb: "TB"
  71464. }
  71465. },
  71466. # Used in number_to_human
  71467. decimal_units: {
  71468. format: "%n %u",
  71469. # Decimal units output formatting
  71470. # By default we will only quantify some of the exponents
  71471. # but the commented ones might be defined or overridden
  71472. # by the user.
  71473. units: {
  71474. # femto: Quadrillionth
  71475. # pico: Trillionth
  71476. # nano: Billionth
  71477. # micro: Millionth
  71478. # mili: Thousandth
  71479. # centi: Hundredth
  71480. # deci: Tenth
  71481. unit: "",
  71482. # ten:
  71483. # one: Ten
  71484. # other: Tens
  71485. # hundred: Hundred
  71486. thousand: "Thousand",
  71487. million: "Million",
  71488. billion: "Billion",
  71489. trillion: "Trillion",
  71490. quadrillion: "Quadrillion"
  71491. }
  71492. }
  71493. }
  71494. }
  71495. DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
  71496. -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
  71497. STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
  71498. # Formats a +number+ into a US phone number (e.g., (555)
  71499. # 123-9876). You can customize the format in the +options+ hash.
  71500. #
  71501. # ==== Options
  71502. #
  71503. # * <tt>:area_code</tt> - Adds parentheses around the area code.
  71504. # * <tt>:delimiter</tt> - Specifies the delimiter to use
  71505. # (defaults to "-").
  71506. # * <tt>:extension</tt> - Specifies an extension to add to the
  71507. # end of the generated number.
  71508. # * <tt>:country_code</tt> - Sets the country code for the phone
  71509. # number.
  71510. # ==== Examples
  71511. #
  71512. # number_to_phone(5551234) # => 555-1234
  71513. # number_to_phone('5551234') # => 555-1234
  71514. # number_to_phone(1235551234) # => 123-555-1234
  71515. # number_to_phone(1235551234, area_code: true) # => (123) 555-1234
  71516. # number_to_phone(1235551234, delimiter: ' ') # => 123 555 1234
  71517. # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
  71518. # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
  71519. # number_to_phone('123a456') # => 123a456
  71520. #
  71521. # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
  71522. # # => +1.123.555.1234 x 1343
  71523. def number_to_phone(number, options = {})
  71524. return unless number
  71525. options = options.symbolize_keys
  71526. number = number.to_s.strip
  71527. area_code = options[:area_code]
  71528. delimiter = options[:delimiter] || "-"
  71529. extension = options[:extension]
  71530. country_code = options[:country_code]
  71531. if area_code
  71532. number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
  71533. else
  71534. number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
  71535. number.slice!(0, 1) if number.start_with?(delimiter) && !delimiter.blank?
  71536. end
  71537. str = ''
  71538. str << "+#{country_code}#{delimiter}" unless country_code.blank?
  71539. str << number
  71540. str << " x #{extension}" unless extension.blank?
  71541. str
  71542. end
  71543. # Formats a +number+ into a currency string (e.g., $13.65). You
  71544. # can customize the format in the +options+ hash.
  71545. #
  71546. # ==== Options
  71547. #
  71548. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  71549. # (defaults to current locale).
  71550. # * <tt>:precision</tt> - Sets the level of precision (defaults
  71551. # to 2).
  71552. # * <tt>:unit</tt> - Sets the denomination of the currency
  71553. # (defaults to "$").
  71554. # * <tt>:separator</tt> - Sets the separator between the units
  71555. # (defaults to ".").
  71556. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  71557. # to ",").
  71558. # * <tt>:format</tt> - Sets the format for non-negative numbers
  71559. # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
  71560. # currency, and <tt>%n</tt> for the number.
  71561. # * <tt>:negative_format</tt> - Sets the format for negative
  71562. # numbers (defaults to prepending an hyphen to the formatted
  71563. # number given by <tt>:format</tt>). Accepts the same fields
  71564. # than <tt>:format</tt>, except <tt>%n</tt> is here the
  71565. # absolute value of the number.
  71566. #
  71567. # ==== Examples
  71568. #
  71569. # number_to_currency(1234567890.50) # => $1,234,567,890.50
  71570. # number_to_currency(1234567890.506) # => $1,234,567,890.51
  71571. # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
  71572. # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51
  71573. # number_to_currency('123a456') # => $123a456
  71574. #
  71575. # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
  71576. # # => ($1,234,567,890.50)
  71577. # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '')
  71578. # # => &pound;1234567890,50
  71579. # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
  71580. # # => 1234567890,50 &pound;
  71581. def number_to_currency(number, options = {})
  71582. return unless number
  71583. options = options.symbolize_keys
  71584. currency = i18n_format_options(options[:locale], :currency)
  71585. currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
  71586. defaults = default_format_options(:currency).merge!(currency)
  71587. defaults[:negative_format] = "-" + options[:format] if options[:format]
  71588. options = defaults.merge!(options)
  71589. unit = options.delete(:unit)
  71590. format = options.delete(:format)
  71591. if number.to_f.phase != 0
  71592. format = options.delete(:negative_format)
  71593. number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
  71594. end
  71595. format.gsub('%n', self.number_to_rounded(number, options)).gsub('%u', unit)
  71596. end
  71597. # Formats a +number+ as a percentage string (e.g., 65%). You can
  71598. # customize the format in the +options+ hash.
  71599. #
  71600. # ==== Options
  71601. #
  71602. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  71603. # (defaults to current locale).
  71604. # * <tt>:precision</tt> - Sets the precision of the number
  71605. # (defaults to 3).
  71606. # * <tt>:significant</tt> - If +true+, precision will be the #
  71607. # of significant_digits. If +false+, the # of fractional
  71608. # digits (defaults to +false+).
  71609. # * <tt>:separator</tt> - Sets the separator between the
  71610. # fractional and integer digits (defaults to ".").
  71611. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  71612. # to "").
  71613. # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
  71614. # insignificant zeros after the decimal separator (defaults to
  71615. # +false+).
  71616. # * <tt>:format</tt> - Specifies the format of the percentage
  71617. # string The number field is <tt>%n</tt> (defaults to "%n%").
  71618. #
  71619. # ==== Examples
  71620. #
  71621. # number_to_percentage(100) # => 100.000%
  71622. # number_to_percentage('98') # => 98.000%
  71623. # number_to_percentage(100, precision: 0) # => 100%
  71624. # number_to_percentage(1000, delimiter: '.', separator: ,') # => 1.000,000%
  71625. # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
  71626. # number_to_percentage(1000, locale: :fr) # => 1 000,000%
  71627. # number_to_percentage('98a') # => 98a%
  71628. # number_to_percentage(100, format: '%n %') # => 100 %
  71629. def number_to_percentage(number, options = {})
  71630. return unless number
  71631. options = options.symbolize_keys
  71632. defaults = format_options(options[:locale], :percentage)
  71633. options = defaults.merge!(options)
  71634. format = options[:format] || "%n%"
  71635. format.gsub('%n', self.number_to_rounded(number, options))
  71636. end
  71637. # Formats a +number+ with grouped thousands using +delimiter+
  71638. # (e.g., 12,324). You can customize the format in the +options+
  71639. # hash.
  71640. #
  71641. # ==== Options
  71642. #
  71643. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  71644. # (defaults to current locale).
  71645. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  71646. # to ",").
  71647. # * <tt>:separator</tt> - Sets the separator between the
  71648. # fractional and integer digits (defaults to ".").
  71649. #
  71650. # ==== Examples
  71651. #
  71652. # number_to_delimited(12345678) # => 12,345,678
  71653. # number_to_delimited('123456') # => 123,456
  71654. # number_to_delimited(12345678.05) # => 12,345,678.05
  71655. # number_to_delimited(12345678, delimiter: '.') # => 12.345.678
  71656. # number_to_delimited(12345678, delimiter: ',') # => 12,345,678
  71657. # number_to_delimited(12345678.05, separator: ' ') # => 12,345,678 05
  71658. # number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05
  71659. # number_to_delimited('112a') # => 112a
  71660. # number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
  71661. # # => 98 765 432,98
  71662. def number_to_delimited(number, options = {})
  71663. options = options.symbolize_keys
  71664. return number unless valid_float?(number)
  71665. options = format_options(options[:locale]).merge!(options)
  71666. parts = number.to_s.to_str.split('.')
  71667. parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
  71668. parts.join(options[:separator])
  71669. end
  71670. # Formats a +number+ with the specified level of
  71671. # <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
  71672. # +:significant+ is +false+, and 5 if +:significant+ is +true+).
  71673. # You can customize the format in the +options+ hash.
  71674. #
  71675. # ==== Options
  71676. #
  71677. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  71678. # (defaults to current locale).
  71679. # * <tt>:precision</tt> - Sets the precision of the number
  71680. # (defaults to 3).
  71681. # * <tt>:significant</tt> - If +true+, precision will be the #
  71682. # of significant_digits. If +false+, the # of fractional
  71683. # digits (defaults to +false+).
  71684. # * <tt>:separator</tt> - Sets the separator between the
  71685. # fractional and integer digits (defaults to ".").
  71686. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  71687. # to "").
  71688. # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
  71689. # insignificant zeros after the decimal separator (defaults to
  71690. # +false+).
  71691. #
  71692. # ==== Examples
  71693. #
  71694. # number_to_rounded(111.2345) # => 111.235
  71695. # number_to_rounded(111.2345, precision: 2) # => 111.23
  71696. # number_to_rounded(13, precision: 5) # => 13.00000
  71697. # number_to_rounded(389.32314, precision: 0) # => 389
  71698. # number_to_rounded(111.2345, significant: true) # => 111
  71699. # number_to_rounded(111.2345, precision: 1, significant: true) # => 100
  71700. # number_to_rounded(13, precision: 5, significant: true) # => 13.000
  71701. # number_to_rounded(111.234, locale: :fr) # => 111,234
  71702. #
  71703. # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
  71704. # # => 13
  71705. #
  71706. # number_to_rounded(389.32314, precision: 4, significant: true) # => 389.3
  71707. # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
  71708. # # => 1.111,23
  71709. def number_to_rounded(number, options = {})
  71710. return number unless valid_float?(number)
  71711. number = Float(number)
  71712. options = options.symbolize_keys
  71713. defaults = format_options(options[:locale], :precision)
  71714. options = defaults.merge!(options)
  71715. precision = options.delete :precision
  71716. significant = options.delete :significant
  71717. strip_insignificant_zeros = options.delete :strip_insignificant_zeros
  71718. if significant && precision > 0
  71719. if number == 0
  71720. digits, rounded_number = 1, 0
  71721. else
  71722. digits = (Math.log10(number.abs) + 1).floor
  71723. rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
  71724. digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
  71725. end
  71726. precision -= digits
  71727. precision = 0 if precision < 0 # don't let it be negative
  71728. else
  71729. rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
  71730. rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
  71731. end
  71732. formatted_number = self.number_to_delimited("%01.#{precision}f" % rounded_number, options)
  71733. if strip_insignificant_zeros
  71734. escaped_separator = Regexp.escape(options[:separator])
  71735. formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
  71736. else
  71737. formatted_number
  71738. end
  71739. end
  71740. # Formats the bytes in +number+ into a more understandable
  71741. # representation (e.g., giving it 1500 yields 1.5 KB). This
  71742. # method is useful for reporting file sizes to users. You can
  71743. # customize the format in the +options+ hash.
  71744. #
  71745. # See <tt>number_to_human</tt> if you want to pretty-print a
  71746. # generic number.
  71747. #
  71748. # ==== Options
  71749. #
  71750. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  71751. # (defaults to current locale).
  71752. # * <tt>:precision</tt> - Sets the precision of the number
  71753. # (defaults to 3).
  71754. # * <tt>:significant</tt> - If +true+, precision will be the #
  71755. # of significant_digits. If +false+, the # of fractional
  71756. # digits (defaults to +true+)
  71757. # * <tt>:separator</tt> - Sets the separator between the
  71758. # fractional and integer digits (defaults to ".").
  71759. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  71760. # to "").
  71761. # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
  71762. # insignificant zeros after the decimal separator (defaults to
  71763. # +true+)
  71764. # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
  71765. # prefix (defaults to :binary)
  71766. #
  71767. # ==== Examples
  71768. #
  71769. # number_to_human_size(123) # => 123 Bytes
  71770. # number_to_human_size(1234) # => 1.21 KB
  71771. # number_to_human_size(12345) # => 12.1 KB
  71772. # number_to_human_size(1234567) # => 1.18 MB
  71773. # number_to_human_size(1234567890) # => 1.15 GB
  71774. # number_to_human_size(1234567890123) # => 1.12 TB
  71775. # number_to_human_size(1234567, precision: 2) # => 1.2 MB
  71776. # number_to_human_size(483989, precision: 2) # => 470 KB
  71777. # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
  71778. #
  71779. # Non-significant zeros after the fractional separator are stripped out by
  71780. # default (set <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
  71781. #
  71782. # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
  71783. # number_to_human_size(524288000, precision: 5) # => "500 MB"
  71784. def number_to_human_size(number, options = {})
  71785. options = options.symbolize_keys
  71786. return number unless valid_float?(number)
  71787. number = Float(number)
  71788. defaults = format_options(options[:locale], :human)
  71789. options = defaults.merge!(options)
  71790. #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
  71791. options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
  71792. storage_units_format = translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
  71793. base = options[:prefix] == :si ? 1000 : 1024
  71794. if number.to_i < base
  71795. unit = translate_number_value_with_default('human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
  71796. storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
  71797. else
  71798. max_exp = STORAGE_UNITS.size - 1
  71799. exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
  71800. exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
  71801. number /= base ** exponent
  71802. unit_key = STORAGE_UNITS[exponent]
  71803. unit = translate_number_value_with_default("human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
  71804. formatted_number = self.number_to_rounded(number, options)
  71805. storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
  71806. end
  71807. end
  71808. # Pretty prints (formats and approximates) a number in a way it
  71809. # is more readable by humans (eg.: 1200000000 becomes "1.2
  71810. # Billion"). This is useful for numbers that can get very large
  71811. # (and too hard to read).
  71812. #
  71813. # See <tt>number_to_human_size</tt> if you want to print a file
  71814. # size.
  71815. #
  71816. # You can also define you own unit-quantifier names if you want
  71817. # to use other decimal units (eg.: 1500 becomes "1.5
  71818. # kilometers", 0.150 becomes "150 milliliters", etc). You may
  71819. # define a wide range of unit quantifiers, even fractional ones
  71820. # (centi, deci, mili, etc).
  71821. #
  71822. # ==== Options
  71823. #
  71824. # * <tt>:locale</tt> - Sets the locale to be used for formatting
  71825. # (defaults to current locale).
  71826. # * <tt>:precision</tt> - Sets the precision of the number
  71827. # (defaults to 3).
  71828. # * <tt>:significant</tt> - If +true+, precision will be the #
  71829. # of significant_digits. If +false+, the # of fractional
  71830. # digits (defaults to +true+)
  71831. # * <tt>:separator</tt> - Sets the separator between the
  71832. # fractional and integer digits (defaults to ".").
  71833. # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
  71834. # to "").
  71835. # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
  71836. # insignificant zeros after the decimal separator (defaults to
  71837. # +true+)
  71838. # * <tt>:units</tt> - A Hash of unit quantifier names. Or a
  71839. # string containing an i18n scope where to find this hash. It
  71840. # might have the following keys:
  71841. # * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
  71842. # *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
  71843. # *<tt>:billion</tt>, <tt>:trillion</tt>,
  71844. # *<tt>:quadrillion</tt>
  71845. # * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
  71846. # *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
  71847. # *<tt>:pico</tt>, <tt>:femto</tt>
  71848. # * <tt>:format</tt> - Sets the format of the output string
  71849. # (defaults to "%n %u"). The field types are:
  71850. # * %u - The quantifier (ex.: 'thousand')
  71851. # * %n - The number
  71852. #
  71853. # ==== Examples
  71854. #
  71855. # number_to_human(123) # => "123"
  71856. # number_to_human(1234) # => "1.23 Thousand"
  71857. # number_to_human(12345) # => "12.3 Thousand"
  71858. # number_to_human(1234567) # => "1.23 Million"
  71859. # number_to_human(1234567890) # => "1.23 Billion"
  71860. # number_to_human(1234567890123) # => "1.23 Trillion"
  71861. # number_to_human(1234567890123456) # => "1.23 Quadrillion"
  71862. # number_to_human(1234567890123456789) # => "1230 Quadrillion"
  71863. # number_to_human(489939, precision: 2) # => "490 Thousand"
  71864. # number_to_human(489939, precision: 4) # => "489.9 Thousand"
  71865. # number_to_human(1234567, precision: 4,
  71866. # significant: false) # => "1.2346 Million"
  71867. # number_to_human(1234567, precision: 1,
  71868. # separator: ',',
  71869. # significant: false) # => "1,2 Million"
  71870. #
  71871. # Non-significant zeros after the decimal separator are stripped
  71872. # out by default (set <tt>:strip_insignificant_zeros</tt> to
  71873. # +false+ to change that):
  71874. #
  71875. # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
  71876. # number_to_human(500000000, precision: 5) # => "500 Million"
  71877. #
  71878. # ==== Custom Unit Quantifiers
  71879. #
  71880. # You can also use your own custom unit quantifiers:
  71881. # number_to_human(500000, units: { unit: 'ml', thousand: 'lt' }) # => "500 lt"
  71882. #
  71883. # If in your I18n locale you have:
  71884. #
  71885. # distance:
  71886. # centi:
  71887. # one: "centimeter"
  71888. # other: "centimeters"
  71889. # unit:
  71890. # one: "meter"
  71891. # other: "meters"
  71892. # thousand:
  71893. # one: "kilometer"
  71894. # other: "kilometers"
  71895. # billion: "gazillion-distance"
  71896. #
  71897. # Then you could do:
  71898. #
  71899. # number_to_human(543934, units: :distance) # => "544 kilometers"
  71900. # number_to_human(54393498, units: :distance) # => "54400 kilometers"
  71901. # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
  71902. # number_to_human(343, units: :distance, precision: 1) # => "300 meters"
  71903. # number_to_human(1, units: :distance) # => "1 meter"
  71904. # number_to_human(0.34, units: :distance) # => "34 centimeters"
  71905. def number_to_human(number, options = {})
  71906. options = options.symbolize_keys
  71907. return number unless valid_float?(number)
  71908. number = Float(number)
  71909. defaults = format_options(options[:locale], :human)
  71910. options = defaults.merge!(options)
  71911. #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
  71912. options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
  71913. inverted_du = DECIMAL_UNITS.invert
  71914. units = options.delete :units
  71915. unit_exponents = case units
  71916. when Hash
  71917. units
  71918. when String, Symbol
  71919. I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
  71920. when nil
  71921. translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :raise => true)
  71922. else
  71923. raise ArgumentError, ":units must be a Hash or String translation scope."
  71924. end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
  71925. number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
  71926. display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
  71927. number /= 10 ** display_exponent
  71928. unit = case units
  71929. when Hash
  71930. units[DECIMAL_UNITS[display_exponent]]
  71931. when String, Symbol
  71932. I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
  71933. else
  71934. translate_number_value_with_default("human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
  71935. end
  71936. decimal_format = options[:format] || translate_number_value_with_default('human.decimal_units.format', :locale => options[:locale])
  71937. formatted_number = self.number_to_rounded(number, options)
  71938. decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip
  71939. end
  71940. def self.private_module_and_instance_method(method_name) #:nodoc:
  71941. private method_name
  71942. private_class_method method_name
  71943. end
  71944. private_class_method :private_module_and_instance_method
  71945. def format_options(locale, namespace = nil) #:nodoc:
  71946. default_format_options(namespace).merge!(i18n_format_options(locale, namespace))
  71947. end
  71948. private_module_and_instance_method :format_options
  71949. def default_format_options(namespace = nil) #:nodoc:
  71950. options = DEFAULTS[:format].dup
  71951. options.merge!(DEFAULTS[namespace][:format]) if namespace
  71952. options
  71953. end
  71954. private_module_and_instance_method :default_format_options
  71955. def i18n_format_options(locale, namespace = nil) #:nodoc:
  71956. options = I18n.translate(:'number.format', locale: locale, default: {}).dup
  71957. if namespace
  71958. options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
  71959. end
  71960. options
  71961. end
  71962. private_module_and_instance_method :i18n_format_options
  71963. def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
  71964. default = key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
  71965. I18n.translate(key, { default: default, scope: :number }.merge!(i18n_options))
  71966. end
  71967. private_module_and_instance_method :translate_number_value_with_default
  71968. def valid_float?(number) #:nodoc:
  71969. Float(number)
  71970. rescue ArgumentError, TypeError
  71971. false
  71972. end
  71973. private_module_and_instance_method :valid_float?
  71974. end
  71975. end
  71976. require 'active_support/core_ext/hash/deep_merge'
  71977. module ActiveSupport
  71978. class OptionMerger #:nodoc:
  71979. instance_methods.each do |method|
  71980. undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
  71981. end
  71982. def initialize(context, options)
  71983. @context, @options = context, options
  71984. end
  71985. private
  71986. def method_missing(method, *arguments, &block)
  71987. if arguments.last.is_a?(Proc)
  71988. proc = arguments.pop
  71989. arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
  71990. else
  71991. arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
  71992. end
  71993. @context.__send__(method, *arguments, &block)
  71994. end
  71995. end
  71996. end
  71997. require 'yaml'
  71998. YAML.add_builtin_type("omap") do |type, val|
  71999. ActiveSupport::OrderedHash[val.map{ |v| v.to_a.first }]
  72000. end
  72001. module ActiveSupport
  72002. # <tt>ActiveSupport::OrderedHash</tt> implements a hash that preserves
  72003. # insertion order.
  72004. #
  72005. # oh = ActiveSupport::OrderedHash.new
  72006. # oh[:a] = 1
  72007. # oh[:b] = 2
  72008. # oh.keys # => [:a, :b], this order is guaranteed
  72009. #
  72010. # Also, maps the +omap+ feature for YAML files
  72011. # (See http://yaml.org/type/omap.html) to support ordered items
  72012. # when loading from yaml.
  72013. #
  72014. # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
  72015. # with other implementations.
  72016. class OrderedHash < ::Hash
  72017. def to_yaml_type
  72018. "!tag:yaml.org,2002:omap"
  72019. end
  72020. def encode_with(coder)
  72021. coder.represent_seq '!omap', map { |k,v| { k => v } }
  72022. end
  72023. def nested_under_indifferent_access
  72024. self
  72025. end
  72026. # Returns true to make sure that this hash is extractable via <tt>Array#extract_options!</tt>
  72027. def extractable_options?
  72028. true
  72029. end
  72030. end
  72031. end
  72032. module ActiveSupport
  72033. # Usually key value pairs are handled something like this:
  72034. #
  72035. # h = {}
  72036. # h[:boy] = 'John'
  72037. # h[:girl] = 'Mary'
  72038. # h[:boy] # => 'John'
  72039. # h[:girl] # => 'Mary'
  72040. #
  72041. # Using +OrderedOptions+, the above code could be reduced to:
  72042. #
  72043. # h = ActiveSupport::OrderedOptions.new
  72044. # h.boy = 'John'
  72045. # h.girl = 'Mary'
  72046. # h.boy # => 'John'
  72047. # h.girl # => 'Mary'
  72048. class OrderedOptions < Hash
  72049. alias_method :_get, :[] # preserve the original #[] method
  72050. protected :_get # make it protected
  72051. def []=(key, value)
  72052. super(key.to_sym, value)
  72053. end
  72054. def [](key)
  72055. super(key.to_sym)
  72056. end
  72057. def method_missing(name, *args)
  72058. name_string = name.to_s
  72059. if name_string.chomp!('=')
  72060. self[name_string] = args.first
  72061. else
  72062. self[name]
  72063. end
  72064. end
  72065. def respond_to_missing?(name, include_private)
  72066. true
  72067. end
  72068. end
  72069. class InheritableOptions < OrderedOptions
  72070. def initialize(parent = nil)
  72071. if parent.kind_of?(OrderedOptions)
  72072. # use the faster _get when dealing with OrderedOptions
  72073. super() { |h,k| parent._get(k) }
  72074. elsif parent
  72075. super() { |h,k| parent[k] }
  72076. else
  72077. super()
  72078. end
  72079. end
  72080. def inheritable_copy
  72081. self.class.new(self)
  72082. end
  72083. end
  72084. end
  72085. module ActiveSupport
  72086. # A class with no predefined methods that behaves similarly to Builder's
  72087. # BlankSlate. Used for proxy classes.
  72088. class ProxyObject < ::BasicObject
  72089. undef_method :==
  72090. undef_method :equal?
  72091. # Let ActiveSupport::ProxyObject at least raise exceptions.
  72092. def raise(*args)
  72093. ::Object.send(:raise, *args)
  72094. end
  72095. end
  72096. end
  72097. # This is private interface.
  72098. #
  72099. # Rails components cherry pick from Active Support as needed, but there are a
  72100. # few features that are used for sure some way or another and it is not worth
  72101. # to put individual requires absolutely everywhere. Think blank? for example.
  72102. #
  72103. # This file is loaded by every Rails component except Active Support itself,
  72104. # but it does not belong to the Rails public interface. It is internal to
  72105. # Rails and can change anytime.
  72106. # Defines Object#blank? and Object#present?.
  72107. require 'active_support/core_ext/object/blank'
  72108. # Rails own autoload, eager_load, etc.
  72109. require 'active_support/dependencies/autoload'
  72110. # Support for ClassMethods and the included macro.
  72111. require 'active_support/concern'
  72112. # Defines Class#class_attribute.
  72113. require 'active_support/core_ext/class/attribute'
  72114. # Defines Module#delegate.
  72115. require 'active_support/core_ext/module/delegation'
  72116. # Defines ActiveSupport::Deprecation.
  72117. require 'active_support/deprecation'
  72118. require "active_support"
  72119. require "active_support/i18n_railtie"
  72120. module ActiveSupport
  72121. class Railtie < Rails::Railtie # :nodoc:
  72122. config.active_support = ActiveSupport::OrderedOptions.new
  72123. config.eager_load_namespaces << ActiveSupport
  72124. initializer "active_support.deprecation_behavior" do |app|
  72125. if deprecation = app.config.active_support.deprecation
  72126. ActiveSupport::Deprecation.behavior = deprecation
  72127. end
  72128. end
  72129. # Sets the default value for Time.zone
  72130. # If assigned value cannot be matched to a TimeZone, an exception will be raised.
  72131. initializer "active_support.initialize_time_zone" do |app|
  72132. require 'active_support/core_ext/time/zones'
  72133. zone_default = Time.find_zone!(app.config.time_zone)
  72134. unless zone_default
  72135. raise 'Value assigned to config.time_zone not recognized. ' \
  72136. 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.'
  72137. end
  72138. Time.zone_default = zone_default
  72139. end
  72140. # Sets the default week start
  72141. # If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
  72142. initializer "active_support.initialize_beginning_of_week" do |app|
  72143. require 'active_support/core_ext/date/calculations'
  72144. beginning_of_week_default = Date.find_beginning_of_week!(app.config.beginning_of_week)
  72145. Date.beginning_of_week_default = beginning_of_week_default
  72146. end
  72147. initializer "active_support.set_configs" do |app|
  72148. app.config.active_support.each do |k, v|
  72149. k = "#{k}="
  72150. ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
  72151. end
  72152. end
  72153. end
  72154. end
  72155. require 'active_support/concern'
  72156. require 'active_support/core_ext/class/attribute'
  72157. require 'active_support/core_ext/proc'
  72158. require 'active_support/core_ext/string/inflections'
  72159. require 'active_support/core_ext/array/extract_options'
  72160. module ActiveSupport
  72161. # Rescuable module adds support for easier exception handling.
  72162. module Rescuable
  72163. extend Concern
  72164. included do
  72165. class_attribute :rescue_handlers
  72166. self.rescue_handlers = []
  72167. end
  72168. module ClassMethods
  72169. # Rescue exceptions raised in controller actions.
  72170. #
  72171. # <tt>rescue_from</tt> receives a series of exception classes or class
  72172. # names, and a trailing <tt>:with</tt> option with the name of a method
  72173. # or a Proc object to be called to handle them. Alternatively a block can
  72174. # be given.
  72175. #
  72176. # Handlers that take one argument will be called with the exception, so
  72177. # that the exception can be inspected when dealing with it.
  72178. #
  72179. # Handlers are inherited. They are searched from right to left, from
  72180. # bottom to top, and up the hierarchy. The handler of the first class for
  72181. # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
  72182. # any.
  72183. #
  72184. # class ApplicationController < ActionController::Base
  72185. # rescue_from User::NotAuthorized, with: :deny_access # self defined exception
  72186. # rescue_from ActiveRecord::RecordInvalid, with: :show_errors
  72187. #
  72188. # rescue_from 'MyAppError::Base' do |exception|
  72189. # render xml: exception, status: 500
  72190. # end
  72191. #
  72192. # protected
  72193. # def deny_access
  72194. # ...
  72195. # end
  72196. #
  72197. # def show_errors(exception)
  72198. # exception.record.new_record? ? ...
  72199. # end
  72200. # end
  72201. #
  72202. # Exceptions raised inside exception handlers are not propagated up.
  72203. def rescue_from(*klasses, &block)
  72204. options = klasses.extract_options!
  72205. unless options.has_key?(:with)
  72206. if block_given?
  72207. options[:with] = block
  72208. else
  72209. raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
  72210. end
  72211. end
  72212. klasses.each do |klass|
  72213. key = if klass.is_a?(Class) && klass <= Exception
  72214. klass.name
  72215. elsif klass.is_a?(String)
  72216. klass
  72217. else
  72218. raise ArgumentError, "#{klass} is neither an Exception nor a String"
  72219. end
  72220. # put the new handler at the end because the list is read in reverse
  72221. self.rescue_handlers += [[key, options[:with]]]
  72222. end
  72223. end
  72224. end
  72225. # Tries to rescue the exception by looking up and calling a registered handler.
  72226. def rescue_with_handler(exception)
  72227. if handler = handler_for_rescue(exception)
  72228. handler.arity != 0 ? handler.call(exception) : handler.call
  72229. true # don't rely on the return value of the handler
  72230. end
  72231. end
  72232. def handler_for_rescue(exception)
  72233. # We go from right to left because pairs are pushed onto rescue_handlers
  72234. # as rescue_from declarations are found.
  72235. _, rescuer = self.class.rescue_handlers.reverse.detect do |klass_name, handler|
  72236. # The purpose of allowing strings in rescue_from is to support the
  72237. # declaration of handler associations for exception classes whose
  72238. # definition is yet unknown.
  72239. #
  72240. # Since this loop needs the constants it would be inconsistent to
  72241. # assume they should exist at this point. An early raised exception
  72242. # could trigger some other handler and the array could include
  72243. # precisely a string whose corresponding constant has not yet been
  72244. # seen. This is why we are tolerant to unknown constants.
  72245. #
  72246. # Note that this tolerance only matters if the exception was given as
  72247. # a string, otherwise a NameError will be raised by the interpreter
  72248. # itself when rescue_from CONSTANT is executed.
  72249. klass = self.class.const_get(klass_name) rescue nil
  72250. klass ||= klass_name.constantize rescue nil
  72251. exception.is_a?(klass) if klass
  72252. end
  72253. case rescuer
  72254. when Symbol
  72255. method(rescuer)
  72256. when Proc
  72257. if rescuer.arity == 0
  72258. Proc.new { instance_exec(&rescuer) }
  72259. else
  72260. Proc.new { |_exception| instance_exec(_exception, &rescuer) }
  72261. end
  72262. end
  72263. end
  72264. end
  72265. end
  72266. module ActiveSupport
  72267. # Wrapping a string in this class gives you a prettier way to test
  72268. # for equality. The value returned by <tt>Rails.env</tt> is wrapped
  72269. # in a StringInquirer object so instead of calling this:
  72270. #
  72271. # Rails.env == 'production'
  72272. #
  72273. # you can call this:
  72274. #
  72275. # Rails.env.production?
  72276. class StringInquirer < String
  72277. private
  72278. def respond_to_missing?(method_name, include_private = false)
  72279. method_name[-1] == '?'
  72280. end
  72281. def method_missing(method_name, *arguments)
  72282. if method_name[-1] == '?'
  72283. self == method_name[0..-2]
  72284. else
  72285. super
  72286. end
  72287. end
  72288. end
  72289. end
  72290. require 'active_support/core_ext/object/blank'
  72291. require 'logger'
  72292. require 'active_support/logger'
  72293. module ActiveSupport
  72294. # Wraps any standard Logger object to provide tagging capabilities.
  72295. #
  72296. # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
  72297. # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
  72298. # logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
  72299. # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
  72300. #
  72301. # This is used by the default Rails.logger as configured by Railties to make
  72302. # it easy to stamp log lines with subdomains, request ids, and anything else
  72303. # to aid debugging of multi-user production applications.
  72304. module TaggedLogging
  72305. module Formatter # :nodoc:
  72306. # This method is invoked when a log event occurs.
  72307. def call(severity, timestamp, progname, msg)
  72308. super(severity, timestamp, progname, "#{tags_text}#{msg}")
  72309. end
  72310. def tagged(*tags)
  72311. new_tags = push_tags(*tags)
  72312. yield self
  72313. ensure
  72314. pop_tags(new_tags.size)
  72315. end
  72316. def push_tags(*tags)
  72317. tags.flatten.reject(&:blank?).tap do |new_tags|
  72318. current_tags.concat new_tags
  72319. end
  72320. end
  72321. def pop_tags(size = 1)
  72322. current_tags.pop size
  72323. end
  72324. def clear_tags!
  72325. current_tags.clear
  72326. end
  72327. def current_tags
  72328. Thread.current[:activesupport_tagged_logging_tags] ||= []
  72329. end
  72330. private
  72331. def tags_text
  72332. tags = current_tags
  72333. if tags.any?
  72334. tags.collect { |tag| "[#{tag}] " }.join
  72335. end
  72336. end
  72337. end
  72338. def self.new(logger)
  72339. # Ensure we set a default formatter so we aren't extending nil!
  72340. logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
  72341. logger.formatter.extend Formatter
  72342. logger.extend(self)
  72343. end
  72344. delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
  72345. def tagged(*tags)
  72346. formatter.tagged(*tags) { yield self }
  72347. end
  72348. def flush
  72349. clear_tags!
  72350. super if defined?(super)
  72351. end
  72352. end
  72353. end
  72354. gem 'minitest' # make sure we get the gem, not stdlib
  72355. require 'minitest/unit'
  72356. require 'active_support/testing/tagged_logging'
  72357. require 'active_support/testing/setup_and_teardown'
  72358. require 'active_support/testing/assertions'
  72359. require 'active_support/testing/deprecation'
  72360. require 'active_support/testing/pending'
  72361. require 'active_support/testing/declarative'
  72362. require 'active_support/testing/isolation'
  72363. require 'active_support/testing/constant_lookup'
  72364. require 'active_support/core_ext/kernel/reporting'
  72365. require 'active_support/deprecation'
  72366. begin
  72367. silence_warnings { require 'mocha/setup' }
  72368. rescue LoadError
  72369. end
  72370. module ActiveSupport
  72371. class TestCase < ::MiniTest::Unit::TestCase
  72372. Assertion = MiniTest::Assertion
  72373. alias_method :method_name, :__name__
  72374. $tags = {}
  72375. def self.for_tag(tag)
  72376. yield if $tags[tag]
  72377. end
  72378. # FIXME: we have tests that depend on run order, we should fix that and
  72379. # remove this method.
  72380. def self.test_order # :nodoc:
  72381. :sorted
  72382. end
  72383. include ActiveSupport::Testing::TaggedLogging
  72384. include ActiveSupport::Testing::SetupAndTeardown
  72385. include ActiveSupport::Testing::Assertions
  72386. include ActiveSupport::Testing::Deprecation
  72387. include ActiveSupport::Testing::Pending
  72388. extend ActiveSupport::Testing::Declarative
  72389. # test/unit backwards compatibility methods
  72390. alias :assert_raise :assert_raises
  72391. alias :assert_not_empty :refute_empty
  72392. alias :assert_not_equal :refute_equal
  72393. alias :assert_not_in_delta :refute_in_delta
  72394. alias :assert_not_in_epsilon :refute_in_epsilon
  72395. alias :assert_not_includes :refute_includes
  72396. alias :assert_not_instance_of :refute_instance_of
  72397. alias :assert_not_kind_of :refute_kind_of
  72398. alias :assert_no_match :refute_match
  72399. alias :assert_not_nil :refute_nil
  72400. alias :assert_not_operator :refute_operator
  72401. alias :assert_not_predicate :refute_predicate
  72402. alias :assert_not_respond_to :refute_respond_to
  72403. alias :assert_not_same :refute_same
  72404. # Fails if the block raises an exception.
  72405. #
  72406. # assert_nothing_raised do
  72407. # ...
  72408. # end
  72409. def assert_nothing_raised(*args)
  72410. yield
  72411. end
  72412. end
  72413. end
  72414. require 'active_support/core_ext/object/blank'
  72415. module ActiveSupport
  72416. module Testing
  72417. module Assertions
  72418. # Assert that an expression is not truthy. Passes if <tt>object</tt> is
  72419. # +nil+ or +false+. "Truthy" means "considered true in a conditional"
  72420. # like <tt>if foo</tt>.
  72421. #
  72422. # assert_not nil # => true
  72423. # assert_not false # => true
  72424. # assert_not 'foo' # => 'foo' is not nil or false
  72425. #
  72426. # An error message can be specified.
  72427. #
  72428. # assert_not foo, 'foo should be false'
  72429. def assert_not(object, message = nil)
  72430. message ||= "Expected #{mu_pp(object)} to be nil or false"
  72431. assert !object, message
  72432. end
  72433. # Test numeric difference between the return value of an expression as a
  72434. # result of what is evaluated in the yielded block.
  72435. #
  72436. # assert_difference 'Article.count' do
  72437. # post :create, article: {...}
  72438. # end
  72439. #
  72440. # An arbitrary expression is passed in and evaluated.
  72441. #
  72442. # assert_difference 'assigns(:article).comments(:reload).size' do
  72443. # post :create, comment: {...}
  72444. # end
  72445. #
  72446. # An arbitrary positive or negative difference can be specified.
  72447. # The default is <tt>1</tt>.
  72448. #
  72449. # assert_difference 'Article.count', -1 do
  72450. # post :delete, id: ...
  72451. # end
  72452. #
  72453. # An array of expressions can also be passed in and evaluated.
  72454. #
  72455. # assert_difference [ 'Article.count', 'Post.count' ], 2 do
  72456. # post :create, article: {...}
  72457. # end
  72458. #
  72459. # A lambda or a list of lambdas can be passed in and evaluated:
  72460. #
  72461. # assert_difference ->{ Article.count }, 2 do
  72462. # post :create, article: {...}
  72463. # end
  72464. #
  72465. # assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
  72466. # post :create, article: {...}
  72467. # end
  72468. #
  72469. # An error message can be specified.
  72470. #
  72471. # assert_difference 'Article.count', -1, 'An Article should be destroyed' do
  72472. # post :delete, id: ...
  72473. # end
  72474. def assert_difference(expression, difference = 1, message = nil, &block)
  72475. expressions = Array(expression)
  72476. exps = expressions.map { |e|
  72477. e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
  72478. }
  72479. before = exps.map { |e| e.call }
  72480. yield
  72481. expressions.zip(exps).each_with_index do |(code, e), i|
  72482. error = "#{code.inspect} didn't change by #{difference}"
  72483. error = "#{message}.\n#{error}" if message
  72484. assert_equal(before[i] + difference, e.call, error)
  72485. end
  72486. end
  72487. # Assertion that the numeric result of evaluating an expression is not
  72488. # changed before and after invoking the passed in block.
  72489. #
  72490. # assert_no_difference 'Article.count' do
  72491. # post :create, article: invalid_attributes
  72492. # end
  72493. #
  72494. # An error message can be specified.
  72495. #
  72496. # assert_no_difference 'Article.count', 'An Article should not be created' do
  72497. # post :create, article: invalid_attributes
  72498. # end
  72499. def assert_no_difference(expression, message = nil, &block)
  72500. assert_difference expression, 0, message, &block
  72501. end
  72502. # Test if an expression is blank. Passes if <tt>object.blank?</tt>
  72503. # is +true+.
  72504. #
  72505. # assert_blank [] # => true
  72506. # assert_blank [[]] # => [[]] is not blank
  72507. #
  72508. # An error message can be specified.
  72509. #
  72510. # assert_blank [], 'this should be blank'
  72511. def assert_blank(object, message=nil)
  72512. ActiveSupport::Deprecation.warn('"assert_blank" is deprecated. Please use "assert object.blank?" instead')
  72513. message ||= "#{object.inspect} is not blank"
  72514. assert object.blank?, message
  72515. end
  72516. # Test if an expression is not blank. Passes if <tt>object.present?</tt>
  72517. # is +true+.
  72518. #
  72519. # assert_present({ data: 'x' }) # => true
  72520. # assert_present({}) # => {} is blank
  72521. #
  72522. # An error message can be specified.
  72523. #
  72524. # assert_present({ data: 'x' }, 'this should not be blank')
  72525. def assert_present(object, message=nil)
  72526. ActiveSupport::Deprecation.warn('"assert_present" is deprecated. Please use "assert object.present?" instead')
  72527. message ||= "#{object.inspect} is blank"
  72528. assert object.present?, message
  72529. end
  72530. end
  72531. end
  72532. end
  72533. gem 'minitest'
  72534. require 'minitest/unit'
  72535. MiniTest::Unit.autorun
  72536. require "active_support/concern"
  72537. require "active_support/inflector"
  72538. module ActiveSupport
  72539. module Testing
  72540. # Resolves a constant from a minitest spec name.
  72541. #
  72542. # Given the following spec-style test:
  72543. #
  72544. # describe WidgetsController, :index do
  72545. # describe "authenticated user" do
  72546. # describe "returns widgets" do
  72547. # it "has a controller that exists" do
  72548. # assert_kind_of WidgetsController, @controller
  72549. # end
  72550. # end
  72551. # end
  72552. # end
  72553. #
  72554. # The test will have the following name:
  72555. #
  72556. # "WidgetsController::index::authenticated user::returns widgets"
  72557. #
  72558. # The constant WidgetsController can be resolved from the name.
  72559. # The following code will resolve the constant:
  72560. #
  72561. # controller = determine_constant_from_test_name(name) do |constant|
  72562. # Class === constant && constant < ::ActionController::Metal
  72563. # end
  72564. module ConstantLookup
  72565. extend ::ActiveSupport::Concern
  72566. module ClassMethods # :nodoc:
  72567. def determine_constant_from_test_name(test_name)
  72568. names = test_name.split "::"
  72569. while names.size > 0 do
  72570. names.last.sub!(/Test$/, "")
  72571. begin
  72572. constant = names.join("::").constantize
  72573. break(constant) if yield(constant)
  72574. rescue NameError
  72575. # Constant wasn't found, move on
  72576. ensure
  72577. names.pop
  72578. end
  72579. end
  72580. end
  72581. end
  72582. end
  72583. end
  72584. end
  72585. module ActiveSupport
  72586. module Testing
  72587. module Declarative
  72588. def self.extended(klass) #:nodoc:
  72589. klass.class_eval do
  72590. unless method_defined?(:describe)
  72591. def self.describe(text)
  72592. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  72593. def self.name
  72594. "#{text}"
  72595. end
  72596. RUBY_EVAL
  72597. end
  72598. end
  72599. end
  72600. end
  72601. unless defined?(Spec)
  72602. # test "verify something" do
  72603. # ...
  72604. # end
  72605. def test(name, &block)
  72606. test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
  72607. defined = instance_method(test_name) rescue false
  72608. raise "#{test_name} is already defined in #{self}" if defined
  72609. if block_given?
  72610. define_method(test_name, &block)
  72611. else
  72612. define_method(test_name) do
  72613. flunk "No implementation provided for #{name}"
  72614. end
  72615. end
  72616. end
  72617. end
  72618. end
  72619. end
  72620. end
  72621. require 'active_support/deprecation'
  72622. module ActiveSupport
  72623. module Testing
  72624. module Deprecation #:nodoc:
  72625. def assert_deprecated(match = nil, &block)
  72626. result, warnings = collect_deprecations(&block)
  72627. assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
  72628. if match
  72629. match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
  72630. assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
  72631. end
  72632. result
  72633. end
  72634. def assert_not_deprecated(&block)
  72635. result, deprecations = collect_deprecations(&block)
  72636. assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
  72637. result
  72638. end
  72639. private
  72640. def collect_deprecations
  72641. old_behavior = ActiveSupport::Deprecation.behavior
  72642. deprecations = []
  72643. ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
  72644. deprecations << message
  72645. end
  72646. result = yield
  72647. [result, deprecations]
  72648. ensure
  72649. ActiveSupport::Deprecation.behavior = old_behavior
  72650. end
  72651. end
  72652. end
  72653. end
  72654. require 'rbconfig'
  72655. begin
  72656. require 'minitest/parallel_each'
  72657. rescue LoadError
  72658. end
  72659. module ActiveSupport
  72660. module Testing
  72661. class RemoteError < StandardError
  72662. attr_reader :message, :backtrace
  72663. def initialize(exception)
  72664. @message = "caught #{exception.class.name}: #{exception.message}"
  72665. @backtrace = exception.backtrace
  72666. end
  72667. end
  72668. class ProxyTestResult
  72669. def initialize(calls = [])
  72670. @calls = calls
  72671. end
  72672. def add_error(e)
  72673. e = Test::Unit::Error.new(e.test_name, RemoteError.new(e.exception))
  72674. @calls << [:add_error, e]
  72675. end
  72676. def __replay__(result)
  72677. @calls.each do |name, args|
  72678. result.send(name, *args)
  72679. end
  72680. end
  72681. def marshal_dump
  72682. @calls
  72683. end
  72684. def marshal_load(calls)
  72685. initialize(calls)
  72686. end
  72687. def method_missing(name, *args)
  72688. @calls << [name, args]
  72689. end
  72690. end
  72691. module Isolation
  72692. require 'thread'
  72693. # Recent versions of MiniTest (such as the one shipped with Ruby 2.0) already define
  72694. # a ParallelEach class.
  72695. unless defined? ParallelEach
  72696. class ParallelEach
  72697. include Enumerable
  72698. # default to 2 cores
  72699. CORES = (ENV['TEST_CORES'] || 2).to_i
  72700. def initialize list
  72701. @list = list
  72702. @queue = SizedQueue.new CORES
  72703. end
  72704. def grep pattern
  72705. self.class.new super
  72706. end
  72707. def each
  72708. threads = CORES.times.map {
  72709. Thread.new {
  72710. while job = @queue.pop
  72711. yield job
  72712. end
  72713. }
  72714. }
  72715. @list.each { |i| @queue << i }
  72716. CORES.times { @queue << nil }
  72717. threads.each(&:join)
  72718. end
  72719. end
  72720. end
  72721. def self.included(klass) #:nodoc:
  72722. klass.extend(Module.new {
  72723. def test_methods
  72724. ParallelEach.new super
  72725. end
  72726. })
  72727. end
  72728. def self.forking_env?
  72729. !ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
  72730. end
  72731. @@class_setup_mutex = Mutex.new
  72732. def _run_class_setup # class setup method should only happen in parent
  72733. @@class_setup_mutex.synchronize do
  72734. unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
  72735. self.class.setup if self.class.respond_to?(:setup)
  72736. @@ran_class_setup = true
  72737. end
  72738. end
  72739. end
  72740. def run(runner)
  72741. _run_class_setup
  72742. serialized = run_in_isolation do |isolated_runner|
  72743. super(isolated_runner)
  72744. end
  72745. retval, proxy = Marshal.load(serialized)
  72746. proxy.__replay__(runner)
  72747. retval
  72748. end
  72749. module Forking
  72750. def run_in_isolation(&blk)
  72751. read, write = IO.pipe
  72752. pid = fork do
  72753. read.close
  72754. proxy = ProxyTestResult.new
  72755. retval = yield proxy
  72756. write.puts [Marshal.dump([retval, proxy])].pack("m")
  72757. exit!
  72758. end
  72759. write.close
  72760. result = read.read
  72761. Process.wait2(pid)
  72762. return result.unpack("m")[0]
  72763. end
  72764. end
  72765. module Subprocess
  72766. ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
  72767. # Crazy H4X to get this working in windows / jruby with
  72768. # no forking.
  72769. def run_in_isolation(&blk)
  72770. require "tempfile"
  72771. if ENV["ISOLATION_TEST"]
  72772. proxy = ProxyTestResult.new
  72773. retval = yield proxy
  72774. File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
  72775. file.puts [Marshal.dump([retval, proxy])].pack("m")
  72776. end
  72777. exit!
  72778. else
  72779. Tempfile.open("isolation") do |tmpfile|
  72780. ENV["ISOLATION_TEST"] = @method_name
  72781. ENV["ISOLATION_OUTPUT"] = tmpfile.path
  72782. load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
  72783. `#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")} -t\"#{self.class}\"`
  72784. ENV.delete("ISOLATION_TEST")
  72785. ENV.delete("ISOLATION_OUTPUT")
  72786. return tmpfile.read.unpack("m")[0]
  72787. end
  72788. end
  72789. end
  72790. end
  72791. include forking_env? ? Forking : Subprocess
  72792. end
  72793. end
  72794. end
  72795. require 'active_support/deprecation'
  72796. module ActiveSupport
  72797. module Testing
  72798. module Pending # :nodoc:
  72799. unless defined?(Spec)
  72800. def pending(description = "", &block)
  72801. ActiveSupport::Deprecation.warn("#pending is deprecated and will be removed in Rails 4.1, please use #skip instead.")
  72802. skip(description.blank? ? nil : description)
  72803. end
  72804. end
  72805. end
  72806. end
  72807. end
  72808. require 'active_support/concern'
  72809. require 'active_support/callbacks'
  72810. module ActiveSupport
  72811. module Testing
  72812. module SetupAndTeardown
  72813. extend ActiveSupport::Concern
  72814. included do
  72815. include ActiveSupport::Callbacks
  72816. define_callbacks :setup, :teardown
  72817. end
  72818. module ClassMethods
  72819. def setup(*args, &block)
  72820. set_callback(:setup, :before, *args, &block)
  72821. end
  72822. def teardown(*args, &block)
  72823. set_callback(:teardown, :after, *args, &block)
  72824. end
  72825. end
  72826. def before_setup
  72827. super
  72828. run_callbacks :setup
  72829. end
  72830. def after_teardown
  72831. run_callbacks :teardown
  72832. super
  72833. end
  72834. end
  72835. end
  72836. end
  72837. module ActiveSupport
  72838. module Testing
  72839. # Logs a "PostsControllerTest: test name" heading before each test to
  72840. # make test.log easier to search and follow along with.
  72841. module TaggedLogging #:nodoc:
  72842. attr_writer :tagged_logger
  72843. def before_setup
  72844. if tagged_logger
  72845. heading = "#{self.class}: #{__name__}"
  72846. divider = '-' * heading.size
  72847. tagged_logger.info divider
  72848. tagged_logger.info heading
  72849. tagged_logger.info divider
  72850. end
  72851. super
  72852. end
  72853. private
  72854. def tagged_logger
  72855. @tagged_logger ||= (defined?(Rails.logger) && Rails.logger)
  72856. end
  72857. end
  72858. end
  72859. end
  72860. require 'active_support'
  72861. module ActiveSupport
  72862. autoload :Duration, 'active_support/duration'
  72863. autoload :TimeWithZone, 'active_support/time_with_zone'
  72864. autoload :TimeZone, 'active_support/values/time_zone'
  72865. end
  72866. require 'date'
  72867. require 'time'
  72868. require 'active_support/core_ext/time'
  72869. require 'active_support/core_ext/date'
  72870. require 'active_support/core_ext/date_time'
  72871. require 'active_support/core_ext/integer/time'
  72872. require 'active_support/core_ext/numeric/time'
  72873. require 'active_support/core_ext/string/conversions'
  72874. require 'active_support/core_ext/string/zones'
  72875. require 'active_support/values/time_zone'
  72876. require 'active_support/core_ext/object/acts_like'
  72877. module ActiveSupport
  72878. # A Time-like class that can represent a time in any time zone. Necessary
  72879. # because standard Ruby Time instances are limited to UTC and the
  72880. # system's <tt>ENV['TZ']</tt> zone.
  72881. #
  72882. # You shouldn't ever need to create a TimeWithZone instance directly via +new+.
  72883. # Instead use methods +local+, +parse+, +at+ and +now+ on TimeZone instances,
  72884. # and +in_time_zone+ on Time and DateTime instances.
  72885. #
  72886. # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
  72887. # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
  72888. # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
  72889. # Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
  72890. # Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
  72891. # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
  72892. #
  72893. # See Time and TimeZone for further documentation of these methods.
  72894. #
  72895. # TimeWithZone instances implement the same API as Ruby Time instances, so
  72896. # that Time and TimeWithZone instances are interchangeable.
  72897. #
  72898. # t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
  72899. # t.hour # => 13
  72900. # t.dst? # => true
  72901. # t.utc_offset # => -14400
  72902. # t.zone # => "EDT"
  72903. # t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
  72904. # t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
  72905. # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
  72906. # t > Time.utc(1999) # => true
  72907. # t.is_a?(Time) # => true
  72908. # t.is_a?(ActiveSupport::TimeWithZone) # => true
  72909. class TimeWithZone
  72910. # Report class name as 'Time' to thwart type checking.
  72911. def self.name
  72912. 'Time'
  72913. end
  72914. include Comparable
  72915. attr_reader :time_zone
  72916. def initialize(utc_time, time_zone, local_time = nil, period = nil)
  72917. @utc, @time_zone, @time = utc_time, time_zone, local_time
  72918. @period = @utc ? period : get_period_and_ensure_valid_local_time
  72919. end
  72920. # Returns a Time or DateTime instance that represents the time in +time_zone+.
  72921. def time
  72922. @time ||= period.to_local(@utc)
  72923. end
  72924. # Returns a Time or DateTime instance that represents the time in UTC.
  72925. def utc
  72926. @utc ||= period.to_utc(@time)
  72927. end
  72928. alias_method :comparable_time, :utc
  72929. alias_method :getgm, :utc
  72930. alias_method :getutc, :utc
  72931. alias_method :gmtime, :utc
  72932. # Returns the underlying TZInfo::TimezonePeriod.
  72933. def period
  72934. @period ||= time_zone.period_for_utc(@utc)
  72935. end
  72936. # Returns the simultaneous time in <tt>Time.zone</tt>, or the specified zone.
  72937. def in_time_zone(new_zone = ::Time.zone)
  72938. return self if time_zone == new_zone
  72939. utc.in_time_zone(new_zone)
  72940. end
  72941. # Returns a <tt>Time.local()</tt> instance of the simultaneous time in your
  72942. # system's <tt>ENV['TZ']</tt> zone.
  72943. def localtime
  72944. utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
  72945. end
  72946. alias_method :getlocal, :localtime
  72947. # Returns true if the current time is within Daylight Savings Time for the
  72948. # specified time zone.
  72949. #
  72950. # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
  72951. # Time.zone.parse("2012-5-30").dst? # => true
  72952. # Time.zone.parse("2012-11-30").dst? # => false
  72953. def dst?
  72954. period.dst?
  72955. end
  72956. alias_method :isdst, :dst?
  72957. # Returns true if the current time zone is set to UTC.
  72958. #
  72959. # Time.zone = 'UTC' # => 'UTC'
  72960. # Time.zone.now.utc? # => true
  72961. # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
  72962. # Time.zone.now.utc? # => false
  72963. def utc?
  72964. time_zone.name == 'UTC'
  72965. end
  72966. alias_method :gmt?, :utc?
  72967. # Returns the offset from current time to UTC time in seconds.
  72968. def utc_offset
  72969. period.utc_total_offset
  72970. end
  72971. alias_method :gmt_offset, :utc_offset
  72972. alias_method :gmtoff, :utc_offset
  72973. # Returns a formatted string of the offset from UTC, or an alternative
  72974. # string if the time zone is already UTC.
  72975. #
  72976. # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
  72977. # Time.zone.now.formatted_offset(true) # => "-05:00"
  72978. # Time.zone.now.formatted_offset(false) # => "-0500"
  72979. # Time.zone = 'UTC' # => "UTC"
  72980. # Time.zone.now.formatted_offset(true, "0") # => "0"
  72981. def formatted_offset(colon = true, alternate_utc_string = nil)
  72982. utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
  72983. end
  72984. # Time uses +zone+ to display the time zone abbreviation, so we're
  72985. # duck-typing it.
  72986. def zone
  72987. period.zone_identifier.to_s
  72988. end
  72989. def inspect
  72990. "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
  72991. end
  72992. def xmlschema(fraction_digits = 0)
  72993. fraction = if fraction_digits > 0
  72994. (".%06i" % time.usec)[0, fraction_digits + 1]
  72995. end
  72996. "#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
  72997. end
  72998. alias_method :iso8601, :xmlschema
  72999. # Coerces time to a string for JSON encoding. The default format is ISO 8601.
  73000. # You can get %Y/%m/%d %H:%M:%S +offset style by setting
  73001. # <tt>ActiveSupport::JSON::Encoding.use_standard_json_time_format</tt>
  73002. # to +false+.
  73003. #
  73004. # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
  73005. # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
  73006. # # => "2005-02-01T15:15:10Z"
  73007. #
  73008. # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
  73009. # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
  73010. # # => "2005/02/01 15:15:10 +0000"
  73011. def as_json(options = nil)
  73012. if ActiveSupport::JSON::Encoding.use_standard_json_time_format
  73013. xmlschema(3)
  73014. else
  73015. %(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
  73016. end
  73017. end
  73018. def encode_with(coder)
  73019. if coder.respond_to?(:represent_object)
  73020. coder.represent_object(nil, utc)
  73021. else
  73022. coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
  73023. end
  73024. end
  73025. # Returns a string of the object's date and time in the format used by
  73026. # HTTP requests.
  73027. #
  73028. # Time.zone.now.httpdate # => "Tue, 01 Jan 2013 04:39:43 GMT"
  73029. def httpdate
  73030. utc.httpdate
  73031. end
  73032. # Returns a string of the object's date and time in the RFC 2822 standard
  73033. # format.
  73034. #
  73035. # Time.zone.now.rfc2822 # => "Tue, 01 Jan 2013 04:51:39 +0000"
  73036. def rfc2822
  73037. to_s(:rfc822)
  73038. end
  73039. alias_method :rfc822, :rfc2822
  73040. # <tt>:db</tt> format outputs time in UTC; all others output time in local.
  73041. # Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly.
  73042. def to_s(format = :default)
  73043. if format == :db
  73044. utc.to_s(format)
  73045. elsif formatter = ::Time::DATE_FORMATS[format]
  73046. formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
  73047. else
  73048. "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
  73049. end
  73050. end
  73051. alias_method :to_formatted_s, :to_s
  73052. # Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and
  73053. # +formatted_offset+, respectively, before passing to Time#strftime, so
  73054. # that zone information is correct
  73055. def strftime(format)
  73056. format = format.gsub('%Z', zone)
  73057. .gsub('%z', formatted_offset(false))
  73058. .gsub('%:z', formatted_offset(true))
  73059. .gsub('%::z', formatted_offset(true) + ":00")
  73060. time.strftime(format)
  73061. end
  73062. # Use the time in UTC for comparisons.
  73063. def <=>(other)
  73064. utc <=> other
  73065. end
  73066. # Returns true if the current object's time is within the specified
  73067. # +min+ and +max+ time.
  73068. def between?(min, max)
  73069. utc.between?(min, max)
  73070. end
  73071. # Returns true if the current object's time is in the past.
  73072. def past?
  73073. utc.past?
  73074. end
  73075. # Returns true if the current object's time falls within
  73076. # the current day.
  73077. def today?
  73078. time.today?
  73079. end
  73080. # Returns true if the current object's time is in the future.
  73081. def future?
  73082. utc.future?
  73083. end
  73084. def eql?(other)
  73085. utc.eql?(other)
  73086. end
  73087. def hash
  73088. utc.hash
  73089. end
  73090. def +(other)
  73091. # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
  73092. # otherwise move forward from #utc, for accuracy when moving across DST boundaries
  73093. if duration_of_variable_length?(other)
  73094. method_missing(:+, other)
  73095. else
  73096. result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other)
  73097. result.in_time_zone(time_zone)
  73098. end
  73099. end
  73100. def -(other)
  73101. # If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
  73102. # otherwise move backwards #utc, for accuracy when moving across DST boundaries
  73103. if other.acts_like?(:time)
  73104. utc.to_f - other.to_f
  73105. elsif duration_of_variable_length?(other)
  73106. method_missing(:-, other)
  73107. else
  73108. result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
  73109. result.in_time_zone(time_zone)
  73110. end
  73111. end
  73112. def since(other)
  73113. # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
  73114. # otherwise move forward from #utc, for accuracy when moving across DST boundaries
  73115. if duration_of_variable_length?(other)
  73116. method_missing(:since, other)
  73117. else
  73118. utc.since(other).in_time_zone(time_zone)
  73119. end
  73120. end
  73121. def ago(other)
  73122. since(-other)
  73123. end
  73124. def advance(options)
  73125. # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
  73126. # otherwise advance from #utc, for accuracy when moving across DST boundaries
  73127. if options.values_at(:years, :weeks, :months, :days).any?
  73128. method_missing(:advance, options)
  73129. else
  73130. utc.advance(options).in_time_zone(time_zone)
  73131. end
  73132. end
  73133. %w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
  73134. class_eval <<-EOV, __FILE__, __LINE__ + 1
  73135. def #{method_name} # def month
  73136. time.#{method_name} # time.month
  73137. end # end
  73138. EOV
  73139. end
  73140. def usec
  73141. time.respond_to?(:usec) ? time.usec : 0
  73142. end
  73143. def to_a
  73144. [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
  73145. end
  73146. def to_f
  73147. utc.to_f
  73148. end
  73149. def to_i
  73150. utc.to_i
  73151. end
  73152. alias_method :tv_sec, :to_i
  73153. # Return an instance of Time in the system timezone.
  73154. def to_time
  73155. utc.to_time
  73156. end
  73157. def to_datetime
  73158. utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
  73159. end
  73160. # So that +self+ <tt>acts_like?(:time)</tt>.
  73161. def acts_like_time?
  73162. true
  73163. end
  73164. # Say we're a Time to thwart type checking.
  73165. def is_a?(klass)
  73166. klass == ::Time || super
  73167. end
  73168. alias_method :kind_of?, :is_a?
  73169. def freeze
  73170. period; utc; time # preload instance variables before freezing
  73171. super
  73172. end
  73173. def marshal_dump
  73174. [utc, time_zone.name, time]
  73175. end
  73176. def marshal_load(variables)
  73177. initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc)
  73178. end
  73179. # Ensure proxy class responds to all methods that underlying time instance
  73180. # responds to.
  73181. def respond_to_missing?(sym, include_priv)
  73182. # consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
  73183. return false if sym.to_sym == :acts_like_date?
  73184. time.respond_to?(sym, include_priv)
  73185. end
  73186. # Send the missing method to +time+ instance, and wrap result in a new
  73187. # TimeWithZone with the existing +time_zone+.
  73188. def method_missing(sym, *args, &block)
  73189. wrap_with_time_zone time.__send__(sym, *args, &block)
  73190. end
  73191. private
  73192. def get_period_and_ensure_valid_local_time
  73193. # we don't want a Time.local instance enforcing its own DST rules as well,
  73194. # so transfer time values to a utc constructor if necessary
  73195. @time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
  73196. begin
  73197. @time_zone.period_for_local(@time)
  73198. rescue ::TZInfo::PeriodNotFound
  73199. # time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
  73200. @time += 1.hour
  73201. retry
  73202. end
  73203. end
  73204. def transfer_time_values_to_utc_constructor(time)
  73205. ::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec, Rational(time.nsec, 1000))
  73206. end
  73207. def duration_of_variable_length?(obj)
  73208. ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) }
  73209. end
  73210. def wrap_with_time_zone(time)
  73211. if time.acts_like?(:time)
  73212. self.class.new(nil, time_zone, time)
  73213. elsif time.is_a?(Range)
  73214. wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
  73215. else
  73216. time
  73217. end
  73218. end
  73219. end
  73220. end
  73221. require 'active_support/core_ext/object/blank'
  73222. require 'active_support/core_ext/object/try'
  73223. module ActiveSupport
  73224. # The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
  73225. # It allows us to do the following:
  73226. #
  73227. # * Limit the set of zones provided by TZInfo to a meaningful subset of 142
  73228. # zones.
  73229. # * Retrieve and display zones with a friendlier name
  73230. # (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
  73231. # * Lazily load TZInfo::Timezone instances only when they're needed.
  73232. # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+,
  73233. # +parse+, +at+ and +now+ methods.
  73234. #
  73235. # If you set <tt>config.time_zone</tt> in the Rails Application, you can
  73236. # access this TimeZone object via <tt>Time.zone</tt>:
  73237. #
  73238. # # application.rb:
  73239. # class Application < Rails::Application
  73240. # config.time_zone = 'Eastern Time (US & Canada)'
  73241. # end
  73242. #
  73243. # Time.zone # => #<TimeZone:0x514834...>
  73244. # Time.zone.name # => "Eastern Time (US & Canada)"
  73245. # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
  73246. #
  73247. # The version of TZInfo bundled with Active Support only includes the
  73248. # definitions necessary to support the zones defined by the TimeZone class.
  73249. # If you need to use zones that aren't defined by TimeZone, you'll need to
  73250. # install the TZInfo gem (if a recent version of the gem is installed locally,
  73251. # this will be used instead of the bundled version.)
  73252. class TimeZone
  73253. # Keys are Rails TimeZone names, values are TZInfo identifiers.
  73254. MAPPING = {
  73255. "International Date Line West" => "Pacific/Midway",
  73256. "Midway Island" => "Pacific/Midway",
  73257. "American Samoa" => "Pacific/Pago_Pago",
  73258. "Hawaii" => "Pacific/Honolulu",
  73259. "Alaska" => "America/Juneau",
  73260. "Pacific Time (US & Canada)" => "America/Los_Angeles",
  73261. "Tijuana" => "America/Tijuana",
  73262. "Mountain Time (US & Canada)" => "America/Denver",
  73263. "Arizona" => "America/Phoenix",
  73264. "Chihuahua" => "America/Chihuahua",
  73265. "Mazatlan" => "America/Mazatlan",
  73266. "Central Time (US & Canada)" => "America/Chicago",
  73267. "Saskatchewan" => "America/Regina",
  73268. "Guadalajara" => "America/Mexico_City",
  73269. "Mexico City" => "America/Mexico_City",
  73270. "Monterrey" => "America/Monterrey",
  73271. "Central America" => "America/Guatemala",
  73272. "Eastern Time (US & Canada)" => "America/New_York",
  73273. "Indiana (East)" => "America/Indiana/Indianapolis",
  73274. "Bogota" => "America/Bogota",
  73275. "Lima" => "America/Lima",
  73276. "Quito" => "America/Lima",
  73277. "Atlantic Time (Canada)" => "America/Halifax",
  73278. "Caracas" => "America/Caracas",
  73279. "La Paz" => "America/La_Paz",
  73280. "Santiago" => "America/Santiago",
  73281. "Newfoundland" => "America/St_Johns",
  73282. "Brasilia" => "America/Sao_Paulo",
  73283. "Buenos Aires" => "America/Argentina/Buenos_Aires",
  73284. "Georgetown" => "America/Guyana",
  73285. "Greenland" => "America/Godthab",
  73286. "Mid-Atlantic" => "Atlantic/South_Georgia",
  73287. "Azores" => "Atlantic/Azores",
  73288. "Cape Verde Is." => "Atlantic/Cape_Verde",
  73289. "Dublin" => "Europe/Dublin",
  73290. "Edinburgh" => "Europe/London",
  73291. "Lisbon" => "Europe/Lisbon",
  73292. "London" => "Europe/London",
  73293. "Casablanca" => "Africa/Casablanca",
  73294. "Monrovia" => "Africa/Monrovia",
  73295. "UTC" => "Etc/UTC",
  73296. "Belgrade" => "Europe/Belgrade",
  73297. "Bratislava" => "Europe/Bratislava",
  73298. "Budapest" => "Europe/Budapest",
  73299. "Ljubljana" => "Europe/Ljubljana",
  73300. "Prague" => "Europe/Prague",
  73301. "Sarajevo" => "Europe/Sarajevo",
  73302. "Skopje" => "Europe/Skopje",
  73303. "Warsaw" => "Europe/Warsaw",
  73304. "Zagreb" => "Europe/Zagreb",
  73305. "Brussels" => "Europe/Brussels",
  73306. "Copenhagen" => "Europe/Copenhagen",
  73307. "Madrid" => "Europe/Madrid",
  73308. "Paris" => "Europe/Paris",
  73309. "Amsterdam" => "Europe/Amsterdam",
  73310. "Berlin" => "Europe/Berlin",
  73311. "Bern" => "Europe/Berlin",
  73312. "Rome" => "Europe/Rome",
  73313. "Stockholm" => "Europe/Stockholm",
  73314. "Vienna" => "Europe/Vienna",
  73315. "West Central Africa" => "Africa/Algiers",
  73316. "Bucharest" => "Europe/Bucharest",
  73317. "Cairo" => "Africa/Cairo",
  73318. "Helsinki" => "Europe/Helsinki",
  73319. "Kyiv" => "Europe/Kiev",
  73320. "Riga" => "Europe/Riga",
  73321. "Sofia" => "Europe/Sofia",
  73322. "Tallinn" => "Europe/Tallinn",
  73323. "Vilnius" => "Europe/Vilnius",
  73324. "Athens" => "Europe/Athens",
  73325. "Istanbul" => "Europe/Istanbul",
  73326. "Minsk" => "Europe/Minsk",
  73327. "Jerusalem" => "Asia/Jerusalem",
  73328. "Harare" => "Africa/Harare",
  73329. "Pretoria" => "Africa/Johannesburg",
  73330. "Moscow" => "Europe/Moscow",
  73331. "St. Petersburg" => "Europe/Moscow",
  73332. "Volgograd" => "Europe/Moscow",
  73333. "Kuwait" => "Asia/Kuwait",
  73334. "Riyadh" => "Asia/Riyadh",
  73335. "Nairobi" => "Africa/Nairobi",
  73336. "Baghdad" => "Asia/Baghdad",
  73337. "Tehran" => "Asia/Tehran",
  73338. "Abu Dhabi" => "Asia/Muscat",
  73339. "Muscat" => "Asia/Muscat",
  73340. "Baku" => "Asia/Baku",
  73341. "Tbilisi" => "Asia/Tbilisi",
  73342. "Yerevan" => "Asia/Yerevan",
  73343. "Kabul" => "Asia/Kabul",
  73344. "Ekaterinburg" => "Asia/Yekaterinburg",
  73345. "Islamabad" => "Asia/Karachi",
  73346. "Karachi" => "Asia/Karachi",
  73347. "Tashkent" => "Asia/Tashkent",
  73348. "Chennai" => "Asia/Kolkata",
  73349. "Kolkata" => "Asia/Kolkata",
  73350. "Mumbai" => "Asia/Kolkata",
  73351. "New Delhi" => "Asia/Kolkata",
  73352. "Kathmandu" => "Asia/Kathmandu",
  73353. "Astana" => "Asia/Dhaka",
  73354. "Dhaka" => "Asia/Dhaka",
  73355. "Sri Jayawardenepura" => "Asia/Colombo",
  73356. "Almaty" => "Asia/Almaty",
  73357. "Novosibirsk" => "Asia/Novosibirsk",
  73358. "Rangoon" => "Asia/Rangoon",
  73359. "Bangkok" => "Asia/Bangkok",
  73360. "Hanoi" => "Asia/Bangkok",
  73361. "Jakarta" => "Asia/Jakarta",
  73362. "Krasnoyarsk" => "Asia/Krasnoyarsk",
  73363. "Beijing" => "Asia/Shanghai",
  73364. "Chongqing" => "Asia/Chongqing",
  73365. "Hong Kong" => "Asia/Hong_Kong",
  73366. "Urumqi" => "Asia/Urumqi",
  73367. "Kuala Lumpur" => "Asia/Kuala_Lumpur",
  73368. "Singapore" => "Asia/Singapore",
  73369. "Taipei" => "Asia/Taipei",
  73370. "Perth" => "Australia/Perth",
  73371. "Irkutsk" => "Asia/Irkutsk",
  73372. "Ulaan Bataar" => "Asia/Ulaanbaatar",
  73373. "Seoul" => "Asia/Seoul",
  73374. "Osaka" => "Asia/Tokyo",
  73375. "Sapporo" => "Asia/Tokyo",
  73376. "Tokyo" => "Asia/Tokyo",
  73377. "Yakutsk" => "Asia/Yakutsk",
  73378. "Darwin" => "Australia/Darwin",
  73379. "Adelaide" => "Australia/Adelaide",
  73380. "Canberra" => "Australia/Melbourne",
  73381. "Melbourne" => "Australia/Melbourne",
  73382. "Sydney" => "Australia/Sydney",
  73383. "Brisbane" => "Australia/Brisbane",
  73384. "Hobart" => "Australia/Hobart",
  73385. "Vladivostok" => "Asia/Vladivostok",
  73386. "Guam" => "Pacific/Guam",
  73387. "Port Moresby" => "Pacific/Port_Moresby",
  73388. "Magadan" => "Asia/Magadan",
  73389. "Solomon Is." => "Pacific/Guadalcanal",
  73390. "New Caledonia" => "Pacific/Noumea",
  73391. "Fiji" => "Pacific/Fiji",
  73392. "Kamchatka" => "Asia/Kamchatka",
  73393. "Marshall Is." => "Pacific/Majuro",
  73394. "Auckland" => "Pacific/Auckland",
  73395. "Wellington" => "Pacific/Auckland",
  73396. "Nuku'alofa" => "Pacific/Tongatapu",
  73397. "Tokelau Is." => "Pacific/Fakaofo",
  73398. "Samoa" => "Pacific/Apia"
  73399. }
  73400. UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
  73401. UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
  73402. # Assumes self represents an offset from UTC in seconds (as returned from
  73403. # Time#utc_offset) and turns this into an +HH:MM formatted string.
  73404. #
  73405. # TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
  73406. def self.seconds_to_utc_offset(seconds, colon = true)
  73407. format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON
  73408. sign = (seconds < 0 ? '-' : '+')
  73409. hours = seconds.abs / 3600
  73410. minutes = (seconds.abs % 3600) / 60
  73411. format % [sign, hours, minutes]
  73412. end
  73413. include Comparable
  73414. attr_reader :name
  73415. attr_reader :tzinfo
  73416. # Create a new TimeZone object with the given name and offset. The
  73417. # offset is the number of seconds that this time zone is offset from UTC
  73418. # (GMT). Seconds were chosen as the offset unit because that is the unit
  73419. # that Ruby uses to represent time zone offsets (see Time#utc_offset).
  73420. def initialize(name, utc_offset = nil, tzinfo = nil)
  73421. self.class.send(:require_tzinfo)
  73422. @name = name
  73423. @utc_offset = utc_offset
  73424. @tzinfo = tzinfo || TimeZone.find_tzinfo(name)
  73425. @current_period = nil
  73426. end
  73427. # Returns the offset of this time zone from UTC in seconds.
  73428. def utc_offset
  73429. if @utc_offset
  73430. @utc_offset
  73431. else
  73432. @current_period ||= tzinfo.try(:current_period)
  73433. @current_period.try(:utc_offset)
  73434. end
  73435. end
  73436. # Returns the offset of this time zone as a formatted string, of the
  73437. # format "+HH:MM".
  73438. def formatted_offset(colon=true, alternate_utc_string = nil)
  73439. utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
  73440. end
  73441. # Compare this time zone to the parameter. The two are compared first on
  73442. # their offsets, and then by name.
  73443. def <=>(zone)
  73444. result = (utc_offset <=> zone.utc_offset)
  73445. result = (name <=> zone.name) if result == 0
  73446. result
  73447. end
  73448. # Compare #name and TZInfo identifier to a supplied regexp, returning +true+
  73449. # if a match is found.
  73450. def =~(re)
  73451. return true if name =~ re || MAPPING[name] =~ re
  73452. end
  73453. # Returns a textual representation of this time zone.
  73454. def to_s
  73455. "(GMT#{formatted_offset}) #{name}"
  73456. end
  73457. # Method for creating new ActiveSupport::TimeWithZone instance in time zone
  73458. # of +self+ from given values.
  73459. #
  73460. # Time.zone = 'Hawaii' # => "Hawaii"
  73461. # Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00
  73462. def local(*args)
  73463. time = Time.utc(*args)
  73464. ActiveSupport::TimeWithZone.new(nil, self, time)
  73465. end
  73466. # Method for creating new ActiveSupport::TimeWithZone instance in time zone
  73467. # of +self+ from number of seconds since the Unix epoch.
  73468. #
  73469. # Time.zone = 'Hawaii' # => "Hawaii"
  73470. # Time.utc(2000).to_f # => 946684800.0
  73471. # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
  73472. def at(secs)
  73473. Time.at(secs).utc.in_time_zone(self)
  73474. end
  73475. # Method for creating new ActiveSupport::TimeWithZone instance in time zone
  73476. # of +self+ from parsed string.
  73477. #
  73478. # Time.zone = 'Hawaii' # => "Hawaii"
  73479. # Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
  73480. #
  73481. # If upper components are missing from the string, they are supplied from
  73482. # TimeZone#now:
  73483. #
  73484. # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
  73485. # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
  73486. def parse(str, now=now)
  73487. parts = Date._parse(str, false)
  73488. return if parts.empty?
  73489. time = Time.new(
  73490. parts.fetch(:year, now.year),
  73491. parts.fetch(:mon, now.month),
  73492. parts.fetch(:mday, now.day),
  73493. parts.fetch(:hour, 0),
  73494. parts.fetch(:min, 0),
  73495. parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
  73496. parts.fetch(:offset, 0)
  73497. )
  73498. if parts[:offset]
  73499. TimeWithZone.new(time.utc, self)
  73500. else
  73501. TimeWithZone.new(nil, self, time)
  73502. end
  73503. end
  73504. # Returns an ActiveSupport::TimeWithZone instance representing the current
  73505. # time in the time zone represented by +self+.
  73506. #
  73507. # Time.zone = 'Hawaii' # => "Hawaii"
  73508. # Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
  73509. def now
  73510. time_now.utc.in_time_zone(self)
  73511. end
  73512. # Return the current date in this time zone.
  73513. def today
  73514. tzinfo.now.to_date
  73515. end
  73516. # Adjust the given time to the simultaneous time in the time zone
  73517. # represented by +self+. Returns a Time.utc() instance -- if you want an
  73518. # ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
  73519. def utc_to_local(time)
  73520. tzinfo.utc_to_local(time)
  73521. end
  73522. # Adjust the given time to the simultaneous time in UTC. Returns a
  73523. # Time.utc() instance.
  73524. def local_to_utc(time, dst=true)
  73525. tzinfo.local_to_utc(time, dst)
  73526. end
  73527. # Available so that TimeZone instances respond like TZInfo::Timezone
  73528. # instances.
  73529. def period_for_utc(time)
  73530. tzinfo.period_for_utc(time)
  73531. end
  73532. # Available so that TimeZone instances respond like TZInfo::Timezone
  73533. # instances.
  73534. def period_for_local(time, dst=true)
  73535. tzinfo.period_for_local(time, dst)
  73536. end
  73537. def self.find_tzinfo(name)
  73538. TZInfo::TimezoneProxy.new(MAPPING[name] || name)
  73539. end
  73540. class << self
  73541. alias_method :create, :new
  73542. # Return a TimeZone instance with the given name, or +nil+ if no
  73543. # such TimeZone instance exists. (This exists to support the use of
  73544. # this class with the +composed_of+ macro.)
  73545. def new(name)
  73546. self[name]
  73547. end
  73548. # Return an array of all TimeZone objects. There are multiple
  73549. # TimeZone objects per time zone, in many cases, to make it easier
  73550. # for users to find their own time zone.
  73551. def all
  73552. @zones ||= zones_map.values.sort
  73553. end
  73554. def zones_map
  73555. @zones_map ||= begin
  73556. new_zones_names = MAPPING.keys - lazy_zones_map.keys
  73557. new_zones = Hash[new_zones_names.map { |place| [place, create(place)] }]
  73558. lazy_zones_map.merge(new_zones)
  73559. end
  73560. end
  73561. # Locate a specific time zone object. If the argument is a string, it
  73562. # is interpreted to mean the name of the timezone to locate. If it is a
  73563. # numeric value it is either the hour offset, or the second offset, of the
  73564. # timezone to find. (The first one with that offset will be returned.)
  73565. # Returns +nil+ if no such time zone is known to the system.
  73566. def [](arg)
  73567. case arg
  73568. when String
  73569. begin
  73570. lazy_zones_map[arg] ||= lookup(arg).tap { |tz| tz.utc_offset }
  73571. rescue TZInfo::InvalidTimezoneIdentifier
  73572. nil
  73573. end
  73574. when Numeric, ActiveSupport::Duration
  73575. arg *= 3600 if arg.abs <= 13
  73576. all.find { |z| z.utc_offset == arg.to_i }
  73577. else
  73578. raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
  73579. end
  73580. end
  73581. # A convenience method for returning a collection of TimeZone objects
  73582. # for time zones in the USA.
  73583. def us_zones
  73584. @us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
  73585. end
  73586. protected
  73587. def require_tzinfo
  73588. require 'tzinfo' unless defined?(::TZInfo)
  73589. rescue LoadError
  73590. $stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
  73591. raise
  73592. end
  73593. private
  73594. def lookup(name)
  73595. (tzinfo = find_tzinfo(name)) && create(tzinfo.name.freeze)
  73596. end
  73597. def lazy_zones_map
  73598. require_tzinfo
  73599. @lazy_zones_map ||= Hash.new do |hash, place|
  73600. hash[place] = create(place) if MAPPING.has_key?(place)
  73601. end
  73602. end
  73603. end
  73604. private
  73605. def time_now
  73606. Time.now
  73607. end
  73608. end
  73609. end
  73610. module ActiveSupport
  73611. module VERSION #:nodoc:
  73612. MAJOR = 4
  73613. MINOR = 0
  73614. TINY = 0
  73615. PRE = "beta"
  73616. STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
  73617. end
  73618. end
  73619. raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM =~ /java/
  73620. require 'jruby'
  73621. include Java
  73622. require 'active_support/core_ext/object/blank'
  73623. java_import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder
  73624. java_import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory
  73625. java_import java.io.StringReader unless defined? StringReader
  73626. java_import org.xml.sax.InputSource unless defined? InputSource
  73627. java_import org.xml.sax.Attributes unless defined? Attributes
  73628. java_import org.w3c.dom.Node unless defined? Node
  73629. module ActiveSupport
  73630. module XmlMini_JDOM #:nodoc:
  73631. extend self
  73632. CONTENT_KEY = '__content__'.freeze
  73633. NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE
  73634. DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE
  73635. PROCESSING_INSTRUCTION_NODE TEXT_NODE}
  73636. node_type_map = {}
  73637. NODE_TYPE_NAMES.each { |type| node_type_map[Node.send(type)] = type }
  73638. # Parse an XML Document string or IO into a simple hash using Java's jdom.
  73639. # data::
  73640. # XML Document string or IO to parse
  73641. def parse(data)
  73642. if data.respond_to?(:read)
  73643. data = data.read
  73644. end
  73645. if data.blank?
  73646. {}
  73647. else
  73648. @dbf = DocumentBuilderFactory.new_instance
  73649. xml_string_reader = StringReader.new(data)
  73650. xml_input_source = InputSource.new(xml_string_reader)
  73651. doc = @dbf.new_document_builder.parse(xml_input_source)
  73652. merge_element!({CONTENT_KEY => ''}, doc.document_element)
  73653. end
  73654. end
  73655. private
  73656. # Convert an XML element and merge into the hash
  73657. #
  73658. # hash::
  73659. # Hash to merge the converted element into.
  73660. # element::
  73661. # XML element to merge into hash
  73662. def merge_element!(hash, element)
  73663. delete_empty(hash)
  73664. merge!(hash, element.tag_name, collapse(element))
  73665. end
  73666. def delete_empty(hash)
  73667. hash.delete(CONTENT_KEY) if hash[CONTENT_KEY] == ''
  73668. end
  73669. # Actually converts an XML document element into a data structure.
  73670. #
  73671. # element::
  73672. # The document element to be collapsed.
  73673. def collapse(element)
  73674. hash = get_attributes(element)
  73675. child_nodes = element.child_nodes
  73676. if child_nodes.length > 0
  73677. (0...child_nodes.length).each do |i|
  73678. child = child_nodes.item(i)
  73679. merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE
  73680. end
  73681. merge_texts!(hash, element) unless empty_content?(element)
  73682. hash
  73683. else
  73684. merge_texts!(hash, element)
  73685. end
  73686. end
  73687. # Merge all the texts of an element into the hash
  73688. #
  73689. # hash::
  73690. # Hash to add the converted element to.
  73691. # element::
  73692. # XML element whose texts are to me merged into the hash
  73693. def merge_texts!(hash, element)
  73694. delete_empty(hash)
  73695. text_children = texts(element)
  73696. if text_children.join.empty?
  73697. hash
  73698. else
  73699. # must use value to prevent double-escaping
  73700. merge!(hash, CONTENT_KEY, text_children.join)
  73701. end
  73702. end
  73703. # Adds a new key/value pair to an existing Hash. If the key to be added
  73704. # already exists and the existing value associated with key is not
  73705. # an Array, it will be wrapped in an Array. Then the new value is
  73706. # appended to that Array.
  73707. #
  73708. # hash::
  73709. # Hash to add key/value pair to.
  73710. # key::
  73711. # Key to be added.
  73712. # value::
  73713. # Value to be associated with key.
  73714. def merge!(hash, key, value)
  73715. if hash.has_key?(key)
  73716. if hash[key].instance_of?(Array)
  73717. hash[key] << value
  73718. else
  73719. hash[key] = [hash[key], value]
  73720. end
  73721. elsif value.instance_of?(Array)
  73722. hash[key] = [value]
  73723. else
  73724. hash[key] = value
  73725. end
  73726. hash
  73727. end
  73728. # Converts the attributes array of an XML element into a hash.
  73729. # Returns an empty Hash if node has no attributes.
  73730. #
  73731. # element::
  73732. # XML element to extract attributes from.
  73733. def get_attributes(element)
  73734. attribute_hash = {}
  73735. attributes = element.attributes
  73736. (0...attributes.length).each do |i|
  73737. attribute_hash[CONTENT_KEY] ||= ''
  73738. attribute_hash[attributes.item(i).name] = attributes.item(i).value
  73739. end
  73740. attribute_hash
  73741. end
  73742. # Determines if a document element has text content
  73743. #
  73744. # element::
  73745. # XML element to be checked.
  73746. def texts(element)
  73747. texts = []
  73748. child_nodes = element.child_nodes
  73749. (0...child_nodes.length).each do |i|
  73750. item = child_nodes.item(i)
  73751. if item.node_type == Node.TEXT_NODE
  73752. texts << item.get_data
  73753. end
  73754. end
  73755. texts
  73756. end
  73757. # Determines if a document element has text content
  73758. #
  73759. # element::
  73760. # XML element to be checked.
  73761. def empty_content?(element)
  73762. text = ''
  73763. child_nodes = element.child_nodes
  73764. (0...child_nodes.length).each do |i|
  73765. item = child_nodes.item(i)
  73766. if item.node_type == Node.TEXT_NODE
  73767. text << item.get_data.strip
  73768. end
  73769. end
  73770. text.strip.length == 0
  73771. end
  73772. end
  73773. end
  73774. require 'libxml'
  73775. require 'active_support/core_ext/object/blank'
  73776. require 'stringio'
  73777. module ActiveSupport
  73778. module XmlMini_LibXML #:nodoc:
  73779. extend self
  73780. # Parse an XML Document string or IO into a simple hash using libxml.
  73781. # data::
  73782. # XML Document string or IO to parse
  73783. def parse(data)
  73784. if !data.respond_to?(:read)
  73785. data = StringIO.new(data || '')
  73786. end
  73787. char = data.getc
  73788. if char.nil?
  73789. {}
  73790. else
  73791. data.ungetc(char)
  73792. LibXML::XML::Parser.io(data).parse.to_hash
  73793. end
  73794. end
  73795. end
  73796. end
  73797. module LibXML #:nodoc:
  73798. module Conversions #:nodoc:
  73799. module Document #:nodoc:
  73800. def to_hash
  73801. root.to_hash
  73802. end
  73803. end
  73804. module Node #:nodoc:
  73805. CONTENT_ROOT = '__content__'.freeze
  73806. # Convert XML document to hash.
  73807. #
  73808. # hash::
  73809. # Hash to merge the converted element into.
  73810. def to_hash(hash={})
  73811. node_hash = {}
  73812. # Insert node hash into parent hash correctly.
  73813. case hash[name]
  73814. when Array then hash[name] << node_hash
  73815. when Hash then hash[name] = [hash[name], node_hash]
  73816. when nil then hash[name] = node_hash
  73817. end
  73818. # Handle child elements
  73819. each_child do |c|
  73820. if c.element?
  73821. c.to_hash(node_hash)
  73822. elsif c.text? || c.cdata?
  73823. node_hash[CONTENT_ROOT] ||= ''
  73824. node_hash[CONTENT_ROOT] << c.content
  73825. end
  73826. end
  73827. # Remove content node if it is blank
  73828. if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank?
  73829. node_hash.delete(CONTENT_ROOT)
  73830. end
  73831. # Handle attributes
  73832. each_attr { |a| node_hash[a.name] = a.value }
  73833. hash
  73834. end
  73835. end
  73836. end
  73837. end
  73838. LibXML::XML::Document.send(:include, LibXML::Conversions::Document)
  73839. LibXML::XML::Node.send(:include, LibXML::Conversions::Node)
  73840. require 'libxml'
  73841. require 'active_support/core_ext/object/blank'
  73842. require 'stringio'
  73843. module ActiveSupport
  73844. module XmlMini_LibXMLSAX #:nodoc:
  73845. extend self
  73846. # Class that will build the hash while the XML document
  73847. # is being parsed using SAX events.
  73848. class HashBuilder
  73849. include LibXML::XML::SaxParser::Callbacks
  73850. CONTENT_KEY = '__content__'.freeze
  73851. HASH_SIZE_KEY = '__hash_size__'.freeze
  73852. attr_reader :hash
  73853. def current_hash
  73854. @hash_stack.last
  73855. end
  73856. def on_start_document
  73857. @hash = { CONTENT_KEY => '' }
  73858. @hash_stack = [@hash]
  73859. end
  73860. def on_end_document
  73861. @hash = @hash_stack.pop
  73862. @hash.delete(CONTENT_KEY)
  73863. end
  73864. def on_start_element(name, attrs = {})
  73865. new_hash = { CONTENT_KEY => '' }.merge(attrs)
  73866. new_hash[HASH_SIZE_KEY] = new_hash.size + 1
  73867. case current_hash[name]
  73868. when Array then current_hash[name] << new_hash
  73869. when Hash then current_hash[name] = [current_hash[name], new_hash]
  73870. when nil then current_hash[name] = new_hash
  73871. end
  73872. @hash_stack.push(new_hash)
  73873. end
  73874. def on_end_element(name)
  73875. if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == ''
  73876. current_hash.delete(CONTENT_KEY)
  73877. end
  73878. @hash_stack.pop
  73879. end
  73880. def on_characters(string)
  73881. current_hash[CONTENT_KEY] << string
  73882. end
  73883. alias_method :on_cdata_block, :on_characters
  73884. end
  73885. attr_accessor :document_class
  73886. self.document_class = HashBuilder
  73887. def parse(data)
  73888. if !data.respond_to?(:read)
  73889. data = StringIO.new(data || '')
  73890. end
  73891. char = data.getc
  73892. if char.nil?
  73893. {}
  73894. else
  73895. data.ungetc(char)
  73896. LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER)
  73897. parser = LibXML::XML::SaxParser.io(data)
  73898. document = self.document_class.new
  73899. parser.callbacks = document
  73900. parser.parse
  73901. document.hash
  73902. end
  73903. end
  73904. end
  73905. end
  73906. begin
  73907. require 'nokogiri'
  73908. rescue LoadError => e
  73909. $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install"
  73910. raise e
  73911. end
  73912. require 'active_support/core_ext/object/blank'
  73913. require 'stringio'
  73914. module ActiveSupport
  73915. module XmlMini_Nokogiri #:nodoc:
  73916. extend self
  73917. # Parse an XML Document string or IO into a simple hash using libxml / nokogiri.
  73918. # data::
  73919. # XML Document string or IO to parse
  73920. def parse(data)
  73921. if !data.respond_to?(:read)
  73922. data = StringIO.new(data || '')
  73923. end
  73924. char = data.getc
  73925. if char.nil?
  73926. {}
  73927. else
  73928. data.ungetc(char)
  73929. doc = Nokogiri::XML(data)
  73930. raise doc.errors.first if doc.errors.length > 0
  73931. doc.to_hash
  73932. end
  73933. end
  73934. module Conversions #:nodoc:
  73935. module Document #:nodoc:
  73936. def to_hash
  73937. root.to_hash
  73938. end
  73939. end
  73940. module Node #:nodoc:
  73941. CONTENT_ROOT = '__content__'.freeze
  73942. # Convert XML document to hash.
  73943. #
  73944. # hash::
  73945. # Hash to merge the converted element into.
  73946. def to_hash(hash={})
  73947. node_hash = {}
  73948. # Insert node hash into parent hash correctly.
  73949. case hash[name]
  73950. when Array then hash[name] << node_hash
  73951. when Hash then hash[name] = [hash[name], node_hash]
  73952. when nil then hash[name] = node_hash
  73953. end
  73954. # Handle child elements
  73955. children.each do |c|
  73956. if c.element?
  73957. c.to_hash(node_hash)
  73958. elsif c.text? || c.cdata?
  73959. node_hash[CONTENT_ROOT] ||= ''
  73960. node_hash[CONTENT_ROOT] << c.content
  73961. end
  73962. end
  73963. # Remove content node if it is blank and there are child tags
  73964. if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank?
  73965. node_hash.delete(CONTENT_ROOT)
  73966. end
  73967. # Handle attributes
  73968. attribute_nodes.each { |a| node_hash[a.node_name] = a.value }
  73969. hash
  73970. end
  73971. end
  73972. end
  73973. Nokogiri::XML::Document.send(:include, Conversions::Document)
  73974. Nokogiri::XML::Node.send(:include, Conversions::Node)
  73975. end
  73976. end
  73977. begin
  73978. require 'nokogiri'
  73979. rescue LoadError => e
  73980. $stderr.puts "You don't have nokogiri installed in your application. Please add it to your Gemfile and run bundle install"
  73981. raise e
  73982. end
  73983. require 'active_support/core_ext/object/blank'
  73984. require 'stringio'
  73985. module ActiveSupport
  73986. module XmlMini_NokogiriSAX #:nodoc:
  73987. extend self
  73988. # Class that will build the hash while the XML document
  73989. # is being parsed using SAX events.
  73990. class HashBuilder < Nokogiri::XML::SAX::Document
  73991. CONTENT_KEY = '__content__'.freeze
  73992. HASH_SIZE_KEY = '__hash_size__'.freeze
  73993. attr_reader :hash
  73994. def current_hash
  73995. @hash_stack.last
  73996. end
  73997. def start_document
  73998. @hash = {}
  73999. @hash_stack = [@hash]
  74000. end
  74001. def end_document
  74002. raise "Parse stack not empty!" if @hash_stack.size > 1
  74003. end
  74004. def error(error_message)
  74005. raise error_message
  74006. end
  74007. def start_element(name, attrs = [])
  74008. new_hash = { CONTENT_KEY => '' }.merge(Hash[attrs])
  74009. new_hash[HASH_SIZE_KEY] = new_hash.size + 1
  74010. case current_hash[name]
  74011. when Array then current_hash[name] << new_hash
  74012. when Hash then current_hash[name] = [current_hash[name], new_hash]
  74013. when nil then current_hash[name] = new_hash
  74014. end
  74015. @hash_stack.push(new_hash)
  74016. end
  74017. def end_element(name)
  74018. if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == ''
  74019. current_hash.delete(CONTENT_KEY)
  74020. end
  74021. @hash_stack.pop
  74022. end
  74023. def characters(string)
  74024. current_hash[CONTENT_KEY] << string
  74025. end
  74026. alias_method :cdata_block, :characters
  74027. end
  74028. attr_accessor :document_class
  74029. self.document_class = HashBuilder
  74030. def parse(data)
  74031. if !data.respond_to?(:read)
  74032. data = StringIO.new(data || '')
  74033. end
  74034. char = data.getc
  74035. if char.nil?
  74036. {}
  74037. else
  74038. data.ungetc(char)
  74039. document = self.document_class.new
  74040. parser = Nokogiri::XML::SAX::Parser.new(document)
  74041. parser.parse(data)
  74042. document.hash
  74043. end
  74044. end
  74045. end
  74046. end
  74047. require 'active_support/core_ext/kernel/reporting'
  74048. require 'active_support/core_ext/object/blank'
  74049. require 'stringio'
  74050. module ActiveSupport
  74051. module XmlMini_REXML #:nodoc:
  74052. extend self
  74053. CONTENT_KEY = '__content__'.freeze
  74054. # Parse an XML Document string or IO into a simple hash.
  74055. #
  74056. # Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
  74057. # and uses the defaults from Active Support.
  74058. #
  74059. # data::
  74060. # XML Document string or IO to parse
  74061. def parse(data)
  74062. if !data.respond_to?(:read)
  74063. data = StringIO.new(data || '')
  74064. end
  74065. char = data.getc
  74066. if char.nil?
  74067. {}
  74068. else
  74069. data.ungetc(char)
  74070. silence_warnings { require 'rexml/document' } unless defined?(REXML::Document)
  74071. doc = REXML::Document.new(data)
  74072. if doc.root
  74073. merge_element!({}, doc.root)
  74074. else
  74075. raise REXML::ParseException,
  74076. "The document #{doc.to_s.inspect} does not have a valid root"
  74077. end
  74078. end
  74079. end
  74080. private
  74081. # Convert an XML element and merge into the hash
  74082. #
  74083. # hash::
  74084. # Hash to merge the converted element into.
  74085. # element::
  74086. # XML element to merge into hash
  74087. def merge_element!(hash, element)
  74088. merge!(hash, element.name, collapse(element))
  74089. end
  74090. # Actually converts an XML document element into a data structure.
  74091. #
  74092. # element::
  74093. # The document element to be collapsed.
  74094. def collapse(element)
  74095. hash = get_attributes(element)
  74096. if element.has_elements?
  74097. element.each_element {|child| merge_element!(hash, child) }
  74098. merge_texts!(hash, element) unless empty_content?(element)
  74099. hash
  74100. else
  74101. merge_texts!(hash, element)
  74102. end
  74103. end
  74104. # Merge all the texts of an element into the hash
  74105. #
  74106. # hash::
  74107. # Hash to add the converted element to.
  74108. # element::
  74109. # XML element whose texts are to me merged into the hash
  74110. def merge_texts!(hash, element)
  74111. unless element.has_text?
  74112. hash
  74113. else
  74114. # must use value to prevent double-escaping
  74115. texts = ''
  74116. element.texts.each { |t| texts << t.value }
  74117. merge!(hash, CONTENT_KEY, texts)
  74118. end
  74119. end
  74120. # Adds a new key/value pair to an existing Hash. If the key to be added
  74121. # already exists and the existing value associated with key is not
  74122. # an Array, it will be wrapped in an Array. Then the new value is
  74123. # appended to that Array.
  74124. #
  74125. # hash::
  74126. # Hash to add key/value pair to.
  74127. # key::
  74128. # Key to be added.
  74129. # value::
  74130. # Value to be associated with key.
  74131. def merge!(hash, key, value)
  74132. if hash.has_key?(key)
  74133. if hash[key].instance_of?(Array)
  74134. hash[key] << value
  74135. else
  74136. hash[key] = [hash[key], value]
  74137. end
  74138. elsif value.instance_of?(Array)
  74139. hash[key] = [value]
  74140. else
  74141. hash[key] = value
  74142. end
  74143. hash
  74144. end
  74145. # Converts the attributes array of an XML element into a hash.
  74146. # Returns an empty Hash if node has no attributes.
  74147. #
  74148. # element::
  74149. # XML element to extract attributes from.
  74150. def get_attributes(element)
  74151. attributes = {}
  74152. element.attributes.each { |n,v| attributes[n] = v }
  74153. attributes
  74154. end
  74155. # Determines if a document element has text content
  74156. #
  74157. # element::
  74158. # XML element to be checked.
  74159. def empty_content?(element)
  74160. element.texts.join.blank?
  74161. end
  74162. end
  74163. end
  74164. require 'time'
  74165. require 'base64'
  74166. require 'active_support/core_ext/module/delegation'
  74167. require 'active_support/core_ext/string/inflections'
  74168. module ActiveSupport
  74169. # = XmlMini
  74170. #
  74171. # To use the much faster libxml parser:
  74172. # gem 'libxml-ruby', '=0.9.7'
  74173. # XmlMini.backend = 'LibXML'
  74174. module XmlMini
  74175. extend self
  74176. # This module decorates files deserialized using Hash.from_xml with
  74177. # the <tt>original_filename</tt> and <tt>content_type</tt> methods.
  74178. module FileLike #:nodoc:
  74179. attr_writer :original_filename, :content_type
  74180. def original_filename
  74181. @original_filename || 'untitled'
  74182. end
  74183. def content_type
  74184. @content_type || 'application/octet-stream'
  74185. end
  74186. end
  74187. DEFAULT_ENCODINGS = {
  74188. "binary" => "base64"
  74189. } unless defined?(DEFAULT_ENCODINGS)
  74190. TYPE_NAMES = {
  74191. "Symbol" => "symbol",
  74192. "Fixnum" => "integer",
  74193. "Bignum" => "integer",
  74194. "BigDecimal" => "decimal",
  74195. "Float" => "float",
  74196. "TrueClass" => "boolean",
  74197. "FalseClass" => "boolean",
  74198. "Date" => "date",
  74199. "DateTime" => "dateTime",
  74200. "Time" => "dateTime",
  74201. "Array" => "array",
  74202. "Hash" => "hash"
  74203. } unless defined?(TYPE_NAMES)
  74204. FORMATTING = {
  74205. "symbol" => Proc.new { |symbol| symbol.to_s },
  74206. "date" => Proc.new { |date| date.to_s(:db) },
  74207. "dateTime" => Proc.new { |time| time.xmlschema },
  74208. "binary" => Proc.new { |binary| ::Base64.encode64(binary) },
  74209. "yaml" => Proc.new { |yaml| yaml.to_yaml }
  74210. } unless defined?(FORMATTING)
  74211. # TODO use regexp instead of Date.parse
  74212. unless defined?(PARSING)
  74213. PARSING = {
  74214. "symbol" => Proc.new { |symbol| symbol.to_sym },
  74215. "date" => Proc.new { |date| ::Date.parse(date) },
  74216. "datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
  74217. "integer" => Proc.new { |integer| integer.to_i },
  74218. "float" => Proc.new { |float| float.to_f },
  74219. "decimal" => Proc.new { |number| BigDecimal(number) },
  74220. "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
  74221. "string" => Proc.new { |string| string.to_s },
  74222. "yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
  74223. "base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
  74224. "binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
  74225. "file" => Proc.new { |file, entity| _parse_file(file, entity) }
  74226. }
  74227. PARSING.update(
  74228. "double" => PARSING["float"],
  74229. "dateTime" => PARSING["datetime"]
  74230. )
  74231. end
  74232. delegate :parse, :to => :backend
  74233. def backend
  74234. current_thread_backend || @backend
  74235. end
  74236. def backend=(name)
  74237. backend = name && cast_backend_name_to_module(name)
  74238. self.current_thread_backend = backend if current_thread_backend
  74239. @backend = backend
  74240. end
  74241. def with_backend(name)
  74242. old_backend = current_thread_backend
  74243. self.current_thread_backend = name && cast_backend_name_to_module(name)
  74244. yield
  74245. ensure
  74246. self.current_thread_backend = old_backend
  74247. end
  74248. def to_tag(key, value, options)
  74249. type_name = options.delete(:type)
  74250. merged_options = options.merge(:root => key, :skip_instruct => true)
  74251. if value.is_a?(::Method) || value.is_a?(::Proc)
  74252. if value.arity == 1
  74253. value.call(merged_options)
  74254. else
  74255. value.call(merged_options, key.to_s.singularize)
  74256. end
  74257. elsif value.respond_to?(:to_xml)
  74258. value.to_xml(merged_options)
  74259. else
  74260. type_name ||= TYPE_NAMES[value.class.name]
  74261. type_name ||= value.class.name if value && !value.respond_to?(:to_str)
  74262. type_name = type_name.to_s if type_name
  74263. type_name = "dateTime" if type_name == "datetime"
  74264. key = rename_key(key.to_s, options)
  74265. attributes = options[:skip_types] || type_name.nil? ? { } : { :type => type_name }
  74266. attributes[:nil] = true if value.nil?
  74267. encoding = options[:encoding] || DEFAULT_ENCODINGS[type_name]
  74268. attributes[:encoding] = encoding if encoding
  74269. formatted_value = FORMATTING[type_name] && !value.nil? ?
  74270. FORMATTING[type_name].call(value) : value
  74271. options[:builder].tag!(key, formatted_value, attributes)
  74272. end
  74273. end
  74274. def rename_key(key, options = {})
  74275. camelize = options[:camelize]
  74276. dasherize = !options.has_key?(:dasherize) || options[:dasherize]
  74277. if camelize
  74278. key = true == camelize ? key.camelize : key.camelize(camelize)
  74279. end
  74280. key = _dasherize(key) if dasherize
  74281. key
  74282. end
  74283. protected
  74284. def _dasherize(key)
  74285. # $2 must be a non-greedy regex for this to work
  74286. left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1,3]
  74287. "#{left}#{middle.tr('_ ', '--')}#{right}"
  74288. end
  74289. # TODO: Add support for other encodings
  74290. def _parse_binary(bin, entity) #:nodoc:
  74291. case entity['encoding']
  74292. when 'base64'
  74293. ::Base64.decode64(bin)
  74294. else
  74295. bin
  74296. end
  74297. end
  74298. def _parse_file(file, entity)
  74299. f = StringIO.new(::Base64.decode64(file))
  74300. f.extend(FileLike)
  74301. f.original_filename = entity['name']
  74302. f.content_type = entity['content_type']
  74303. f
  74304. end
  74305. private
  74306. def current_thread_backend
  74307. Thread.current[:xml_mini_backend]
  74308. end
  74309. def current_thread_backend=(name)
  74310. Thread.current[:xml_mini_backend] = name && cast_backend_name_to_module(name)
  74311. end
  74312. def cast_backend_name_to_module(name)
  74313. if name.is_a?(Module)
  74314. name
  74315. else
  74316. require "active_support/xml_mini/#{name.downcase}"
  74317. ActiveSupport.const_get("XmlMini_#{name}")
  74318. end
  74319. end
  74320. end
  74321. XmlMini.backend = 'REXML'
  74322. end
  74323. #--
  74324. # Copyright (c) 2005-2013 David Heinemeier Hansson
  74325. #
  74326. # Permission is hereby granted, free of charge, to any person obtaining
  74327. # a copy of this software and associated documentation files (the
  74328. # "Software"), to deal in the Software without restriction, including
  74329. # without limitation the rights to use, copy, modify, merge, publish,
  74330. # distribute, sublicense, and/or sell copies of the Software, and to
  74331. # permit persons to whom the Software is furnished to do so, subject to
  74332. # the following conditions:
  74333. #
  74334. # The above copyright notice and this permission notice shall be
  74335. # included in all copies or substantial portions of the Software.
  74336. #
  74337. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  74338. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  74339. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  74340. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  74341. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  74342. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  74343. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  74344. #++
  74345. require 'securerandom'
  74346. require "active_support/dependencies/autoload"
  74347. require "active_support/version"
  74348. require "active_support/logger"
  74349. require "active_support/lazy_load_hooks"
  74350. module ActiveSupport
  74351. extend ActiveSupport::Autoload
  74352. autoload :Concern
  74353. autoload :Dependencies
  74354. autoload :DescendantsTracker
  74355. autoload :FileUpdateChecker
  74356. autoload :LogSubscriber
  74357. autoload :Notifications
  74358. eager_autoload do
  74359. autoload :BacktraceCleaner
  74360. autoload :BasicObject
  74361. autoload :ProxyObject
  74362. autoload :Benchmarkable
  74363. autoload :Cache
  74364. autoload :Callbacks
  74365. autoload :Configurable
  74366. autoload :Deprecation
  74367. autoload :Gzip
  74368. autoload :Inflector
  74369. autoload :JSON
  74370. autoload :KeyGenerator
  74371. autoload :MessageEncryptor
  74372. autoload :MessageVerifier
  74373. autoload :Multibyte
  74374. autoload :OptionMerger
  74375. autoload :OrderedHash
  74376. autoload :OrderedOptions
  74377. autoload :StringInquirer
  74378. autoload :TaggedLogging
  74379. autoload :XmlMini
  74380. end
  74381. autoload :Rescuable
  74382. autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
  74383. autoload :TestCase
  74384. end
  74385. autoload :I18n, "active_support/i18n"
  74386. class ApplicationController < ActionController::Base
  74387. # Prevent CSRF attacks by raising an exception.
  74388. # For APIs, you may want to use :reset_session instead.
  74389. protect_from_forgery :with => :exception
  74390. endmodule ApplicationHelper
  74391. end
  74392. require File.expand_path('../boot', __FILE__)
  74393. require 'rails/all'
  74394. if defined?(Bundler)
  74395. # If you precompile assets before deploying to production, use this line.
  74396. Bundler.require(*Rails.groups(:assets => %w(development test)))
  74397. # If you want your assets lazily compiled in production, use this line.
  74398. # Bundler.require(:default, :assets, Rails.env)
  74399. end
  74400. module Blog
  74401. class Application < Rails::Application
  74402. # Settings in config/environments/* take precedence over those specified here.
  74403. # Application configuration should go into files in config/initializers
  74404. # -- all .rb files in that directory are automatically loaded.
  74405. # Custom directories with classes and modules you want to be autoloadable.
  74406. # config.autoload_paths += %W(#{config.root}/extras)
  74407. # Activate observers that should always be running.
  74408. # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
  74409. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
  74410. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
  74411. # config.time_zone = 'Central Time (US & Canada)'
  74412. # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
  74413. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
  74414. # config.i18n.default_locale = :de
  74415. # Configure the default encoding used in templates for Ruby 1.9.
  74416. config.encoding = "utf-8"
  74417. # Configure sensitive parameters which will be filtered from the log file.
  74418. config.filter_parameters += [:password]
  74419. # Enable escaping HTML in JSON. The default is false.
  74420. # config.active_support.escape_html_entities_in_json = true
  74421. # Use SQL instead of Active Record's schema dumper when creating the database.
  74422. # This is necessary if your schema can't be completely dumped by the schema dumper,
  74423. # like if you have constraints or database-specific column types.
  74424. # config.active_record.schema_format = :sql
  74425. # Enforce whitelist mode for mass assignment.
  74426. # This will create an empty whitelist of attributes available for mass-assignment for all models
  74427. # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
  74428. # parameters by using an attr_accessible or attr_protected declaration.
  74429. config.active_record.whitelist_attributes = true
  74430. # Specifies whether or not has_many or has_one association option :dependent => :restrict raises
  74431. # an exception. If set to true, then an ActiveRecord::DeleteRestrictionError exception would be
  74432. # raised. If set to false, then an error will be added on the model instead.
  74433. config.active_record.dependent_restrict_raises = false
  74434. # Enable the asset pipeline.
  74435. config.assets.enabled = true
  74436. # Version of your assets, change this if you want to expire all your assets.
  74437. config.assets.version = '1.0'
  74438. end
  74439. end
  74440. # Set up gems listed in the Gemfile.
  74441. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
  74442. require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
  74443. # Load the rails application
  74444. require File.expand_path('../application', __FILE__)
  74445. # Initialize the rails application
  74446. Blog::Application.initialize!
  74447. Blog::Application.configure do
  74448. # Settings specified here will take precedence over those in config/application.rb.
  74449. # In the development environment your application's code is reloaded on
  74450. # every request. This slows down response time but is perfect for development
  74451. # since you don't have to restart the web server when you make code changes.
  74452. config.cache_classes = false
  74453. # Show full error reports and disable caching.
  74454. config.consider_all_requests_local = true
  74455. config.action_controller.perform_caching = false
  74456. # Don't care if the mailer can't send.
  74457. config.action_mailer.raise_delivery_errors = false
  74458. # Print deprecation notices to the Rails logger.
  74459. config.active_support.deprecation = :log
  74460. # Only use best-standards-support built into browsers.
  74461. config.action_dispatch.best_standards_support = :builtin
  74462. # Raise exception on mass assignment protection for Active Record models.
  74463. config.active_record.mass_assignment_sanitizer = :strict
  74464. # Log the query plan for queries taking more than this (works
  74465. # with SQLite, MySQL, and PostgreSQL).
  74466. config.active_record.auto_explain_threshold_in_seconds = 0.5
  74467. # Do not compress assets.
  74468. config.assets.compress = false
  74469. # Expands the lines which load the assets.
  74470. config.assets.debug = true
  74471. # In development, use an in-memory queue for queueing
  74472. config.queue = Rails::Queueing::Queue
  74473. end
  74474. Blog::Application.configure do
  74475. # Settings specified here will take precedence over those in config/application.rb.
  74476. # Code is not reloaded between requests.
  74477. config.cache_classes = true
  74478. # Full error reports are disabled and caching is turned on.
  74479. config.consider_all_requests_local = false
  74480. config.action_controller.perform_caching = true
  74481. # Disable Rails's static asset server (Apache or nginx will already do this).
  74482. config.serve_static_assets = false
  74483. # Compress JavaScripts and CSS.
  74484. config.assets.compress = true
  74485. # Don't fallback to assets pipeline if a precompiled asset is missed.
  74486. config.assets.compile = false
  74487. # Generate digests for assets URLs.
  74488. config.assets.digest = true
  74489. # Defaults to nil and saved in location specified by config.assets.prefix
  74490. # config.assets.manifest = YOUR_PATH
  74491. # Specifies the header that your server uses for sending files.
  74492. # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
  74493. # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
  74494. # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  74495. # config.force_ssl = true
  74496. # Set to :debug to see everything in the log.
  74497. config.log_level = :info
  74498. # Prepend all log lines with the following tags.
  74499. # config.log_tags = [ :subdomain, :uuid ]
  74500. # Use a different logger for distributed setups.
  74501. # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
  74502. # Use a different cache store in production.
  74503. # config.cache_store = :mem_cache_store
  74504. # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  74505. # config.action_controller.asset_host = "http://assets.example.com"
  74506. # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added).
  74507. # config.assets.precompile += %w( search.js )
  74508. # Disable delivery errors, bad email addresses will be ignored.
  74509. # config.action_mailer.raise_delivery_errors = false
  74510. # Enable threaded mode.
  74511. # config.threadsafe!
  74512. # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  74513. # the I18n.default_locale when a translation can not be found).
  74514. config.i18n.fallbacks = true
  74515. # Send deprecation notices to registered listeners.
  74516. config.active_support.deprecation = :notify
  74517. # Log the query plan for queries taking more than this (works
  74518. # with SQLite, MySQL, and PostgreSQL).
  74519. # config.active_record.auto_explain_threshold_in_seconds = 0.5
  74520. # Disable automatic flushing of the log to improve performance.
  74521. # config.autoflush_log = false
  74522. # Use default logging formatter so that PID and timestamp are not suppressed
  74523. config.log_formatter = ::Logger::Formatter.new
  74524. # Default the production mode queue to an in-memory queue. You will probably
  74525. # want to replace this with an out-of-process queueing solution
  74526. config.queue = Rails::Queueing::Queue
  74527. end
  74528. Blog::Application.configure do
  74529. # Settings specified here will take precedence over those in config/application.rb.
  74530. # The test environment is used exclusively to run your application's
  74531. # test suite. You never need to work with it otherwise. Remember that
  74532. # your test database is "scratch space" for the test suite and is wiped
  74533. # and recreated between test runs. Don't rely on the data there!
  74534. config.cache_classes = true
  74535. # Configure static asset server for tests with Cache-Control for performance.
  74536. config.serve_static_assets = true
  74537. config.static_cache_control = "public, max-age=3600"
  74538. # Show full error reports and disable caching.
  74539. config.consider_all_requests_local = true
  74540. config.action_controller.perform_caching = false
  74541. # Raise exceptions instead of rendering exception templates.
  74542. config.action_dispatch.show_exceptions = false
  74543. # Disable request forgery protection in test environment.
  74544. config.action_controller.allow_forgery_protection = false
  74545. # Tell Action Mailer not to deliver emails to the real world.
  74546. # The :test delivery method accumulates sent emails in the
  74547. # ActionMailer::Base.deliveries array.
  74548. config.action_mailer.delivery_method = :test
  74549. # Raise exception on mass assignment protection for Active Record models.
  74550. config.active_record.mass_assignment_sanitizer = :strict
  74551. # Print deprecation notices to the stderr.
  74552. config.active_support.deprecation = :stderr
  74553. # Use the testing queue
  74554. config.queue = Rails::Queueing::TestQueue
  74555. end
  74556. # Be sure to restart your server when you modify this file.
  74557. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
  74558. # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
  74559. # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
  74560. # Rails.backtrace_cleaner.remove_silencers!
  74561. # Be sure to restart your server when you modify this file.
  74562. # Add new inflection rules using the following format
  74563. # (all these examples are active by default):
  74564. # ActiveSupport::Inflector.inflections do |inflect|
  74565. # inflect.plural /^(ox)$/i, '\1en'
  74566. # inflect.singular /^(ox)en/i, '\1'
  74567. # inflect.irregular 'person', 'people'
  74568. # inflect.uncountable %w( fish sheep )
  74569. # end
  74570. #
  74571. # These inflection rules are supported but not enabled by default:
  74572. # ActiveSupport::Inflector.inflections do |inflect|
  74573. # inflect.acronym 'RESTful'
  74574. # end
  74575. # Be sure to restart your server when you modify this file.
  74576. # Add new mime types for use in respond_to blocks:
  74577. # Mime::Type.register "text/richtext", :rtf
  74578. # Mime::Type.register_alias "text/html", :iphone
  74579. # Be sure to restart your server when you modify this file.
  74580. # Your secret key for verifying the integrity of signed cookies.
  74581. # If you change this key, all old signed cookies will become invalid!
  74582. # Make sure the secret is at least 30 characters and all random,
  74583. # no regular words or you'll be exposed to dictionary attacks.
  74584. # Make sure your secret_token is kept private
  74585. # if you're sharing your code publicly.
  74586. Blog::Application.config.secret_token = 'c3506f27fb9dccbc56aaa14aef6bbbd2f2c115ba3198830bc97dca5bcc0d55db9fd12caa8aabeaa6fd31a12316575d2ef09c1b340b3fb168596ad3ad5196eb6a'
  74587. # Be sure to restart your server when you modify this file.
  74588. Blog::Application.config.session_store :cookie_store, key: '_blog_session'
  74589. # Use the database for sessions instead of the cookie-based default,
  74590. # which shouldn't be used to store highly confidential information
  74591. # (create the session table with "rails generate session_migration")
  74592. # Blog::Application.config.session_store :active_record_store
  74593. # Be sure to restart your server when you modify this file.
  74594. #
  74595. # This file contains settings for ActionController::ParamsWrapper which
  74596. # is enabled by default.
  74597. # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
  74598. ActiveSupport.on_load(:action_controller) do
  74599. wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
  74600. end
  74601. # Disable root element in JSON by default.
  74602. ActiveSupport.on_load(:active_record) do
  74603. self.include_root_in_json = false
  74604. end
  74605. Blog::Application.routes.draw do
  74606. # The priority is based upon order of creation:
  74607. # first created -> highest priority.
  74608. # Sample of regular route:
  74609. # get 'products/:id' => 'catalog#view'
  74610. # Keep in mind you can assign values other than :controller and :action
  74611. # Sample of named route:
  74612. # get 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
  74613. # This route can be invoked with purchase_url(:id => product.id)
  74614. # Sample resource route (maps HTTP verbs to controller actions automatically):
  74615. # resources :products
  74616. # Sample resource route with options:
  74617. # resources :products do
  74618. # member do
  74619. # get 'short'
  74620. # post 'toggle'
  74621. # end
  74622. #
  74623. # collection do
  74624. # get 'sold'
  74625. # end
  74626. # end
  74627. # Sample resource route with sub-resources:
  74628. # resources :products do
  74629. # resources :comments, :sales
  74630. # resource :seller
  74631. # end
  74632. # Sample resource route with more complex sub-resources
  74633. # resources :products do
  74634. # resources :comments
  74635. # resources :sales do
  74636. # get 'recent', :on => :collection
  74637. # end
  74638. # end
  74639. # Sample resource route within a namespace:
  74640. # namespace :admin do
  74641. # # Directs /admin/products/* to Admin::ProductsController
  74642. # # (app/controllers/admin/products_controller.rb)
  74643. # resources :products
  74644. # end
  74645. # You can have the root of your site routed with "root"
  74646. # just remember to delete public/index.html.
  74647. # root :to => 'welcome#index'
  74648. # See how all your routes lay out with "rake routes"
  74649. end# This file should contain all the record creation needed to seed the database with its default values.
  74650. # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
  74651. #
  74652. # Examples:
  74653. #
  74654. # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
  74655. # Mayor.create(name: 'Emanuel', city: cities.first)
  74656. #!/usr/bin/env ruby
  74657. require 'fileutils'
  74658. include FileUtils
  74659. commands = [
  74660. 'mysql -e "create database activerecord_unittest;"',
  74661. 'mysql -e "create database activerecord_unittest2;"',
  74662. 'psql -c "create database activerecord_unittest;" -U postgres',
  74663. 'psql -c "create database activerecord_unittest2;" -U postgres'
  74664. ]
  74665. commands.each do |command|
  74666. system("#{command} > /dev/null 2>&1")
  74667. end
  74668. class Build
  74669. MAP = {
  74670. 'railties' => 'railties',
  74671. 'ap' => 'actionpack',
  74672. 'am' => 'actionmailer',
  74673. 'amo' => 'activemodel',
  74674. 'as' => 'activesupport',
  74675. 'ar' => 'activerecord'
  74676. }
  74677. attr_reader :component, :options
  74678. def initialize(component, options = {})
  74679. @component = component
  74680. @options = options
  74681. end
  74682. def run!(options = {})
  74683. self.options.update(options)
  74684. Dir.chdir(dir) do
  74685. announce(heading)
  74686. rake(*tasks)
  74687. end
  74688. end
  74689. def announce(heading)
  74690. puts "\n\e[1;33m[Travis CI] #{heading}\e[m\n"
  74691. end
  74692. def heading
  74693. heading = [gem]
  74694. heading << "with #{adapter}" if activerecord?
  74695. heading << "in isolation" if isolated?
  74696. heading.join(' ')
  74697. end
  74698. def tasks
  74699. if activerecord?
  74700. ['mysql:rebuild_databases', "#{adapter}:#{'isolated_' if isolated?}test"]
  74701. else
  74702. ["test#{':isolated' if isolated?}"]
  74703. end
  74704. end
  74705. def key
  74706. key = [gem]
  74707. key << adapter if activerecord?
  74708. key << 'isolated' if isolated?
  74709. key.join(':')
  74710. end
  74711. def activerecord?
  74712. gem == 'activerecord'
  74713. end
  74714. def isolated?
  74715. options[:isolated]
  74716. end
  74717. def gem
  74718. MAP[component.split(':').first]
  74719. end
  74720. alias :dir :gem
  74721. def adapter
  74722. component.split(':').last
  74723. end
  74724. def rake(*tasks)
  74725. tasks.each do |task|
  74726. cmd = "bundle exec rake #{task}"
  74727. puts "Running command: #{cmd}"
  74728. return false unless system(cmd)
  74729. end
  74730. true
  74731. end
  74732. end
  74733. results = {}
  74734. ENV['GEM'].split(',').each do |gem|
  74735. [false, true].each do |isolated|
  74736. next if gem == 'railties' && isolated
  74737. build = Build.new(gem, :isolated => isolated)
  74738. results[build.key] = build.run!
  74739. end
  74740. end
  74741. # puts
  74742. # puts "Build environment:"
  74743. # puts " #{`cat /etc/issue`}"
  74744. # puts " #{`uname -a`}"
  74745. # puts " #{`ruby -v`}"
  74746. # puts " #{`mysql --version`}"
  74747. # # puts " #{`pg_config --version`}"
  74748. # puts " SQLite3: #{`sqlite3 -version`}"
  74749. # `gem env`.each_line {|line| print " #{line}"}
  74750. # puts " Bundled gems:"
  74751. # `bundle show`.each_line {|line| print " #{line}"}
  74752. # puts " Local gems:"
  74753. # `gem list`.each_line {|line| print " #{line}"}
  74754. failures = results.select { |key, value| value == false }
  74755. if failures.empty?
  74756. puts
  74757. puts "Rails build finished successfully"
  74758. exit(true)
  74759. else
  74760. puts
  74761. puts "Rails build FAILED"
  74762. puts "Failed components: #{failures.map { |component| component.first }.join(', ')}"
  74763. exit(false)
  74764. end
  74765. class ApplicationController < ActionController::Base
  74766. # Prevent CSRF attacks by raising an exception.
  74767. # For APIs, you may want to use :null_session instead.
  74768. protect_from_forgery with: :exception
  74769. end
  74770. class CommentsController < ApplicationController
  74771.   http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy
  74772. def create
  74773. @post = Post.find(params[:post_id])
  74774. @comment = @post.comments.create(params[:comment].permit(:commenter, :body))
  74775. redirect_to post_path(@post)
  74776. end
  74777. def destroy
  74778. @post = Post.find(params[:post_id])
  74779. @comment = @post.comments.find(params[:id])
  74780. @comment.destroy
  74781. redirect_to post_path(@post)
  74782. end
  74783. end
  74784. class PostsController < ApplicationController
  74785.   http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
  74786. def index
  74787. @posts = Post.all
  74788. end
  74789. def show
  74790. @post = Post.find(params[:id])
  74791. end
  74792. def edit
  74793. @post = Post.find(params[:id])
  74794. end
  74795. def update
  74796. @post = Post.find(params[:id])
  74797. if @post.update(params[:post].permit(:title, :text))
  74798. redirect_to action: :show, id: @post.id
  74799. else
  74800. render 'edit'
  74801. end
  74802. end
  74803. def new
  74804. @post = Post.new
  74805. end
  74806. def create
  74807. @post = Post.new(params[:post].permit(:title, :text))
  74808. if @post.save
  74809. redirect_to action: :show, id: @post.id
  74810. else
  74811. render 'new'
  74812. end
  74813. end
  74814. def destroy
  74815. @post = Post.find(params[:id])
  74816. @post.destroy
  74817. redirect_to action: :index
  74818. end
  74819. end
  74820. class WelcomeController < ApplicationController
  74821. def index
  74822. end
  74823. end
  74824. module ApplicationHelper
  74825. end
  74826. module CommentsHelper
  74827. end
  74828. module PostsHelper
  74829. end
  74830. module WelcomeHelper
  74831. end
  74832. class Comment < ActiveRecord::Base
  74833. belongs_to :post
  74834. end
  74835. class Post < ActiveRecord::Base
  74836. has_many :comments, dependent: :destroy
  74837. validates :title,
  74838. presence: true,
  74839. length: { minimum: 5 }
  74840. end
  74841. require File.expand_path('../boot', __FILE__)
  74842. require 'rails/all'
  74843. # Assets should be precompiled for production (so we don't need the gems loaded then)
  74844. Bundler.require(*Rails.groups(assets: %w(development test)))
  74845. module Blog
  74846. class Application < Rails::Application
  74847. # Settings in config/environments/* take precedence over those specified here.
  74848. # Application configuration should go into files in config/initializers
  74849. # -- all .rb files in that directory are automatically loaded.
  74850. # Custom directories with classes and modules you want to be autoloadable.
  74851. # config.autoload_paths += %W(#{config.root}/extras)
  74852. end
  74853. end
  74854. # Set up gems listed in the Gemfile.
  74855. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
  74856. require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
  74857. # Load the rails application.
  74858. require File.expand_path('../application', __FILE__)
  74859. # Initialize the rails application.
  74860. Blog::Application.initialize!
  74861. Blog::Application.configure do
  74862. # Settings specified here will take precedence over those in config/application.rb.
  74863. # In the development environment your application's code is reloaded on
  74864. # every request. This slows down response time but is perfect for development
  74865. # since you don't have to restart the web server when you make code changes.
  74866. config.cache_classes = false
  74867. # Do not eager load code on boot.
  74868. config.eager_load = false
  74869. # Show full error reports and disable caching.
  74870. config.consider_all_requests_local = true
  74871. config.action_controller.perform_caching = false
  74872. # Don't care if the mailer can't send.
  74873. config.action_mailer.raise_delivery_errors = false
  74874. # Print deprecation notices to the Rails logger.
  74875. config.active_support.deprecation = :log
  74876. # Only use best-standards-support built into browsers.
  74877. config.action_dispatch.best_standards_support = :builtin
  74878. # Log the query plan for queries taking more than this (works
  74879. # with SQLite, MySQL, and PostgreSQL).
  74880. config.active_record.auto_explain_threshold_in_seconds = 0.5
  74881. # Raise an error on page load if there are pending migrations
  74882. config.active_record.migration_error = :page_load
  74883. # Debug mode disables concatenation and preprocessing of assets.
  74884. config.assets.debug = true
  74885. end
  74886. Blog::Application.configure do
  74887. # Settings specified here will take precedence over those in config/application.rb.
  74888. # Code is not reloaded between requests.
  74889. config.cache_classes = true
  74890. # Eager load code on boot. This eager loads most of Rails and
  74891. # your application in memory, allowing both thread web servers
  74892. # and those relying on copy on write to perform better.
  74893. # Rake tasks automatically ignore this option for performance.
  74894. config.eager_load = true
  74895. # Full error reports are disabled and caching is turned on.
  74896. config.consider_all_requests_local = false
  74897. config.action_controller.perform_caching = true
  74898. # Enable Rack::Cache to put a simple HTTP cache in front of your application
  74899. # Add `rack-cache` to your Gemfile before enabling this.
  74900. # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
  74901. # config.action_dispatch.rack_cache = true
  74902. # Disable Rails's static asset server (Apache or nginx will already do this).
  74903. config.serve_static_assets = false
  74904. # Compress JavaScripts and CSS.
  74905. config.assets.js_compressor = :uglifier
  74906. # config.assets.css_compressor = :sass
  74907. # Whether to fallback to assets pipeline if a precompiled asset is missed.
  74908. config.assets.compile = false
  74909. # Generate digests for assets URLs.
  74910. config.assets.digest = true
  74911. # Version of your assets, change this if you want to expire all your assets.
  74912. config.assets.version = '1.0'
  74913. # Specifies the header that your server uses for sending files.
  74914. # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
  74915. # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
  74916. # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  74917. # config.force_ssl = true
  74918. # Set to :debug to see everything in the log.
  74919. config.log_level = :info
  74920. # Prepend all log lines with the following tags.
  74921. # config.log_tags = [ :subdomain, :uuid ]
  74922. # Use a different logger for distributed setups.
  74923. # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
  74924. # Use a different cache store in production.
  74925. # config.cache_store = :mem_cache_store
  74926. # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  74927. # config.action_controller.asset_host = "http://assets.example.com"
  74928. # Precompile additional assets.
  74929. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
  74930. # config.assets.precompile += %w( search.js )
  74931. # Ignore bad email addresses and do not raise email delivery errors.
  74932. # Set this to true and configure the email server for immediate delivery to raise delivery errors.
  74933. # config.action_mailer.raise_delivery_errors = false
  74934. # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  74935. # the I18n.default_locale when a translation can not be found).
  74936. config.i18n.fallbacks = true
  74937. # Send deprecation notices to registered listeners.
  74938. config.active_support.deprecation = :notify
  74939. # Log the query plan for queries taking more than this (works
  74940. # with SQLite, MySQL, and PostgreSQL).
  74941. # config.active_record.auto_explain_threshold_in_seconds = 0.5
  74942. # Disable automatic flushing of the log to improve performance.
  74943. # config.autoflush_log = false
  74944. # Use default logging formatter so that PID and timestamp are not suppressed.
  74945. config.log_formatter = ::Logger::Formatter.new
  74946. end
  74947. Blog::Application.configure do
  74948. # Settings specified here will take precedence over those in config/application.rb.
  74949. # The test environment is used exclusively to run your application's
  74950. # test suite. You never need to work with it otherwise. Remember that
  74951. # your test database is "scratch space" for the test suite and is wiped
  74952. # and recreated between test runs. Don't rely on the data there!
  74953. config.cache_classes = true
  74954. # Do not eager load code on boot. This avoids loading your whole application
  74955. # just for the purpose of running a single test. If you are using a tool that
  74956. # preloads Rails for running tests, you may have to set it to true.
  74957. config.eager_load = false
  74958. # Configure static asset server for tests with Cache-Control for performance.
  74959. config.serve_static_assets = true
  74960. config.static_cache_control = "public, max-age=3600"
  74961. # Show full error reports and disable caching.
  74962. config.consider_all_requests_local = true
  74963. config.action_controller.perform_caching = false
  74964. # Raise exceptions instead of rendering exception templates.
  74965. config.action_dispatch.show_exceptions = false
  74966. # Disable request forgery protection in test environment.
  74967. config.action_controller.allow_forgery_protection = false
  74968. # Tell Action Mailer not to deliver emails to the real world.
  74969. # The :test delivery method accumulates sent emails in the
  74970. # ActionMailer::Base.deliveries array.
  74971. config.action_mailer.delivery_method = :test
  74972. # Print deprecation notices to the stderr.
  74973. config.active_support.deprecation = :stderr
  74974. end
  74975. # Be sure to restart your server when you modify this file.
  74976. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
  74977. # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
  74978. # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
  74979. # Rails.backtrace_cleaner.remove_silencers!
  74980. # Be sure to restart your server when you modify this file.
  74981. # Configure sensitive parameters which will be filtered from the log file.
  74982. Rails.application.config.filter_parameters += [:password]
  74983. # Be sure to restart your server when you modify this file.
  74984. # Add new inflection rules using the following format. Inflections
  74985. # are locale specific, and you may define rules for as many different
  74986. # locales as you wish. All of these examples are active by default:
  74987. # ActiveSupport::Inflector.inflections(:en) do |inflect|
  74988. # inflect.plural /^(ox)$/i, '\1en'
  74989. # inflect.singular /^(ox)en/i, '\1'
  74990. # inflect.irregular 'person', 'people'
  74991. # inflect.uncountable %w( fish sheep )
  74992. # end
  74993. # These inflection rules are supported but not enabled by default:
  74994. # ActiveSupport::Inflector.inflections(:en) do |inflect|
  74995. # inflect.acronym 'RESTful'
  74996. # end
  74997. # Be sure to restart your server when you modify this file.
  74998. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
  74999. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
  75000. # Rails.application.config.time_zone = 'Central Time (US & Canada)'
  75001. # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
  75002. # Rails.application.config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
  75003. # Rails.application.config.i18n.default_locale = :de
  75004. # Be sure to restart your server when you modify this file.
  75005. # Add new mime types for use in respond_to blocks:
  75006. # Mime::Type.register "text/richtext", :rtf
  75007. # Mime::Type.register_alias "text/html", :iphone
  75008. # Be sure to restart your server when you modify this file.
  75009. # Your secret key for verifying the integrity of signed cookies.
  75010. # If you change this key, all old signed cookies will become invalid!
  75011. # Make sure the secret is at least 30 characters and all random,
  75012. # no regular words or you'll be exposed to dictionary attacks.
  75013. # You can use `rake secret` to generate a secure secret key.
  75014. # Make sure your secret_key_base is kept private
  75015. # if you're sharing your code publicly.
  75016. Blog::Application.config.secret_key_base = 'e8aab50cec8a06a75694111a4cbaf6e22fc288ccbc6b268683aae7273043c69b15ca07d10c92a788dd6077a54762cbfcc55f19c3459f7531221b3169f8171a53'
  75017. # Be sure to restart your server when you modify this file.
  75018. Blog::Application.config.session_store :encrypted_cookie_store, key: '_blog_session'
  75019. # Be sure to restart your server when you modify this file.
  75020. # This file contains settings for ActionController::ParamsWrapper which
  75021. # is enabled by default.
  75022. # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
  75023. ActiveSupport.on_load(:action_controller) do
  75024. wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
  75025. end
  75026. # To enable root element in JSON for ActiveRecord objects.
  75027. # ActiveSupport.on_load(:active_record) do
  75028. # self.include_root_in_json = true
  75029. # end
  75030. Blog::Application.routes.draw do
  75031. resources :posts do
  75032. resources :comments
  75033. end
  75034. root to: "welcome#index"
  75035. end
  75036. class CreatePosts < ActiveRecord::Migration
  75037. def change
  75038. create_table :posts do |t|
  75039. t.string :title
  75040. t.text :text
  75041. t.timestamps
  75042. end
  75043. end
  75044. end
  75045. class CreateComments < ActiveRecord::Migration
  75046. def change
  75047. create_table :comments do |t|
  75048. t.string :commenter
  75049. t.text :body
  75050. t.references :post, index: true
  75051. t.timestamps
  75052. end
  75053. end
  75054. end
  75055. # encoding: UTF-8
  75056. # This file is auto-generated from the current state of the database. Instead
  75057. # of editing this file, please use the migrations feature of Active Record to
  75058. # incrementally modify your database, and then regenerate this schema definition.
  75059. #
  75060. # Note that this schema.rb definition is the authoritative source for your
  75061. # database schema. If you need to create the application database on another
  75062. # system, you should be using db:schema:load, not running all the migrations
  75063. # from scratch. The latter is a flawed and unsustainable approach (the more migrations
  75064. # you'll amass, the slower it'll run and the greater likelihood for issues).
  75065. #
  75066. # It's strongly recommended that you check this file into your version control system.
  75067. ActiveRecord::Schema.define(version: 20130122045842) do
  75068. create_table "comments", force: true do |t|
  75069. t.string "commenter"
  75070. t.text "body"
  75071. t.integer "post_id"
  75072. t.datetime "created_at"
  75073. t.datetime "updated_at"
  75074. end
  75075. add_index "comments", ["post_id"], name: "index_comments_on_post_id"
  75076. create_table "posts", force: true do |t|
  75077. t.string "title"
  75078. t.text "text"
  75079. t.datetime "created_at"
  75080. t.datetime "updated_at"
  75081. end
  75082. end
  75083. # This file should contain all the record creation needed to seed the database with its default values.
  75084. # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
  75085. #
  75086. # Examples:
  75087. #
  75088. # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
  75089. # Mayor.create(name: 'Emanuel', city: cities.first)
  75090. # ---------------------------------------------------------------------------
  75091. #
  75092. # This script generates the guides. It can be invoked via the
  75093. # guides:generate rake task within the guides directory.
  75094. #
  75095. # Guides are taken from the source directory, and the resulting HTML goes into the
  75096. # output directory. Assets are stored under files, and copied to output/files as
  75097. # part of the generation process.
  75098. #
  75099. # Some arguments may be passed via environment variables:
  75100. #
  75101. # WARNINGS
  75102. # If you are writing a guide, please work always with WARNINGS=1. Users can
  75103. # generate the guides, and thus this flag is off by default.
  75104. #
  75105. # Internal links (anchors) are checked. If a reference is broken levenshtein
  75106. # distance is used to suggest an existing one. This is useful since IDs are
  75107. # generated by Markdown from headers and thus edits alter them.
  75108. #
  75109. # Also detects duplicated IDs. They happen if there are headers with the same
  75110. # text. Please do resolve them, if any, so guides are valid XHTML.
  75111. #
  75112. # ALL
  75113. # Set to "1" to force the generation of all guides.
  75114. #
  75115. # ONLY
  75116. # Use ONLY if you want to generate only one or a set of guides. Prefixes are
  75117. # enough:
  75118. #
  75119. # # generates only association_basics.html
  75120. # ONLY=assoc ruby rails_guides.rb
  75121. #
  75122. # Separate many using commas:
  75123. #
  75124. # # generates only association_basics.html and migrations.html
  75125. # ONLY=assoc,migrations ruby rails_guides.rb
  75126. #
  75127. # Note that if you are working on a guide generation will by default process
  75128. # only that one, so ONLY is rarely used nowadays.
  75129. #
  75130. # GUIDES_LANGUAGE
  75131. # Use GUIDES_LANGUAGE when you want to generate translated guides in
  75132. # <tt>source/<GUIDES_LANGUAGE></tt> folder (such as <tt>source/es</tt>).
  75133. # Ignore it when generating English guides.
  75134. #
  75135. # EDGE
  75136. # Set to "1" to indicate generated guides should be marked as edge. This
  75137. # inserts a badge and changes the preamble of the home page.
  75138. #
  75139. # ---------------------------------------------------------------------------
  75140. require 'set'
  75141. require 'fileutils'
  75142. require 'active_support/core_ext/string/output_safety'
  75143. require 'active_support/core_ext/object/blank'
  75144. require 'action_controller'
  75145. require 'action_view'
  75146. require 'rails_guides/indexer'
  75147. require 'rails_guides/helpers'
  75148. require 'rails_guides/levenshtein'
  75149. module RailsGuides
  75150. class Generator
  75151. attr_reader :guides_dir, :source_dir, :output_dir, :edge, :warnings, :all
  75152. GUIDES_RE = /\.(?:erb|md)\z/
  75153. def initialize(output=nil)
  75154. set_flags_from_environment
  75155. if kindle?
  75156. check_for_kindlegen
  75157. register_kindle_mime_types
  75158. end
  75159. initialize_dirs(output)
  75160. create_output_dir_if_needed
  75161. end
  75162. def set_flags_from_environment
  75163. @edge = ENV['EDGE'] == '1'
  75164. @warnings = ENV['WARNINGS'] == '1'
  75165. @all = ENV['ALL'] == '1'
  75166. @kindle = ENV['KINDLE'] == '1'
  75167. @version = ENV['RAILS_VERSION'] || 'local'
  75168. @lang = ENV['GUIDES_LANGUAGE']
  75169. end
  75170. def register_kindle_mime_types
  75171. Mime::Type.register_alias("application/xml", :opf, %w(opf))
  75172. Mime::Type.register_alias("application/xml", :ncx, %w(ncx))
  75173. end
  75174. def generate
  75175. generate_guides
  75176. copy_assets
  75177. generate_mobi if kindle?
  75178. end
  75179. private
  75180. def kindle?
  75181. @kindle
  75182. end
  75183. def check_for_kindlegen
  75184. if `which kindlegen`.blank?
  75185. raise "Can't create a kindle version without `kindlegen`."
  75186. end
  75187. end
  75188. def generate_mobi
  75189. require 'rails_guides/kindle'
  75190. out = "#{output_dir}/kindlegen.out"
  75191. Kindle.generate(output_dir, mobi, out)
  75192. puts "(kindlegen log at #{out})."
  75193. end
  75194. def mobi
  75195. "ruby_on_rails_guides_#@version%s.mobi" % (@lang.present? ? ".#@lang" : '')
  75196. end
  75197. def initialize_dirs(output)
  75198. @guides_dir = File.join(File.dirname(__FILE__), '..')
  75199. @source_dir = "#@guides_dir/source/#@lang"
  75200. @output_dir = if output
  75201. output
  75202. elsif kindle?
  75203. "#@guides_dir/output/kindle/#@lang"
  75204. else
  75205. "#@guides_dir/output/#@lang"
  75206. end.sub(%r</$>, '')
  75207. end
  75208. def create_output_dir_if_needed
  75209. FileUtils.mkdir_p(output_dir)
  75210. end
  75211. def generate_guides
  75212. guides_to_generate.each do |guide|
  75213. output_file = output_file_for(guide)
  75214. generate_guide(guide, output_file) if generate?(guide, output_file)
  75215. end
  75216. end
  75217. def guides_to_generate
  75218. guides = Dir.entries(source_dir).grep(GUIDES_RE)
  75219. if kindle?
  75220. Dir.entries("#{source_dir}/kindle").grep(GUIDES_RE).map do |entry|
  75221. next if entry == 'KINDLE.md'
  75222. guides << "kindle/#{entry}"
  75223. end
  75224. end
  75225. ENV.key?('ONLY') ? select_only(guides) : guides
  75226. end
  75227. def select_only(guides)
  75228. prefixes = ENV['ONLY'].split(",").map(&:strip)
  75229. guides.select do |guide|
  75230. prefixes.any? { |p| guide.start_with?(p) || guide.start_with?("kindle") }
  75231. end
  75232. end
  75233. def copy_assets
  75234. FileUtils.cp_r(Dir.glob("#{guides_dir}/assets/*"), output_dir)
  75235. end
  75236. def output_file_for(guide)
  75237. if guide.end_with?('.md')
  75238. guide.sub(/md\z/, 'html')
  75239. else
  75240. guide.sub(/\.erb\z/, '')
  75241. end
  75242. end
  75243. def output_path_for(output_file)
  75244. File.join(output_dir, File.basename(output_file))
  75245. end
  75246. def generate?(source_file, output_file)
  75247. fin = File.join(source_dir, source_file)
  75248. fout = output_path_for(output_file)
  75249. all || !File.exists?(fout) || File.mtime(fout) < File.mtime(fin)
  75250. end
  75251. def generate_guide(guide, output_file)
  75252. output_path = output_path_for(output_file)
  75253. puts "Generating #{guide} as #{output_file}"
  75254. layout = kindle? ? 'kindle/layout' : 'layout'
  75255. File.open(output_path, 'w') do |f|
  75256. view = ActionView::Base.new(source_dir, :edge => @edge, :version => @version, :mobi => "kindle/#{mobi}")
  75257. view.extend(Helpers)
  75258. if guide =~ /\.(\w+)\.erb$/
  75259. # Generate the special pages like the home.
  75260. # Passing a template handler in the template name is deprecated. So pass the file name without the extension.
  75261. result = view.render(:layout => layout, :formats => [$1], :file => $`)
  75262. else
  75263. body = File.read(File.join(source_dir, guide))
  75264. result = RailsGuides::Markdown.new(view, layout).render(body)
  75265. warn_about_broken_links(result) if @warnings
  75266. end
  75267. f.write(result)
  75268. end
  75269. end
  75270. def warn_about_broken_links(html)
  75271. anchors = extract_anchors(html)
  75272. check_fragment_identifiers(html, anchors)
  75273. end
  75274. def extract_anchors(html)
  75275. # Markdown generates headers with IDs computed from titles.
  75276. anchors = Set.new
  75277. html.scan(/<h\d\s+id="([^"]+)/).flatten.each do |anchor|
  75278. if anchors.member?(anchor)
  75279. puts "*** DUPLICATE ID: #{anchor}, please make sure that there're no headings with the same name at the same level."
  75280. else
  75281. anchors << anchor
  75282. end
  75283. end
  75284. # Footnotes.
  75285. anchors += Set.new(html.scan(/<p\s+class="footnote"\s+id="([^"]+)/).flatten)
  75286. anchors += Set.new(html.scan(/<sup\s+class="footnote"\s+id="([^"]+)/).flatten)
  75287. return anchors
  75288. end
  75289. def check_fragment_identifiers(html, anchors)
  75290. html.scan(/<a\s+href="#([^"]+)/).flatten.each do |fragment_identifier|
  75291. next if fragment_identifier == 'mainCol' # in layout, jumps to some DIV
  75292. unless anchors.member?(fragment_identifier)
  75293. guess = anchors.min { |a, b|
  75294. Levenshtein.distance(fragment_identifier, a) <=> Levenshtein.distance(fragment_identifier, b)
  75295. }
  75296. puts "*** BROKEN LINK: ##{fragment_identifier}, perhaps you meant ##{guess}."
  75297. end
  75298. end
  75299. end
  75300. end
  75301. end
  75302. module RailsGuides
  75303. module Helpers
  75304. def guide(name, url, options = {}, &block)
  75305. link = content_tag(:a, :href => url) { name }
  75306. result = content_tag(:dt, link)
  75307. if options[:work_in_progress]
  75308. result << content_tag(:dd, 'Work in progress', :class => 'work-in-progress')
  75309. end
  75310. result << content_tag(:dd, capture(&block))
  75311. result
  75312. end
  75313. def documents_by_section
  75314. @documents_by_section ||= YAML.load_file(File.expand_path('../../source/documents.yaml', __FILE__))
  75315. end
  75316. def documents_flat
  75317. documents_by_section.map {|section| section['documents']}.flatten
  75318. end
  75319. def finished_documents(documents)
  75320. documents.reject { |document| document['work_in_progress'] }
  75321. end
  75322. def docs_for_menu(position=nil)
  75323. if position.nil?
  75324. documents_by_section
  75325. elsif position == 'L'
  75326. documents_by_section.to(3)
  75327. else
  75328. documents_by_section.from(4)
  75329. end
  75330. end
  75331. def author(name, nick, image = 'credits_pic_blank.gif', &block)
  75332. image = "images/#{image}"
  75333. result = content_tag(:img, nil, :src => image, :class => 'left pic', :alt => name, :width => 91, :height => 91)
  75334. result << content_tag(:h3, name)
  75335. result << content_tag(:p, capture(&block))
  75336. content_tag(:div, result, :class => 'clearfix', :id => nick)
  75337. end
  75338. def code(&block)
  75339. c = capture(&block)
  75340. content_tag(:code, c)
  75341. end
  75342. end
  75343. end
  75344. require 'active_support/core_ext/object/blank'
  75345. require 'active_support/core_ext/string/inflections'
  75346. module RailsGuides
  75347. class Indexer
  75348. attr_reader :body, :result, :warnings, :level_hash
  75349. def initialize(body, warnings)
  75350. @body = body
  75351. @result = @body.dup
  75352. @warnings = warnings
  75353. end
  75354. def index
  75355. @level_hash = process(body)
  75356. end
  75357. private
  75358. def process(string, current_level=3, counters=[1])
  75359. s = StringScanner.new(string)
  75360. level_hash = {}
  75361. while !s.eos?
  75362. re = %r{^h(\d)(?:\((#.*?)\))?\s*\.\s*(.*)$}
  75363. s.match?(re)
  75364. if matched = s.matched
  75365. matched =~ re
  75366. level, idx, title = $1.to_i, $2, $3.strip
  75367. if level < current_level
  75368. # This is needed. Go figure.
  75369. return level_hash
  75370. elsif level == current_level
  75371. index = counters.join(".")
  75372. idx ||= '#' + title_to_idx(title)
  75373. raise "Parsing Fail" unless @result.sub!(matched, "h#{level}(#{idx}). #{index} #{title}")
  75374. key = {
  75375. :title => title,
  75376. :id => idx
  75377. }
  75378. # Recurse
  75379. counters << 1
  75380. level_hash[key] = process(s.post_match, current_level + 1, counters)
  75381. counters.pop
  75382. # Increment the current level
  75383. last = counters.pop
  75384. counters << last + 1
  75385. end
  75386. end
  75387. s.getch
  75388. end
  75389. level_hash
  75390. end
  75391. def title_to_idx(title)
  75392. idx = title.strip.parameterize.sub(/^\d+/, '')
  75393. if warnings && idx.blank?
  75394. puts "BLANK ID: please put an explicit ID for section #{title}, as in h5(#my-id)"
  75395. end
  75396. idx
  75397. end
  75398. end
  75399. end
  75400. #!/usr/bin/env ruby
  75401. unless `which kindlerb`
  75402. abort "Please gem install kindlerb"
  75403. end
  75404. require 'nokogiri'
  75405. require 'fileutils'
  75406. require 'yaml'
  75407. require 'date'
  75408. module Kindle
  75409. extend self
  75410. def generate(output_dir, mobi_outfile, logfile)
  75411. output_dir = File.absolute_path(output_dir)
  75412. Dir.chdir output_dir do
  75413. puts "=> Using output dir: #{output_dir}"
  75414. puts "=> Arranging html pages in document order"
  75415. toc = File.read("toc.ncx")
  75416. doc = Nokogiri::XML(toc).xpath("//ncx:content", 'ncx' => "http://www.daisy.org/z3986/2005/ncx/")
  75417. html_pages = doc.select {|c| c[:src]}.map {|c| c[:src]}.uniq
  75418. generate_front_matter(html_pages)
  75419. generate_sections(html_pages)
  75420. generate_document_metadata(mobi_outfile)
  75421. puts "Creating MOBI document with kindlegen. This make take a while."
  75422. cmd = "kindlerb . > #{File.absolute_path logfile} 2>&1"
  75423. puts cmd
  75424. system(cmd)
  75425. puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}"
  75426. end
  75427. end
  75428. def generate_front_matter(html_pages)
  75429. frontmatter = []
  75430. html_pages.delete_if {|x|
  75431. if x =~ /(toc|welcome|credits|copyright).html/
  75432. frontmatter << x unless x =~ /toc/
  75433. true
  75434. end
  75435. }
  75436. html = frontmatter.map {|x|
  75437. Nokogiri::HTML(File.open(x)).at("body").inner_html
  75438. }.join("\n")
  75439. fdoc = Nokogiri::HTML(html)
  75440. fdoc.search("h3").each do |h3|
  75441. h3.name = 'h4'
  75442. end
  75443. fdoc.search("h2").each do |h2|
  75444. h2.name = 'h3'
  75445. h2['id'] = h2.inner_text.gsub(/\s/, '-')
  75446. end
  75447. add_head_section fdoc, "Front Matter"
  75448. File.open("frontmatter.html",'w') {|f| f.puts fdoc.to_html}
  75449. html_pages.unshift "frontmatter.html"
  75450. end
  75451. def generate_sections(html_pages)
  75452. FileUtils::rm_rf("sections/")
  75453. html_pages.each_with_index do |page, section_idx|
  75454. FileUtils::mkdir_p("sections/%03d" % section_idx)
  75455. doc = Nokogiri::HTML(File.open(page))
  75456. title = doc.at("title").inner_text.gsub("Ruby on Rails Guides: ", '')
  75457. title = page.capitalize.gsub('.html', '') if title.strip == ''
  75458. File.open("sections/%03d/_section.txt" % section_idx, 'w') {|f| f.puts title}
  75459. doc.xpath("//h3[@id]").each_with_index do |h3,item_idx|
  75460. subsection = h3.inner_text
  75461. content = h3.xpath("./following-sibling::*").take_while {|x| x.name != "h3"}.map {|x| x.to_html}
  75462. item = Nokogiri::HTML(h3.to_html + content.join("\n"))
  75463. item_path = "sections/%03d/%03d.html" % [section_idx, item_idx]
  75464. add_head_section(item, subsection)
  75465. item.search("img").each do |img|
  75466. img['src'] = "#{Dir.pwd}/#{img['src']}"
  75467. end
  75468. item.xpath("//li/p").each {|p| p.swap(p.children); p.remove}
  75469. File.open(item_path, 'w') {|f| f.puts item.to_html}
  75470. end
  75471. end
  75472. end
  75473. def generate_document_metadata(mobi_outfile)
  75474. puts "=> Generating _document.yml"
  75475. x = Nokogiri::XML(File.open("rails_guides.opf")).remove_namespaces!
  75476. cover_jpg = "#{Dir.pwd}/images/rails_guides_kindle_cover.jpg"
  75477. cover_gif = cover_jpg.sub(/jpg$/, 'gif')
  75478. puts `convert #{cover_jpg} #{cover_gif}`
  75479. document = {
  75480. 'doc_uuid' => x.at("package")['unique-identifier'],
  75481. 'title' => x.at("title").inner_text.gsub(/\(.*$/, " v2"),
  75482. 'publisher' => x.at("publisher").inner_text,
  75483. 'author' => x.at("creator").inner_text,
  75484. 'subject' => x.at("subject").inner_text,
  75485. 'date' => x.at("date").inner_text,
  75486. 'cover' => cover_gif,
  75487. 'masthead' => nil,
  75488. 'mobi_outfile' => mobi_outfile
  75489. }
  75490. puts document.to_yaml
  75491. File.open("_document.yml", 'w'){|f| f.puts document.to_yaml}
  75492. end
  75493. def add_head_section(doc, title)
  75494. head = Nokogiri::XML::Node.new "head", doc
  75495. title_node = Nokogiri::XML::Node.new "title", doc
  75496. title_node.content = title
  75497. title_node.parent = head
  75498. css = Nokogiri::XML::Node.new "link", doc
  75499. css['rel'] = 'stylesheet'
  75500. css['type'] = 'text/css'
  75501. css['href'] = "#{Dir.pwd}/stylesheets/kindle.css"
  75502. css.parent = head
  75503. doc.at("body").before head
  75504. end
  75505. end
  75506. module RailsGuides
  75507. module Levenshtein
  75508. # Based on the pseudocode in http://en.wikipedia.org/wiki/Levenshtein_distance
  75509. def self.distance(s1, s2)
  75510. s = s1.unpack('U*')
  75511. t = s2.unpack('U*')
  75512. m = s.length
  75513. n = t.length
  75514. # matrix initialization
  75515. d = []
  75516. 0.upto(m) { |i| d << [i] }
  75517. 0.upto(n) { |j| d[0][j] = j }
  75518. # distance computation
  75519. 1.upto(m) do |i|
  75520. 1.upto(n) do |j|
  75521. cost = s[i] == t[j] ? 0 : 1
  75522. d[i][j] = [
  75523. d[i-1][j] + 1, # deletion
  75524. d[i][j-1] + 1, # insertion
  75525. d[i-1][j-1] + cost, # substitution
  75526. ].min
  75527. end
  75528. end
  75529. # all done
  75530. return d[m][n]
  75531. end
  75532. end
  75533. end
  75534. module RailsGuides
  75535. class Markdown
  75536. class Renderer < Redcarpet::Render::HTML
  75537. def initialize(options={})
  75538. super
  75539. end
  75540. def block_code(code, language)
  75541. <<-HTML
  75542. <div class="code_container">
  75543. <pre class="brush: #{brush_for(language)}; gutter: false; toolbar: false">
  75544. #{ERB::Util.h(code)}
  75545. </pre>
  75546. </div>
  75547. HTML
  75548. end
  75549. def header(text, header_level)
  75550. # Always increase the heading level by, so we can use h1, h2 heading in the document
  75551. header_level += 1
  75552. %(<h#{header_level}>#{text}</h#{header_level}>)
  75553. end
  75554. def paragraph(text)
  75555. if text =~ /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)/
  75556. convert_notes(text)
  75557. elsif text =~ /^\[<sup>(\d+)\]:<\/sup> (.+)$/
  75558. linkback = %(<a href="#footnote-#{$1}-ref"><sup>#{$1}</sup></a>)
  75559. %(<p class="footnote" id="footnote-#{$1}">#{linkback} #{$2}</p>)
  75560. else
  75561. text = convert_footnotes(text)
  75562. "<p>#{text}</p>"
  75563. end
  75564. end
  75565. private
  75566. def convert_footnotes(text)
  75567. text.gsub(/\[<sup>(\d+)\]<\/sup>/i) do
  75568. %(<sup class="footnote" id="footnote-#{$1}-ref">) +
  75569. %(<a href="#footnote-#{$1}">#{$1}</a></sup>)
  75570. end
  75571. end
  75572. def brush_for(code_type)
  75573. case code_type
  75574. when 'ruby', 'sql', 'plain'
  75575. code_type
  75576. when 'erb'
  75577. 'ruby; html-script: true'
  75578. when 'html'
  75579. 'xml' # html is understood, but there are .xml rules in the CSS
  75580. else
  75581. 'plain'
  75582. end
  75583. end
  75584. def convert_notes(body)
  75585. # The following regexp detects special labels followed by a
  75586. # paragraph, perhaps at the end of the document.
  75587. #
  75588. # It is important that we do not eat more than one newline
  75589. # because formatting may be wrong otherwise. For example,
  75590. # if a bulleted list follows the first item is not rendered
  75591. # as a list item, but as a paragraph starting with a plain
  75592. # asterisk.
  75593. body.gsub(/^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)(\n(?=\n)|\Z)/m) do |m|
  75594. css_class = case $1
  75595. when 'CAUTION', 'IMPORTANT'
  75596. 'warning'
  75597. when 'TIP'
  75598. 'info'
  75599. else
  75600. $1.downcase
  75601. end
  75602. %(<div class="#{css_class}"><p>#{$2.strip}</p></div>)
  75603. end
  75604. end
  75605. end
  75606. end
  75607. end
  75608. # encoding: utf-8
  75609. require 'redcarpet'
  75610. require 'nokogiri'
  75611. require 'rails_guides/markdown/renderer'
  75612. module RailsGuides
  75613. class Markdown
  75614. def initialize(view, layout)
  75615. @view = view
  75616. @layout = layout
  75617. @index_counter = Hash.new(0)
  75618. @raw_header = ''
  75619. @node_ids = {}
  75620. end
  75621. def render(body)
  75622. @raw_body = body
  75623. extract_raw_header_and_body
  75624. generate_header
  75625. generate_title
  75626. generate_body
  75627. generate_structure
  75628. generate_index
  75629. render_page
  75630. end
  75631. private
  75632. def dom_id(nodes)
  75633. dom_id = dom_id_text(nodes.last.text)
  75634. # Fix duplicate node by prefix with its parent node
  75635. if @node_ids[dom_id]
  75636. if @node_ids[dom_id].size > 1
  75637. duplicate_nodes = @node_ids.delete(dom_id)
  75638. new_node_id = "#{duplicate_nodes[-2][:id]}-#{duplicate_nodes.last[:id]}"
  75639. duplicate_nodes.last[:id] = new_node_id
  75640. @node_ids[new_node_id] = duplicate_nodes
  75641. end
  75642. dom_id = "#{nodes[-2][:id]}-#{dom_id}"
  75643. end
  75644. @node_ids[dom_id] = nodes
  75645. dom_id
  75646. end
  75647. def dom_id_text(text)
  75648. text.downcase.gsub(/\?/, '-questionmark').gsub(/!/, '-bang').gsub(/[^a-z0-9]+/, ' ')
  75649. .strip.gsub(/\s+/, '-')
  75650. end
  75651. def engine
  75652. @engine ||= Redcarpet::Markdown.new(Renderer, {
  75653. no_intra_emphasis: true,
  75654. fenced_code_blocks: true,
  75655. autolink: true,
  75656. strikethrough: true,
  75657. superscript: true,
  75658. tables: true
  75659. })
  75660. end
  75661. def extract_raw_header_and_body
  75662. if @raw_body =~ /^\-{40,}$/
  75663. @raw_header, _, @raw_body = @raw_body.partition(/^\-{40,}$/).map(&:strip)
  75664. end
  75665. end
  75666. def generate_body
  75667. @body = engine.render(@raw_body)
  75668. end
  75669. def generate_header
  75670. @header = engine.render(@raw_header).html_safe
  75671. end
  75672. def generate_structure
  75673. @headings_for_index = []
  75674. if @body.present?
  75675. @body = Nokogiri::HTML(@body).tap do |doc|
  75676. hierarchy = []
  75677. doc.at('body').children.each do |node|
  75678. if node.name =~ /^h[3-6]$/
  75679. case node.name
  75680. when 'h3'
  75681. hierarchy = [node]
  75682. @headings_for_index << [1, node, node.inner_html]
  75683. when 'h4'
  75684. hierarchy = hierarchy[0, 1] + [node]
  75685. @headings_for_index << [2, node, node.inner_html]
  75686. when 'h5'
  75687. hierarchy = hierarchy[0, 2] + [node]
  75688. when 'h6'
  75689. hierarchy = hierarchy[0, 3] + [node]
  75690. end
  75691. node[:id] = dom_id(hierarchy)
  75692. node.inner_html = "#{node_index(hierarchy)} #{node.inner_html}"
  75693. end
  75694. end
  75695. end.to_html
  75696. end
  75697. end
  75698. def generate_index
  75699. if @headings_for_index.present?
  75700. raw_index = ''
  75701. @headings_for_index.each do |level, node, label|
  75702. if level == 1
  75703. raw_index += "1. [#{label}](##{node[:id]})\n"
  75704. elsif level == 2
  75705. raw_index += " * [#{label}](##{node[:id]})\n"
  75706. end
  75707. end
  75708. @index = Nokogiri::HTML(engine.render(raw_index)).tap do |doc|
  75709. doc.at('ol')[:class] = 'chapters'
  75710. end.to_html
  75711. @index = <<-INDEX.html_safe
  75712. <div id="subCol">
  75713. <h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />Chapters</h3>
  75714. #{@index}
  75715. </div>
  75716. INDEX
  75717. end
  75718. end
  75719. def generate_title
  75720. if heading = Nokogiri::HTML(@header).at(:h2)
  75721. @title = "#{heading.text} — Ruby on Rails Guides".html_safe
  75722. else
  75723. @title = "Ruby on Rails Guides"
  75724. end
  75725. end
  75726. def node_index(hierarchy)
  75727. case hierarchy.size
  75728. when 1
  75729. @index_counter[2] = @index_counter[3] = @index_counter[4] = 0
  75730. "#{@index_counter[1] += 1}"
  75731. when 2
  75732. @index_counter[3] = @index_counter[4] = 0
  75733. "#{@index_counter[1]}.#{@index_counter[2] += 1}"
  75734. when 3
  75735. @index_counter[4] = 0
  75736. "#{@index_counter[1]}.#{@index_counter[2]}.#{@index_counter[3] += 1}"
  75737. when 4
  75738. "#{@index_counter[1]}.#{@index_counter[2]}.#{@index_counter[3]}.#{@index_counter[4] += 1}"
  75739. end
  75740. end
  75741. def render_page
  75742. @view.content_for(:header_section) { @header }
  75743. @view.content_for(:page_title) { @title }
  75744. @view.content_for(:index_section) { @index }
  75745. @view.render(:layout => @layout, :text => @body)
  75746. end
  75747. end
  75748. end
  75749. pwd = File.dirname(__FILE__)
  75750. $:.unshift pwd
  75751. # This is a predicate useful for the doc:guides task of applications.
  75752. def bundler?
  75753. # Note that rake sets the cwd to the one that contains the Rakefile
  75754. # being executed.
  75755. File.exists?('Gemfile')
  75756. end
  75757. begin
  75758. # Guides generation in the Rails repo.
  75759. as_lib = File.join(pwd, "../activesupport/lib")
  75760. ap_lib = File.join(pwd, "../actionpack/lib")
  75761. $:.unshift as_lib if File.directory?(as_lib)
  75762. $:.unshift ap_lib if File.directory?(ap_lib)
  75763. rescue LoadError
  75764. # Guides generation from gems.
  75765. gem "actionpack", '>= 3.0'
  75766. end
  75767. begin
  75768. require 'redcarpet'
  75769. rescue Gem::LoadError
  75770. # This can happen if doc:guides is executed in an application.
  75771. $stderr.puts('Generating guides requires Redcarpet 2.1.1+.')
  75772. $stderr.puts(<<ERROR) if bundler?
  75773. Please add
  75774. gem 'redcarpet', '~> 2.1.1'
  75775. to the Gemfile, run
  75776. bundle install
  75777. and try again.
  75778. ERROR
  75779. exit 1
  75780. end
  75781. require 'rails_guides/markdown'
  75782. require "rails_guides/generator"
  75783. RailsGuides::Generator.new.generate
  75784. # ---------------------------------------------------------------------------
  75785. #
  75786. # This script validates the generated guides against the W3C Validator.
  75787. #
  75788. # Guides are taken from the output directory, from where all .html files are
  75789. # submitted to the validator.
  75790. #
  75791. # This script is prepared to be launched from the guides directory as a rake task:
  75792. #
  75793. # rake guides:validate
  75794. #
  75795. # If nothing is specified, all files will be validated, but you can check just
  75796. # some of them using this environment variable:
  75797. #
  75798. # ONLY
  75799. # Use ONLY if you want to validate only one or a set of guides. Prefixes are
  75800. # enough:
  75801. #
  75802. # # validates only association_basics.html
  75803. # rake guides:validate ONLY=assoc
  75804. #
  75805. # Separate many using commas:
  75806. #
  75807. # # validates only association_basics.html and migrations.html
  75808. # rake guides:validate ONLY=assoc,migrations
  75809. #
  75810. # ---------------------------------------------------------------------------
  75811. require 'w3c_validators'
  75812. include W3CValidators
  75813. module RailsGuides
  75814. class Validator
  75815. def validate
  75816. validator = MarkupValidator.new
  75817. STDOUT.sync = true
  75818. errors_on_guides = {}
  75819. guides_to_validate.each do |f|
  75820. begin
  75821. results = validator.validate_file(f)
  75822. rescue Exception => e
  75823. puts "\nCould not validate #{f} because of #{e}"
  75824. next
  75825. end
  75826. if results.validity
  75827. print "."
  75828. else
  75829. print "E"
  75830. errors_on_guides[f] = results.errors
  75831. end
  75832. end
  75833. show_results(errors_on_guides)
  75834. end
  75835. private
  75836. def guides_to_validate
  75837. guides = Dir["./output/*.html"]
  75838. guides.delete("./output/layout.html")
  75839. ENV.key?('ONLY') ? select_only(guides) : guides
  75840. end
  75841. def select_only(guides)
  75842. prefixes = ENV['ONLY'].split(",").map(&:strip)
  75843. guides.select do |guide|
  75844. prefixes.any? {|p| guide.start_with?("./output/#{p}")}
  75845. end
  75846. end
  75847. def show_results(error_list)
  75848. if error_list.size == 0
  75849. puts "\n\nAll checked guides validate OK!"
  75850. else
  75851. error_summary = error_detail = ""
  75852. error_list.each_pair do |name, errors|
  75853. error_summary += "\n #{name}"
  75854. error_detail += "\n\n #{name} has #{errors.size} validation error(s):\n"
  75855. errors.each do |error|
  75856. error_detail += "\n "+error.to_s.delete("\n")
  75857. end
  75858. end
  75859. puts "\n\nThere are #{error_list.size} guides with validation errors:\n" + error_summary
  75860. puts "\nHere are the detailed errors for each guide:" + error_detail
  75861. end
  75862. end
  75863. end
  75864. end
  75865. RailsGuides::Validator.new.validate
  75866. version = ARGV.pop
  75867. if version.nil?
  75868. puts "Usage: ruby install.rb version"
  75869. exit(64)
  75870. end
  75871. %w( activesupport activemodel activerecord actionpack actionmailer railties ).each do |framework|
  75872. puts "Installing #{framework}..."
  75873. `cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --local --no-ri --no-rdoc && rm #{framework}-#{version}.gem`
  75874. end
  75875. puts "Installing Rails..."
  75876. `gem build rails.gemspec`
  75877. `gem install rails-#{version}.gem --local --no-ri --no-rdoc `
  75878. `rm rails-#{version}.gem`
  75879. # bust gem prelude
  75880. require 'bundler'
  75881. Bundler.setup
  75882. require "rails"
  75883. %w(
  75884. active_record
  75885. action_controller
  75886. action_mailer
  75887. rails/test_unit
  75888. sprockets
  75889. ).each do |framework|
  75890. begin
  75891. require "#{framework}/railtie"
  75892. rescue LoadError
  75893. end
  75894. end
  75895. require 'pathname'
  75896. module Rails
  75897. module AppRailsLoader
  75898. RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
  75899. EXECUTABLE = 'bin/rails'
  75900. def self.exec_app_rails
  75901. cwd = Dir.pwd
  75902. return unless in_rails_application_or_engine? || in_rails_application_or_engine_subdirectory?
  75903. exec RUBY, EXECUTABLE, *ARGV if in_rails_application_or_engine?
  75904. Dir.chdir("..") do
  75905. # Recurse in a chdir block: if the search fails we want to be sure
  75906. # the application is generated in the original working directory.
  75907. exec_app_rails unless cwd == Dir.pwd
  75908. end
  75909. rescue SystemCallError
  75910. # could not chdir, no problem just return
  75911. end
  75912. def self.in_rails_application_or_engine?
  75913. File.exists?(EXECUTABLE) && File.read(EXECUTABLE) =~ /(APP|ENGINE)_PATH/
  75914. end
  75915. def self.in_rails_application_or_engine_subdirectory?(path = Pathname.new(Dir.pwd))
  75916. File.exists?(File.join(path, EXECUTABLE)) || !path.root? && in_rails_application_or_engine_subdirectory?(path.parent)
  75917. end
  75918. end
  75919. end
  75920. require "active_support/notifications"
  75921. require "active_support/dependencies"
  75922. require "active_support/descendants_tracker"
  75923. module Rails
  75924. class Application
  75925. module Bootstrap
  75926. include Initializable
  75927. initializer :load_environment_hook, group: :all do end
  75928. initializer :load_active_support, group: :all do
  75929. require "active_support/all" unless config.active_support.bare
  75930. end
  75931. initializer :set_eager_load, group: :all do
  75932. if config.eager_load.nil?
  75933. warn <<-INFO
  75934. config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
  75935. * development - set it to false
  75936. * test - set it to false (unless you use a tool that preloads your test environment)
  75937. * production - set it to true
  75938. INFO
  75939. config.eager_load = config.cache_classes
  75940. end
  75941. end
  75942. # Initialize the logger early in the stack in case we need to log some deprecation.
  75943. initializer :initialize_logger, group: :all do
  75944. Rails.logger ||= config.logger || begin
  75945. path = config.paths["log"].first
  75946. unless File.exist? File.dirname path
  75947. FileUtils.mkdir_p File.dirname path
  75948. end
  75949. f = File.open path, 'a'
  75950. f.binmode
  75951. f.sync = config.autoflush_log # if true make sure every write flushes
  75952. logger = ActiveSupport::Logger.new f
  75953. logger.formatter = config.log_formatter
  75954. logger = ActiveSupport::TaggedLogging.new(logger)
  75955. logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase)
  75956. logger
  75957. rescue StandardError
  75958. logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDERR))
  75959. logger.level = ActiveSupport::Logger::WARN
  75960. logger.warn(
  75961. "Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " +
  75962. "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
  75963. )
  75964. logger
  75965. end
  75966. end
  75967. # Initialize cache early in the stack so railties can make use of it.
  75968. initializer :initialize_cache, group: :all do
  75969. unless Rails.cache
  75970. Rails.cache = ActiveSupport::Cache.lookup_store(config.cache_store)
  75971. if Rails.cache.respond_to?(:middleware)
  75972. config.middleware.insert_before("Rack::Runtime", Rails.cache.middleware)
  75973. end
  75974. end
  75975. end
  75976. # Sets the dependency loading mechanism.
  75977. initializer :initialize_dependency_mechanism, group: :all do
  75978. ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
  75979. end
  75980. initializer :bootstrap_hook, group: :all do |app|
  75981. ActiveSupport.run_load_hooks(:before_initialize, app)
  75982. end
  75983. end
  75984. end
  75985. end
  75986. require 'active_support/core_ext/kernel/reporting'
  75987. require 'active_support/file_update_checker'
  75988. require 'rails/engine/configuration'
  75989. module Rails
  75990. class Application
  75991. class Configuration < ::Rails::Engine::Configuration
  75992. attr_accessor :asset_host, :assets, :autoflush_log,
  75993. :cache_classes, :cache_store, :consider_all_requests_local, :console,
  75994. :eager_load, :exceptions_app, :file_watcher, :filter_parameters,
  75995. :force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
  75996. :railties_order, :relative_url_root, :secret_key_base, :secret_token,
  75997. :serve_static_assets, :ssl_options, :static_cache_control, :session_options,
  75998. :time_zone, :reload_classes_only_on_change,
  75999. :beginning_of_week, :filter_redirect
  76000. attr_writer :log_level
  76001. attr_reader :encoding
  76002. def initialize(*)
  76003. super
  76004. self.encoding = "utf-8"
  76005. @consider_all_requests_local = false
  76006. @filter_parameters = []
  76007. @filter_redirect = []
  76008. @helpers_paths = []
  76009. @serve_static_assets = true
  76010. @static_cache_control = nil
  76011. @force_ssl = false
  76012. @ssl_options = {}
  76013. @session_store = :cookie_store
  76014. @session_options = {}
  76015. @time_zone = "UTC"
  76016. @beginning_of_week = :monday
  76017. @log_level = nil
  76018. @middleware = app_middleware
  76019. @generators = app_generators
  76020. @cache_store = [ :file_store, "#{root}/tmp/cache/" ]
  76021. @railties_order = [:all]
  76022. @relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"]
  76023. @reload_classes_only_on_change = true
  76024. @file_watcher = ActiveSupport::FileUpdateChecker
  76025. @exceptions_app = nil
  76026. @autoflush_log = true
  76027. @log_formatter = ActiveSupport::Logger::SimpleFormatter.new
  76028. @eager_load = nil
  76029. @secret_token = nil
  76030. @secret_key_base = nil
  76031. @assets = ActiveSupport::OrderedOptions.new
  76032. @assets.enabled = true
  76033. @assets.paths = []
  76034. @assets.precompile = [ Proc.new { |path, fn| fn =~ /app\/assets/ && !%w(.js .css).include?(File.extname(path)) },
  76035. /(?:\/|\\|\A)application\.(css|js)$/ ]
  76036. @assets.prefix = "/assets"
  76037. @assets.version = '1.0'
  76038. @assets.debug = false
  76039. @assets.compile = true
  76040. @assets.digest = false
  76041. @assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/#{Rails.env}/" ]
  76042. @assets.js_compressor = nil
  76043. @assets.css_compressor = nil
  76044. @assets.initialize_on_precompile = true
  76045. @assets.logger = nil
  76046. end
  76047. def encoding=(value)
  76048. @encoding = value
  76049. silence_warnings do
  76050. Encoding.default_external = value
  76051. Encoding.default_internal = value
  76052. end
  76053. end
  76054. def paths
  76055. @paths ||= begin
  76056. paths = super
  76057. paths.add "config/database", with: "config/database.yml"
  76058. paths.add "config/environment", with: "config/environment.rb"
  76059. paths.add "lib/templates"
  76060. paths.add "log", with: "log/#{Rails.env}.log"
  76061. paths.add "public"
  76062. paths.add "public/javascripts"
  76063. paths.add "public/stylesheets"
  76064. paths.add "tmp"
  76065. paths
  76066. end
  76067. end
  76068. def threadsafe!
  76069. message = "config.threadsafe! is deprecated. Rails applications " \
  76070. "behave by default as thread safe in production as long as config.cache_classes and " \
  76071. "config.eager_load are set to true"
  76072. ActiveSupport::Deprecation.warn message
  76073. @cache_classes = true
  76074. @eager_load = true
  76075. self
  76076. end
  76077. # Loads and returns the contents of the #database_configuration_file. The
  76078. # contents of the file are processed via ERB before being sent through
  76079. # YAML::load.
  76080. def database_configuration
  76081. require 'erb'
  76082. YAML.load ERB.new(IO.read(paths["config/database"].first)).result
  76083. rescue Psych::SyntaxError => e
  76084. raise "YAML syntax error occurred while parsing #{paths["config/database"].first}. " \
  76085. "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
  76086. "Error: #{e.message}"
  76087. end
  76088. def log_level
  76089. @log_level ||= Rails.env.production? ? :info : :debug
  76090. end
  76091. def colorize_logging
  76092. ActiveSupport::LogSubscriber.colorize_logging
  76093. end
  76094. def colorize_logging=(val)
  76095. ActiveSupport::LogSubscriber.colorize_logging = val
  76096. self.generators.colorize_logging = val
  76097. end
  76098. def session_store(*args)
  76099. if args.empty?
  76100. case @session_store
  76101. when :disabled
  76102. nil
  76103. when :active_record_store
  76104. begin
  76105. ActionDispatch::Session::ActiveRecordStore
  76106. rescue NameError
  76107. raise "`ActiveRecord::SessionStore` is extracted out of Rails into a gem. " \
  76108. "Please add `activerecord-session_store` to your Gemfile to use it."
  76109. end
  76110. when Symbol
  76111. ActionDispatch::Session.const_get(@session_store.to_s.camelize)
  76112. else
  76113. @session_store
  76114. end
  76115. else
  76116. @session_store = args.shift
  76117. @session_options = args.shift || {}
  76118. end
  76119. end
  76120. def whiny_nils=(*)
  76121. ActiveSupport::Deprecation.warn "config.whiny_nils option is deprecated and no longer works"
  76122. end
  76123. end
  76124. end
  76125. end
  76126. module Rails
  76127. class Application
  76128. module Finisher
  76129. include Initializable
  76130. initializer :add_generator_templates do
  76131. config.generators.templates.unshift(*paths["lib/templates"].existent)
  76132. end
  76133. initializer :ensure_autoload_once_paths_as_subset do
  76134. extra = ActiveSupport::Dependencies.autoload_once_paths -
  76135. ActiveSupport::Dependencies.autoload_paths
  76136. unless extra.empty?
  76137. abort <<-end_error
  76138. autoload_once_paths must be a subset of the autoload_paths.
  76139. Extra items in autoload_once_paths: #{extra * ','}
  76140. end_error
  76141. end
  76142. end
  76143. initializer :add_builtin_route do |app|
  76144. if Rails.env.development?
  76145. app.routes.append do
  76146. get '/rails/info/properties' => "rails/info#properties"
  76147. get '/rails/info/routes' => "rails/info#routes"
  76148. get '/rails/info' => "rails/info#index"
  76149. get '/' => "rails/welcome#index"
  76150. end
  76151. end
  76152. end
  76153. initializer :build_middleware_stack do
  76154. build_middleware_stack
  76155. end
  76156. initializer :define_main_app_helper do |app|
  76157. app.routes.define_mounted_helper(:main_app)
  76158. end
  76159. initializer :add_to_prepare_blocks do
  76160. config.to_prepare_blocks.each do |block|
  76161. ActionDispatch::Reloader.to_prepare(&block)
  76162. end
  76163. end
  76164. # This needs to happen before eager load so it happens
  76165. # in exactly the same point regardless of config.cache_classes
  76166. initializer :run_prepare_callbacks do
  76167. ActionDispatch::Reloader.prepare!
  76168. end
  76169. initializer :eager_load! do
  76170. if config.eager_load
  76171. ActiveSupport.run_load_hooks(:before_eager_load, self)
  76172. config.eager_load_namespaces.each(&:eager_load!)
  76173. end
  76174. end
  76175. # All initialization is done, including eager loading in production
  76176. initializer :finisher_hook do
  76177. ActiveSupport.run_load_hooks(:after_initialize, self)
  76178. end
  76179. # Set app reload just after the finisher hook to ensure
  76180. # routes added in the hook are still loaded.
  76181. initializer :set_routes_reloader_hook do
  76182. reloader = routes_reloader
  76183. reloader.execute_if_updated
  76184. self.reloaders << reloader
  76185. ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
  76186. end
  76187. # Set app reload just after the finisher hook to ensure
  76188. # paths added in the hook are still loaded.
  76189. initializer :set_clear_dependencies_hook, group: :all do
  76190. callback = lambda do
  76191. ActiveSupport::DescendantsTracker.clear
  76192. ActiveSupport::Dependencies.clear
  76193. end
  76194. if config.reload_classes_only_on_change
  76195. reloader = config.file_watcher.new(*watchable_args, &callback)
  76196. self.reloaders << reloader
  76197. # We need to set a to_prepare callback regardless of the reloader result, i.e.
  76198. # models should be reloaded if any of the reloaders (i18n, routes) were updated.
  76199. ActionDispatch::Reloader.to_prepare(prepend: true){ reloader.execute }
  76200. else
  76201. ActionDispatch::Reloader.to_cleanup(&callback)
  76202. end
  76203. end
  76204. # Disable dependency loading during request cycle
  76205. initializer :disable_dependency_loading do
  76206. if config.eager_load && config.cache_classes
  76207. ActiveSupport::Dependencies.unhook!
  76208. end
  76209. end
  76210. end
  76211. end
  76212. end
  76213. require "active_support/core_ext/module/delegation"
  76214. module Rails
  76215. class Application
  76216. class RoutesReloader
  76217. attr_reader :route_sets, :paths
  76218. delegate :execute_if_updated, :execute, :updated?, to: :updater
  76219. def initialize
  76220. @paths = []
  76221. @route_sets = []
  76222. end
  76223. def reload!
  76224. clear!
  76225. load_paths
  76226. finalize!
  76227. ensure
  76228. revert
  76229. end
  76230. private
  76231. def updater
  76232. @updater ||= begin
  76233. updater = ActiveSupport::FileUpdateChecker.new(paths) { reload! }
  76234. updater.execute
  76235. updater
  76236. end
  76237. end
  76238. def clear!
  76239. route_sets.each do |routes|
  76240. routes.disable_clear_and_finalize = true
  76241. routes.clear!
  76242. end
  76243. end
  76244. def load_paths
  76245. paths.each { |path| load(path) }
  76246. end
  76247. def finalize!
  76248. route_sets.each do |routes|
  76249. routes.finalize!
  76250. end
  76251. end
  76252. def revert
  76253. route_sets.each do |routes|
  76254. routes.disable_clear_and_finalize = false
  76255. end
  76256. end
  76257. end
  76258. end
  76259. end
  76260. require 'fileutils'
  76261. # FIXME remove DummyKeyGenerator and this require in 4.1
  76262. require 'active_support/key_generator'
  76263. require 'rails/engine'
  76264. module Rails
  76265. # In Rails 3.0, a Rails::Application object was introduced which is nothing more than
  76266. # an Engine but with the responsibility of coordinating the whole boot process.
  76267. #
  76268. # == Initialization
  76269. #
  76270. # Rails::Application is responsible for executing all railties and engines
  76271. # initializers. It also executes some bootstrap initializers (check
  76272. # Rails::Application::Bootstrap) and finishing initializers, after all the others
  76273. # are executed (check Rails::Application::Finisher).
  76274. #
  76275. # == Configuration
  76276. #
  76277. # Besides providing the same configuration as Rails::Engine and Rails::Railtie,
  76278. # the application object has several specific configurations, for example
  76279. # "cache_classes", "consider_all_requests_local", "filter_parameters",
  76280. # "logger" and so forth.
  76281. #
  76282. # Check Rails::Application::Configuration to see them all.
  76283. #
  76284. # == Routes
  76285. #
  76286. # The application object is also responsible for holding the routes and reloading routes
  76287. # whenever the files change in development.
  76288. #
  76289. # == Middlewares
  76290. #
  76291. # The Application is also responsible for building the middleware stack.
  76292. #
  76293. # == Booting process
  76294. #
  76295. # The application is also responsible for setting up and executing the booting
  76296. # process. From the moment you require "config/application.rb" in your app,
  76297. # the booting process goes like this:
  76298. #
  76299. # 1) require "config/boot.rb" to setup load paths
  76300. # 2) require railties and engines
  76301. # 3) Define Rails.application as "class MyApp::Application < Rails::Application"
  76302. # 4) Run config.before_configuration callbacks
  76303. # 5) Load config/environments/ENV.rb
  76304. # 6) Run config.before_initialize callbacks
  76305. # 7) Run Railtie#initializer defined by railties, engines and application.
  76306. # One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
  76307. # 9) Custom Railtie#initializers added by railties, engines and applications are executed
  76308. # 10) Build the middleware stack and run to_prepare callbacks
  76309. # 11) Run config.before_eager_load and eager_load! if eager_load is true
  76310. # 12) Run config.after_initialize callbacks
  76311. #
  76312. class Application < Engine
  76313. autoload :Bootstrap, 'rails/application/bootstrap'
  76314. autoload :Configuration, 'rails/application/configuration'
  76315. autoload :Finisher, 'rails/application/finisher'
  76316. autoload :Railties, 'rails/engine/railties'
  76317. autoload :RoutesReloader, 'rails/application/routes_reloader'
  76318. class << self
  76319. def inherited(base)
  76320. raise "You cannot have more than one Rails::Application" if Rails.application
  76321. super
  76322. Rails.application = base.instance
  76323. Rails.application.add_lib_to_load_path!
  76324. ActiveSupport.run_load_hooks(:before_configuration, base.instance)
  76325. end
  76326. end
  76327. attr_accessor :assets, :sandbox
  76328. alias_method :sandbox?, :sandbox
  76329. attr_reader :reloaders
  76330. delegate :default_url_options, :default_url_options=, to: :routes
  76331. def initialize
  76332. super
  76333. @initialized = false
  76334. @reloaders = []
  76335. @routes_reloader = nil
  76336. @env_config = nil
  76337. @ordered_railties = nil
  76338. @railties = nil
  76339. end
  76340. # Returns true if the application is initialized.
  76341. def initialized?
  76342. @initialized
  76343. end
  76344. # Implements call according to the Rack API. It simply
  76345. # dispatches the request to the underlying middleware stack.
  76346. def call(env)
  76347. env["ORIGINAL_FULLPATH"] = build_original_fullpath(env)
  76348. super(env)
  76349. end
  76350. # Reload application routes regardless if they changed or not.
  76351. def reload_routes!
  76352. routes_reloader.reload!
  76353. end
  76354. # Return the application's KeyGenerator
  76355. def key_generator
  76356. # number of iterations selected based on consultation with the google security
  76357. # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
  76358. @caching_key_generator ||= begin
  76359. if config.secret_key_base
  76360. key_generator = ActiveSupport::KeyGenerator.new(config.secret_key_base, iterations: 1000)
  76361. ActiveSupport::CachingKeyGenerator.new(key_generator)
  76362. else
  76363. ActiveSupport::DummyKeyGenerator.new(config.secret_token)
  76364. end
  76365. end
  76366. end
  76367. # Stores some of the Rails initial environment parameters which
  76368. # will be used by middlewares and engines to configure themselves.
  76369. # Currently stores:
  76370. #
  76371. # * "action_dispatch.parameter_filter" => config.filter_parameters
  76372. # * "action_dispatch.redirect_filter" => config.filter_redirect
  76373. # * "action_dispatch.secret_token" => config.secret_token,
  76374. # * "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions
  76375. # * "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local
  76376. # * "action_dispatch.logger" => Rails.logger
  76377. # * "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner
  76378. # * "action_dispatch.key_generator" => key_generator
  76379. # * "action_dispatch.http_auth_salt" => config.action_dispatch.http_auth_salt
  76380. # * "action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt
  76381. # * "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt
  76382. # * "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt
  76383. #
  76384. def env_config
  76385. @env_config ||= begin
  76386. if config.secret_key_base.nil?
  76387. ActiveSupport::Deprecation.warn "You didn't set config.secret_key_base in config/initializers/secret_token.rb file. " +
  76388. "This should be used instead of the old deprecated config.secret_token in order to use the new EncryptedCookieStore. " +
  76389. "To convert safely to the encrypted store (without losing existing cookies and sessions), see http://guides.rubyonrails.org/upgrading_ruby_on_rails.html#action-pack"
  76390. if config.secret_token.blank?
  76391. raise "You must set config.secret_key_base in your app's config"
  76392. end
  76393. end
  76394. super.merge({
  76395. "action_dispatch.parameter_filter" => config.filter_parameters,
  76396. "action_dispatch.redirect_filter" => config.filter_redirect,
  76397. "action_dispatch.secret_token" => config.secret_token,
  76398. "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
  76399. "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
  76400. "action_dispatch.logger" => Rails.logger,
  76401. "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
  76402. "action_dispatch.key_generator" => key_generator,
  76403. "action_dispatch.http_auth_salt" => config.action_dispatch.http_auth_salt,
  76404. "action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt,
  76405. "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt,
  76406. "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt
  76407. })
  76408. end
  76409. end
  76410. ## Rails internal API
  76411. # This method is called just after an application inherits from Rails::Application,
  76412. # allowing the developer to load classes in lib and use them during application
  76413. # configuration.
  76414. #
  76415. # class MyApplication < Rails::Application
  76416. # require "my_backend" # in lib/my_backend
  76417. # config.i18n.backend = MyBackend
  76418. # end
  76419. #
  76420. # Notice this method takes into consideration the default root path. So if you
  76421. # are changing config.root inside your application definition or having a custom
  76422. # Rails application, you will need to add lib to $LOAD_PATH on your own in case
  76423. # you need to load files in lib/ during the application configuration as well.
  76424. def add_lib_to_load_path! #:nodoc:
  76425. path = File.join config.root, 'lib'
  76426. $LOAD_PATH.unshift(path) if File.exists?(path)
  76427. end
  76428. def require_environment! #:nodoc:
  76429. environment = paths["config/environment"].existent.first
  76430. require environment if environment
  76431. end
  76432. def routes_reloader #:nodoc:
  76433. @routes_reloader ||= RoutesReloader.new
  76434. end
  76435. # Returns an array of file paths appended with a hash of
  76436. # directories-extensions suitable for ActiveSupport::FileUpdateChecker
  76437. # API.
  76438. def watchable_args #:nodoc:
  76439. files, dirs = config.watchable_files.dup, config.watchable_dirs.dup
  76440. ActiveSupport::Dependencies.autoload_paths.each do |path|
  76441. dirs[path.to_s] = [:rb]
  76442. end
  76443. [files, dirs]
  76444. end
  76445. # Initialize the application passing the given group. By default, the
  76446. # group is :default but sprockets precompilation passes group equals
  76447. # to assets if initialize_on_precompile is false to avoid booting the
  76448. # whole app.
  76449. def initialize!(group=:default) #:nodoc:
  76450. raise "Application has been already initialized." if @initialized
  76451. run_initializers(group, self)
  76452. @initialized = true
  76453. self
  76454. end
  76455. def initializers #:nodoc:
  76456. Bootstrap.initializers_for(self) +
  76457. railties_initializers(super) +
  76458. Finisher.initializers_for(self)
  76459. end
  76460. def config #:nodoc:
  76461. @config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd))
  76462. end
  76463. def to_app #:nodoc:
  76464. self
  76465. end
  76466. def helpers_paths #:nodoc:
  76467. config.helpers_paths
  76468. end
  76469. protected
  76470. alias :build_middleware_stack :app
  76471. def run_tasks_blocks(app) #:nodoc:
  76472. railties.each { |r| r.run_tasks_blocks(app) }
  76473. super
  76474. require "rails/tasks"
  76475. config = self.config
  76476. task :environment do
  76477. config.eager_load = false
  76478. require_environment!
  76479. end
  76480. end
  76481. def run_generators_blocks(app) #:nodoc:
  76482. railties.each { |r| r.run_generators_blocks(app) }
  76483. super
  76484. end
  76485. def run_runner_blocks(app) #:nodoc:
  76486. railties.each { |r| r.run_runner_blocks(app) }
  76487. super
  76488. end
  76489. def run_console_blocks(app) #:nodoc:
  76490. railties.each { |r| r.run_console_blocks(app) }
  76491. super
  76492. end
  76493. # Returns the ordered railties for this application considering railties_order.
  76494. def ordered_railties #:nodoc:
  76495. @ordered_railties ||= begin
  76496. order = config.railties_order.map do |railtie|
  76497. if railtie == :main_app
  76498. self
  76499. elsif railtie.respond_to?(:instance)
  76500. railtie.instance
  76501. else
  76502. railtie
  76503. end
  76504. end
  76505. all = (railties - order)
  76506. all.push(self) unless (all + order).include?(self)
  76507. order.push(:all) unless order.include?(:all)
  76508. index = order.index(:all)
  76509. order[index] = all
  76510. order.reverse.flatten
  76511. end
  76512. end
  76513. def railties_initializers(current) #:nodoc:
  76514. initializers = []
  76515. ordered_railties.each do |r|
  76516. if r == self
  76517. initializers += current
  76518. else
  76519. initializers += r.initializers
  76520. end
  76521. end
  76522. initializers
  76523. end
  76524. def reload_dependencies? #:nodoc:
  76525. config.reload_classes_only_on_change != true || reloaders.map(&:updated?).any?
  76526. end
  76527. def default_middleware_stack #:nodoc:
  76528. ActionDispatch::MiddlewareStack.new.tap do |middleware|
  76529. app = self
  76530. if rack_cache = config.action_dispatch.rack_cache
  76531. begin
  76532. require 'rack/cache'
  76533. rescue LoadError => error
  76534. error.message << ' Be sure to add rack-cache to your Gemfile'
  76535. raise
  76536. end
  76537. if rack_cache == true
  76538. rack_cache = {
  76539. metastore: "rails:/",
  76540. entitystore: "rails:/",
  76541. verbose: false
  76542. }
  76543. end
  76544. require "action_dispatch/http/rack_cache"
  76545. middleware.use ::Rack::Cache, rack_cache
  76546. end
  76547. if config.force_ssl
  76548. middleware.use ::ActionDispatch::SSL, config.ssl_options
  76549. end
  76550. if config.action_dispatch.x_sendfile_header.present?
  76551. middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
  76552. end
  76553. if config.serve_static_assets
  76554. middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
  76555. end
  76556. middleware.use ::Rack::Lock unless config.cache_classes
  76557. middleware.use ::Rack::Runtime
  76558. middleware.use ::Rack::MethodOverride
  76559. middleware.use ::ActionDispatch::RequestId
  76560. middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods
  76561. middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
  76562. middleware.use ::ActionDispatch::DebugExceptions, app
  76563. middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
  76564. unless config.cache_classes
  76565. middleware.use ::ActionDispatch::Reloader, lambda { app.reload_dependencies? }
  76566. end
  76567. middleware.use ::ActionDispatch::Callbacks
  76568. middleware.use ::ActionDispatch::Cookies
  76569. if config.session_store
  76570. if config.force_ssl && !config.session_options.key?(:secure)
  76571. config.session_options[:secure] = true
  76572. end
  76573. middleware.use config.session_store, config.session_options
  76574. middleware.use ::ActionDispatch::Flash
  76575. end
  76576. middleware.use ::ActionDispatch::ParamsParser
  76577. middleware.use ::Rack::Head
  76578. middleware.use ::Rack::ConditionalGet
  76579. middleware.use ::Rack::ETag, "no-cache"
  76580. end
  76581. end
  76582. def build_original_fullpath(env) #:nodoc:
  76583. path_info = env["PATH_INFO"]
  76584. query_string = env["QUERY_STRING"]
  76585. script_name = env["SCRIPT_NAME"]
  76586. if query_string.present?
  76587. "#{script_name}#{path_info}?#{query_string}"
  76588. else
  76589. "#{script_name}#{path_info}"
  76590. end
  76591. end
  76592. end
  76593. end
  76594. require 'active_support/backtrace_cleaner'
  76595. module Rails
  76596. class BacktraceCleaner < ActiveSupport::BacktraceCleaner
  76597. APP_DIRS_PATTERN = /^\/?(app|config|lib|test)/
  76598. RENDER_TEMPLATE_PATTERN = /:in `_render_template_\w*'/
  76599. def initialize
  76600. super
  76601. add_filter { |line| line.sub("#{Rails.root}/", '') }
  76602. add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, '') }
  76603. add_filter { |line| line.sub('./', '/') } # for tests
  76604. add_gem_filters
  76605. add_silencer { |line| line !~ APP_DIRS_PATTERN }
  76606. end
  76607. private
  76608. def add_gem_filters
  76609. gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
  76610. return if gems_paths.empty?
  76611. gems_regexp = %r{(#{gems_paths.join('|')})/gems/([^/]+)-([\w.]+)/(.*)}
  76612. add_filter { |line| line.sub(gems_regexp, '\2 (\3) \4') }
  76613. end
  76614. end
  76615. end
  76616. require 'rbconfig'
  76617. require 'rails/app_rails_loader'
  76618. # If we are inside a Rails application this method performs an exec and thus
  76619. # the rest of this script is not run.
  76620. #
  76621. # TODO: when we hit this, advise adding ./bin to $PATH instead. Then the
  76622. # app's `rails` executable is run immediately.
  76623. Rails::AppRailsLoader.exec_app_rails
  76624. require 'rails/ruby_version_check'
  76625. Signal.trap("INT") { puts; exit(1) }
  76626. if ARGV.first == 'plugin'
  76627. ARGV.shift
  76628. require 'rails/commands/plugin_new'
  76629. else
  76630. require 'rails/commands/application'
  76631. end
  76632. class CodeStatistics #:nodoc:
  76633. TEST_TYPES = ['Controller tests',
  76634. 'Helper tests',
  76635. 'Model tests',
  76636. 'Mailer tests',
  76637. 'Integration tests',
  76638. 'Functional tests (old)',
  76639. 'Unit tests (old)']
  76640. def initialize(*pairs)
  76641. @pairs = pairs
  76642. @statistics = calculate_statistics
  76643. @total = calculate_total if pairs.length > 1
  76644. end
  76645. def to_s
  76646. print_header
  76647. @pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) }
  76648. print_splitter
  76649. if @total
  76650. print_line("Total", @total)
  76651. print_splitter
  76652. end
  76653. print_code_test_stats
  76654. end
  76655. private
  76656. def calculate_statistics
  76657. Hash[@pairs.map{|pair| [pair.first, calculate_directory_statistics(pair.last)]}]
  76658. end
  76659. def calculate_directory_statistics(directory, pattern = /.*\.(rb|js|coffee)$/)
  76660. stats = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
  76661. Dir.foreach(directory) do |file_name|
  76662. if File.directory?(directory + "/" + file_name) and (/^\./ !~ file_name)
  76663. newstats = calculate_directory_statistics(directory + "/" + file_name, pattern)
  76664. stats.each { |k, v| stats[k] += newstats[k] }
  76665. end
  76666. next unless file_name =~ pattern
  76667. comment_started = false
  76668. case file_name
  76669. when /.*\.js$/
  76670. comment_pattern = /^\s*\/\//
  76671. else
  76672. comment_pattern = /^\s*#/
  76673. end
  76674. File.open(directory + "/" + file_name) do |f|
  76675. while line = f.gets
  76676. stats["lines"] += 1
  76677. if(comment_started)
  76678. if line =~ /^=end/
  76679. comment_started = false
  76680. end
  76681. next
  76682. else
  76683. if line =~ /^=begin/
  76684. comment_started = true
  76685. next
  76686. end
  76687. end
  76688. stats["classes"] += 1 if line =~ /^\s*class\s+[_A-Z]/
  76689. stats["methods"] += 1 if line =~ /^\s*def\s+[_a-z]/
  76690. stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ comment_pattern
  76691. end
  76692. end
  76693. end
  76694. stats
  76695. end
  76696. def calculate_total
  76697. total = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
  76698. @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } }
  76699. total
  76700. end
  76701. def calculate_code
  76702. code_loc = 0
  76703. @statistics.each { |k, v| code_loc += v['codelines'] unless TEST_TYPES.include? k }
  76704. code_loc
  76705. end
  76706. def calculate_tests
  76707. test_loc = 0
  76708. @statistics.each { |k, v| test_loc += v['codelines'] if TEST_TYPES.include? k }
  76709. test_loc
  76710. end
  76711. def print_header
  76712. print_splitter
  76713. puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |"
  76714. print_splitter
  76715. end
  76716. def print_splitter
  76717. puts "+----------------------+-------+-------+---------+---------+-----+-------+"
  76718. end
  76719. def print_line(name, statistics)
  76720. m_over_c = (statistics["methods"] / statistics["classes"]) rescue m_over_c = 0
  76721. loc_over_m = (statistics["codelines"] / statistics["methods"]) - 2 rescue loc_over_m = 0
  76722. puts "| #{name.ljust(20)} " +
  76723. "| #{statistics["lines"].to_s.rjust(5)} " +
  76724. "| #{statistics["codelines"].to_s.rjust(5)} " +
  76725. "| #{statistics["classes"].to_s.rjust(7)} " +
  76726. "| #{statistics["methods"].to_s.rjust(7)} " +
  76727. "| #{m_over_c.to_s.rjust(3)} " +
  76728. "| #{loc_over_m.to_s.rjust(5)} |"
  76729. end
  76730. def print_code_test_stats
  76731. code = calculate_code
  76732. tests = calculate_tests
  76733. puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
  76734. puts ""
  76735. end
  76736. end
  76737. require 'rails/version'
  76738. if ['--version', '-v'].include?(ARGV.first)
  76739. puts "Rails #{Rails::VERSION::STRING}"
  76740. exit(0)
  76741. end
  76742. if ARGV.first != "new"
  76743. ARGV[0] = "--help"
  76744. else
  76745. ARGV.shift
  76746. unless ARGV.delete("--no-rc")
  76747. customrc = ARGV.index{ |x| x.include?("--rc=") }
  76748. railsrc = if customrc
  76749. File.expand_path(ARGV.delete_at(customrc).gsub(/--rc=/, ""))
  76750. else
  76751. File.join(File.expand_path("~"), '.railsrc')
  76752. end
  76753. if File.exist?(railsrc)
  76754. extra_args_string = File.read(railsrc)
  76755. extra_args = extra_args_string.split(/\n+/).map {|l| l.split}.flatten
  76756. puts "Using #{extra_args.join(" ")} from #{railsrc}"
  76757. ARGV.insert(1, *extra_args)
  76758. end
  76759. end
  76760. end
  76761. require 'rails/generators'
  76762. require 'rails/generators/rails/app/app_generator'
  76763. module Rails
  76764. module Generators
  76765. class AppGenerator # :nodoc:
  76766. # We want to exit on failure to be kind to other libraries
  76767. # This is only when accessing via CLI
  76768. def self.exit_on_failure?
  76769. true
  76770. end
  76771. end
  76772. end
  76773. end
  76774. Rails::Generators::AppGenerator.start
  76775. require 'optparse'
  76776. require 'irb'
  76777. require 'irb/completion'
  76778. module Rails
  76779. class Console
  76780. class << self
  76781. def start(*args)
  76782. new(*args).start
  76783. end
  76784. def parse_arguments(arguments)
  76785. options = {}
  76786. OptionParser.new do |opt|
  76787. opt.banner = "Usage: rails console [environment] [options]"
  76788. opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
  76789. opt.on("-e", "--environment=name", String,
  76790. "Specifies the environment to run this console under (test/development/production).",
  76791. "Default: development") { |v| options[:environment] = v.strip }
  76792. opt.on("--debugger", 'Enable the debugger.') { |v| options[:debugger] = v }
  76793. opt.parse!(arguments)
  76794. end
  76795. if arguments.first && arguments.first[0] != '-'
  76796. env = arguments.first
  76797. if available_environments.include? env
  76798. options[:environment] = env
  76799. else
  76800. options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
  76801. end
  76802. end
  76803. options
  76804. end
  76805. private
  76806. def available_environments
  76807. Dir['config/environments/*.rb'].map { |fname| File.basename(fname, '.*') }
  76808. end
  76809. end
  76810. attr_reader :options, :app, :console
  76811. def initialize(app, options={})
  76812. @app = app
  76813. @options = options
  76814. app.load_console
  76815. @console = app.config.console || IRB
  76816. end
  76817. def sandbox?
  76818. options[:sandbox]
  76819. end
  76820. def environment
  76821. options[:environment] ||= ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
  76822. end
  76823. def environment?
  76824. environment
  76825. end
  76826. def set_environment!
  76827. Rails.env = environment
  76828. end
  76829. def debugger?
  76830. options[:debugger]
  76831. end
  76832. def start
  76833. app.sandbox = sandbox?
  76834. require_debugger if debugger?
  76835. set_environment! if environment?
  76836. if sandbox?
  76837. puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})"
  76838. puts "Any modifications you make will be rolled back on exit"
  76839. else
  76840. puts "Loading #{Rails.env} environment (Rails #{Rails.version})"
  76841. end
  76842. if defined?(console::ExtendCommandBundle)
  76843. console::ExtendCommandBundle.send :include, Rails::ConsoleMethods
  76844. end
  76845. console.start
  76846. end
  76847. def require_debugger
  76848. require 'debugger'
  76849. puts "=> Debugger enabled"
  76850. rescue LoadError
  76851. puts "You're missing the 'debugger' gem. Add it to your Gemfile, bundle, and try again."
  76852. exit
  76853. end
  76854. end
  76855. end
  76856. require 'erb'
  76857. require 'yaml'
  76858. require 'optparse'
  76859. require 'rbconfig'
  76860. module Rails
  76861. class DBConsole
  76862. attr_reader :arguments
  76863. def self.start
  76864. new.start
  76865. end
  76866. def initialize(arguments = ARGV)
  76867. @arguments = arguments
  76868. end
  76869. def start
  76870. options = parse_arguments(arguments)
  76871. ENV['RAILS_ENV'] = options[:environment] || environment
  76872. case config["adapter"]
  76873. when /^mysql/
  76874. args = {
  76875. 'host' => '--host',
  76876. 'port' => '--port',
  76877. 'socket' => '--socket',
  76878. 'username' => '--user',
  76879. 'encoding' => '--default-character-set',
  76880. 'sslca' => '--ssl-ca',
  76881. 'sslcert' => '--ssl-cert',
  76882. 'sslcapath' => '--ssl-capath',
  76883. 'sslcipher' => '--ssh-cipher',
  76884. 'sslkey' => '--ssl-key'
  76885. }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
  76886. if config['password'] && options['include_password']
  76887. args << "--password=#{config['password']}"
  76888. elsif config['password'] && !config['password'].to_s.empty?
  76889. args << "-p"
  76890. end
  76891. args << config['database']
  76892. find_cmd_and_exec(['mysql', 'mysql5'], *args)
  76893. when "postgresql", "postgres"
  76894. ENV['PGUSER'] = config["username"] if config["username"]
  76895. ENV['PGHOST'] = config["host"] if config["host"]
  76896. ENV['PGPORT'] = config["port"].to_s if config["port"]
  76897. ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && options['include_password']
  76898. find_cmd_and_exec('psql', config["database"])
  76899. when "sqlite"
  76900. find_cmd_and_exec('sqlite', config["database"])
  76901. when "sqlite3"
  76902. args = []
  76903. args << "-#{options['mode']}" if options['mode']
  76904. args << "-header" if options['header']
  76905. args << File.expand_path(config['database'], Rails.respond_to?(:root) ? Rails.root : nil)
  76906. find_cmd_and_exec('sqlite3', *args)
  76907. when "oracle", "oracle_enhanced"
  76908. logon = ""
  76909. if config['username']
  76910. logon = config['username']
  76911. logon << "/#{config['password']}" if config['password'] && options['include_password']
  76912. logon << "@#{config['database']}" if config['database']
  76913. end
  76914. find_cmd_and_exec('sqlplus', logon)
  76915. else
  76916. abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!"
  76917. end
  76918. end
  76919. def config
  76920. @config ||= begin
  76921. cfg = begin
  76922. YAML.load(ERB.new(IO.read("config/database.yml")).result)
  76923. rescue SyntaxError, StandardError
  76924. require APP_PATH
  76925. Rails.application.config.database_configuration
  76926. end
  76927. cfg[environment] || abort("No database is configured for the environment '#{environment}'")
  76928. end
  76929. end
  76930. def environment
  76931. if Rails.respond_to?(:env)
  76932. Rails.env
  76933. else
  76934. ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
  76935. end
  76936. end
  76937. protected
  76938. def parse_arguments(arguments)
  76939. options = {}
  76940. OptionParser.new do |opt|
  76941. opt.banner = "Usage: rails dbconsole [environment] [options]"
  76942. opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v|
  76943. options['include_password'] = true
  76944. end
  76945. opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'],
  76946. "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode|
  76947. options['mode'] = mode
  76948. end
  76949. opt.on("--header") do |h|
  76950. options['header'] = h
  76951. end
  76952. opt.on("-h", "--help", "Show this help message.") do
  76953. puts opt
  76954. exit
  76955. end
  76956. opt.on("-e", "--environment=name", String,
  76957. "Specifies the environment to run this console under (test/development/production).",
  76958. "Default: development"
  76959. ) { |v| options[:environment] = v.strip }
  76960. opt.parse!(arguments)
  76961. abort opt.to_s unless (0..1).include?(arguments.size)
  76962. end
  76963. if arguments.first && arguments.first[0] != '-'
  76964. env = arguments.first
  76965. if available_environments.include? env
  76966. options[:environment] = env
  76967. else
  76968. options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
  76969. end
  76970. end
  76971. options
  76972. end
  76973. def available_environments
  76974. Dir['config/environments/*.rb'].map { |fname| File.basename(fname, '.*') }
  76975. end
  76976. def find_cmd_and_exec(commands, *args)
  76977. commands = Array(commands)
  76978. dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR)
  76979. commands += commands.map{|cmd| "#{cmd}.exe"} if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
  76980. full_path_command = nil
  76981. found = commands.detect do |cmd|
  76982. dirs_on_path.detect do |path|
  76983. full_path_command = File.join(path, cmd)
  76984. File.executable? full_path_command
  76985. end
  76986. end
  76987. if found
  76988. exec full_path_command, *args
  76989. else
  76990. abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
  76991. end
  76992. end
  76993. end
  76994. end
  76995. require 'rails/generators'
  76996. if [nil, "-h", "--help"].include?(ARGV.first)
  76997. Rails::Generators.help 'destroy'
  76998. exit
  76999. end
  77000. name = ARGV.shift
  77001. Rails::Generators.invoke name, ARGV, behavior: :revoke, destination_root: Rails.root
  77002. require 'rails/generators'
  77003. if [nil, "-h", "--help"].include?(ARGV.first)
  77004. Rails::Generators.help 'generate'
  77005. exit
  77006. end
  77007. name = ARGV.shift
  77008. root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
  77009. Rails::Generators.invoke name, ARGV, behavior: :invoke, destination_root: root
  77010. if ARGV.first != "new"
  77011. ARGV[0] = "--help"
  77012. else
  77013. ARGV.shift
  77014. end
  77015. require 'rails/generators'
  77016. require 'rails/generators/rails/plugin_new/plugin_new_generator'
  77017. Rails::Generators::PluginNewGenerator.startrequire 'optparse'
  77018. require 'rbconfig'
  77019. options = { environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup }
  77020. code_or_file = nil
  77021. if ARGV.first.nil?
  77022. ARGV.push "-h"
  77023. end
  77024. ARGV.clone.options do |opts|
  77025. opts.banner = "Usage: rails runner [options] ('Some.ruby(code)' or a filename)"
  77026. opts.separator ""
  77027. opts.on("-e", "--environment=name", String,
  77028. "Specifies the environment for the runner to operate under (test/development/production).",
  77029. "Default: development") { |v| options[:environment] = v }
  77030. opts.separator ""
  77031. opts.on("-h", "--help",
  77032. "Show this help message.") { $stdout.puts opts; exit }
  77033. if RbConfig::CONFIG['host_os'] !~ /mswin|mingw/
  77034. opts.separator ""
  77035. opts.separator "You can also use runner as a shebang line for your executables:"
  77036. opts.separator "-------------------------------------------------------------"
  77037. opts.separator "#!/usr/bin/env #{File.expand_path($0)} runner"
  77038. opts.separator ""
  77039. opts.separator "Product.all.each { |p| p.price *= 2 ; p.save! }"
  77040. opts.separator "-------------------------------------------------------------"
  77041. end
  77042. opts.order! { |o| code_or_file ||= o } rescue retry
  77043. end
  77044. ARGV.delete(code_or_file)
  77045. ENV["RAILS_ENV"] = options[:environment]
  77046. require APP_PATH
  77047. Rails.application.require_environment!
  77048. Rails.application.load_runner
  77049. if code_or_file.nil?
  77050. $stderr.puts "Run '#{$0} -h' for help."
  77051. exit 1
  77052. elsif File.exist?(code_or_file)
  77053. $0 = code_or_file
  77054. eval(File.read(code_or_file), nil, code_or_file)
  77055. else
  77056. eval(code_or_file)
  77057. end
  77058. require 'fileutils'
  77059. require 'optparse'
  77060. require 'action_dispatch'
  77061. module Rails
  77062. class Server < ::Rack::Server
  77063. class Options
  77064. def parse!(args)
  77065. args, options = args.dup, {}
  77066. opt_parser = OptionParser.new do |opts|
  77067. opts.banner = "Usage: rails server [mongrel, thin, etc] [options]"
  77068. opts.on("-p", "--port=port", Integer,
  77069. "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v }
  77070. opts.on("-b", "--binding=ip", String,
  77071. "Binds Rails to the specified ip.", "Default: 0.0.0.0") { |v| options[:Host] = v }
  77072. opts.on("-c", "--config=file", String,
  77073. "Use custom rackup configuration file") { |v| options[:config] = v }
  77074. opts.on("-d", "--daemon", "Make server run as a Daemon.") { options[:daemonize] = true }
  77075. opts.on("-u", "--debugger", "Enable the debugger") { options[:debugger] = true }
  77076. opts.on("-e", "--environment=name", String,
  77077. "Specifies the environment to run this server under (test/development/production).",
  77078. "Default: development") { |v| options[:environment] = v }
  77079. opts.on("-P","--pid=pid",String,
  77080. "Specifies the PID file.",
  77081. "Default: tmp/pids/server.pid") { |v| options[:pid] = v }
  77082. opts.separator ""
  77083. opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
  77084. end
  77085. opt_parser.parse! args
  77086. options[:server] = args.shift
  77087. options
  77088. end
  77089. end
  77090. def initialize(*)
  77091. super
  77092. set_environment
  77093. end
  77094. def app
  77095. @app ||= super.respond_to?(:to_app) ? super.to_app : super
  77096. end
  77097. def opt_parser
  77098. Options.new
  77099. end
  77100. def set_environment
  77101. ENV["RAILS_ENV"] ||= options[:environment]
  77102. end
  77103. def start
  77104. url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}"
  77105. puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
  77106. puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"
  77107. puts "=> Call with -d to detach" unless options[:daemonize]
  77108. trap(:INT) { exit }
  77109. puts "=> Ctrl-C to shutdown server" unless options[:daemonize]
  77110. #Create required tmp directories if not found
  77111. %w(cache pids sessions sockets).each do |dir_to_make|
  77112. FileUtils.mkdir_p(File.join(Rails.root, 'tmp', dir_to_make))
  77113. end
  77114. unless options[:daemonize]
  77115. wrapped_app # touch the app so the logger is set up
  77116. console = ActiveSupport::Logger.new($stdout)
  77117. console.formatter = Rails.logger.formatter
  77118. console.level = Rails.logger.level
  77119. Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
  77120. end
  77121. super
  77122. ensure
  77123. # The '-h' option calls exit before @options is set.
  77124. # If we call 'options' with it unset, we get double help banners.
  77125. puts 'Exiting' unless @options && options[:daemonize]
  77126. end
  77127. def middleware
  77128. middlewares = []
  77129. middlewares << [Rails::Rack::Debugger] if options[:debugger]
  77130. middlewares << [::Rack::ContentLength]
  77131. # FIXME: add Rack::Lock in the case people are using webrick.
  77132. # This is to remain backwards compatible for those who are
  77133. # running webrick in production. We should consider removing this
  77134. # in development.
  77135. if server.name == 'Rack::Handler::WEBrick'
  77136. middlewares << [::Rack::Lock]
  77137. end
  77138. Hash.new(middlewares)
  77139. end
  77140. def log_path
  77141. "log/#{options[:environment]}.log"
  77142. end
  77143. def default_options
  77144. super.merge({
  77145. Port: 3000,
  77146. DoNotReverseLookup: true,
  77147. environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup,
  77148. daemonize: false,
  77149. debugger: false,
  77150. pid: File.expand_path("tmp/pids/server.pid"),
  77151. config: File.expand_path("config.ru")
  77152. })
  77153. end
  77154. end
  77155. end
  77156. require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators'))
  77157. if ARGV.size == 0
  77158. Rails::Generators.help
  77159. exit
  77160. end
  77161. name = ARGV.shift
  77162. Rails::Generators.invoke name, ARGV, behavior: :skip
  77163. ARGV << '--help' if ARGV.empty?
  77164. aliases = {
  77165. "g" => "generate",
  77166. "d" => "destroy",
  77167. "c" => "console",
  77168. "s" => "server",
  77169. "db" => "dbconsole",
  77170. "r" => "runner"
  77171. }
  77172. help_message = <<-EOT
  77173. Usage: rails COMMAND [ARGS]
  77174. The most common rails commands are:
  77175. generate Generate new code (short-cut alias: "g")
  77176. console Start the Rails console (short-cut alias: "c")
  77177. server Start the Rails server (short-cut alias: "s")
  77178. dbconsole Start a console for the database specified in config/database.yml
  77179. (short-cut alias: "db")
  77180. new Create a new Rails application. "rails new my_app" creates a
  77181. new application called MyApp in "./my_app"
  77182. In addition to those, there are:
  77183. application Generate the Rails application code
  77184. destroy Undo code generated with "generate" (short-cut alias: "d")
  77185. plugin new Generates skeleton for developing a Rails plugin
  77186. runner Run a piece of code in the application environment (short-cut alias: "r")
  77187. All commands can be run with -h (or --help) for more information.
  77188. EOT
  77189. command = ARGV.shift
  77190. command = aliases[command] || command
  77191. case command
  77192. when 'generate', 'destroy', 'plugin'
  77193. require 'rails/generators'
  77194. if command == 'plugin' && ARGV.first == 'new'
  77195. require "rails/commands/plugin_new"
  77196. else
  77197. require APP_PATH
  77198. Rails.application.require_environment!
  77199. Rails.application.load_generators
  77200. require "rails/commands/#{command}"
  77201. end
  77202. when 'console'
  77203. require 'rails/commands/console'
  77204. options = Rails::Console.parse_arguments(ARGV)
  77205. # RAILS_ENV needs to be set before config/application is required
  77206. ENV['RAILS_ENV'] = options[:environment] if options[:environment]
  77207. # shift ARGV so IRB doesn't freak
  77208. ARGV.shift if ARGV.first && ARGV.first[0] != '-'
  77209. require APP_PATH
  77210. Rails.application.require_environment!
  77211. Rails::Console.start(Rails.application, options)
  77212. when 'server'
  77213. # Change to the application's path if there is no config.ru file in current dir.
  77214. # This allows us to run `rails server` from other directories, but still get
  77215. # the main config.ru and properly set the tmp directory.
  77216. Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))
  77217. require 'rails/commands/server'
  77218. Rails::Server.new.tap do |server|
  77219. # We need to require application after the server sets environment,
  77220. # otherwise the --environment option given to the server won't propagate.
  77221. require APP_PATH
  77222. Dir.chdir(Rails.application.root)
  77223. server.start
  77224. end
  77225. when 'dbconsole'
  77226. require 'rails/commands/dbconsole'
  77227. Rails::DBConsole.start
  77228. when 'application', 'runner'
  77229. require "rails/commands/#{command}"
  77230. when 'new'
  77231. if %w(-h --help).include?(ARGV.first)
  77232. require 'rails/commands/application'
  77233. else
  77234. puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
  77235. puts "Type 'rails' for help."
  77236. exit(1)
  77237. end
  77238. when '--version', '-v'
  77239. ARGV.unshift '--version'
  77240. require 'rails/commands/application'
  77241. when '-h', '--help'
  77242. puts help_message
  77243. else
  77244. puts "Error: Command '#{command}' not recognized"
  77245. if %x{rake #{command} --dry-run 2>&1 } && $?.success?
  77246. puts "Did you mean: `$ rake #{command}` ?\n\n"
  77247. end
  77248. puts help_message
  77249. exit(1)
  77250. end
  77251. require 'active_support/deprecation'
  77252. require 'active_support/ordered_options'
  77253. require 'active_support/core_ext/object'
  77254. require 'rails/paths'
  77255. require 'rails/rack'
  77256. module Rails
  77257. module Configuration
  77258. # MiddlewareStackProxy is a proxy for the Rails middleware stack that allows
  77259. # you to configure middlewares in your application. It works basically as a
  77260. # command recorder, saving each command to be applied after initialization
  77261. # over the default middleware stack, so you can add, swap, or remove any
  77262. # middleware in Rails.
  77263. #
  77264. # You can add your own middlewares by using the +config.middleware.use+ method:
  77265. #
  77266. # config.middleware.use Magical::Unicorns
  77267. #
  77268. # This will put the <tt>Magical::Unicorns</tt> middleware on the end of the stack.
  77269. # You can use +insert_before+ if you wish to add a middleware before another:
  77270. #
  77271. # config.middleware.insert_before ActionDispatch::Head, Magical::Unicorns
  77272. #
  77273. # There's also +insert_after+ which will insert a middleware after another:
  77274. #
  77275. # config.middleware.insert_after ActionDispatch::Head, Magical::Unicorns
  77276. #
  77277. # Middlewares can also be completely swapped out and replaced with others:
  77278. #
  77279. # config.middleware.swap ActionDispatch::Flash, Magical::Unicorns
  77280. #
  77281. # And finally they can also be removed from the stack completely:
  77282. #
  77283. # config.middleware.delete ActionDispatch::Flash
  77284. #
  77285. class MiddlewareStackProxy
  77286. def initialize
  77287. @operations = []
  77288. end
  77289. def insert_before(*args, &block)
  77290. @operations << [__method__, args, block]
  77291. end
  77292. alias :insert :insert_before
  77293. def insert_after(*args, &block)
  77294. @operations << [__method__, args, block]
  77295. end
  77296. def swap(*args, &block)
  77297. @operations << [__method__, args, block]
  77298. end
  77299. def use(*args, &block)
  77300. @operations << [__method__, args, block]
  77301. end
  77302. def delete(*args, &block)
  77303. @operations << [__method__, args, block]
  77304. end
  77305. def merge_into(other) #:nodoc:
  77306. @operations.each do |operation, args, block|
  77307. other.send(operation, *args, &block)
  77308. end
  77309. other
  77310. end
  77311. end
  77312. class Generators #:nodoc:
  77313. attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging
  77314. attr_reader :hidden_namespaces
  77315. def initialize
  77316. @aliases = Hash.new { |h,k| h[k] = {} }
  77317. @options = Hash.new { |h,k| h[k] = {} }
  77318. @fallbacks = {}
  77319. @templates = []
  77320. @colorize_logging = true
  77321. @hidden_namespaces = []
  77322. end
  77323. def initialize_copy(source)
  77324. @aliases = @aliases.deep_dup
  77325. @options = @options.deep_dup
  77326. @fallbacks = @fallbacks.deep_dup
  77327. @templates = @templates.dup
  77328. end
  77329. def hide_namespace(namespace)
  77330. @hidden_namespaces << namespace
  77331. end
  77332. def method_missing(method, *args)
  77333. method = method.to_s.sub(/=$/, '').to_sym
  77334. return @options[method] if args.empty?
  77335. if method == :rails || args.first.is_a?(Hash)
  77336. namespace, configuration = method, args.shift
  77337. else
  77338. namespace, configuration = args.shift, args.shift
  77339. namespace = namespace.to_sym if namespace.respond_to?(:to_sym)
  77340. @options[:rails][method] = namespace
  77341. end
  77342. if configuration
  77343. aliases = configuration.delete(:aliases)
  77344. @aliases[namespace].merge!(aliases) if aliases
  77345. @options[namespace].merge!(configuration)
  77346. end
  77347. end
  77348. end
  77349. end
  77350. end
  77351. require 'active_support/all'
  77352. require 'action_controller'
  77353. module Rails
  77354. module ConsoleMethods
  77355. # reference the global "app" instance, created on demand. To recreate the
  77356. # instance, pass a non-false value as the parameter.
  77357. def app(create=false)
  77358. @app_integration_instance = nil if create
  77359. @app_integration_instance ||= new_session do |sess|
  77360. sess.host! "www.example.com"
  77361. end
  77362. end
  77363. # create a new session. If a block is given, the new session will be yielded
  77364. # to the block before being returned.
  77365. def new_session
  77366. app = Rails.application
  77367. session = ActionDispatch::Integration::Session.new(app)
  77368. yield session if block_given?
  77369. session
  77370. end
  77371. # reloads the environment
  77372. def reload!(print=true)
  77373. puts "Reloading..." if print
  77374. ActionDispatch::Reloader.cleanup!
  77375. ActionDispatch::Reloader.prepare!
  77376. true
  77377. end
  77378. end
  77379. end
  77380. module Rails
  77381. module ConsoleMethods
  77382. def helper
  77383. @helper ||= ApplicationController.helpers
  77384. end
  77385. def controller
  77386. @controller ||= ApplicationController.new
  77387. end
  77388. end
  77389. end
  77390. require 'active_support/deprecation/proxy_wrappers'
  77391. module Rails
  77392. class DeprecatedConstant < ActiveSupport::Deprecation::DeprecatedConstantProxy
  77393. def self.deprecate(old, current)
  77394. # double assignment is used to avoid "assigned but unused variable" warning
  77395. constant = constant = new(old, current)
  77396. eval "::#{old} = constant"
  77397. end
  77398. private
  77399. def target
  77400. ::Kernel.eval @new_const.to_s
  77401. end
  77402. end
  77403. DeprecatedConstant.deprecate('RAILS_CACHE', '::Rails.cache')
  77404. end
  77405. ARGV << '--help' if ARGV.empty?
  77406. aliases = {
  77407. "g" => "generate",
  77408. "d" => "destroy"
  77409. }
  77410. command = ARGV.shift
  77411. command = aliases[command] || command
  77412. require ENGINE_PATH
  77413. engine = ::Rails::Engine.find(ENGINE_ROOT)
  77414. case command
  77415. when 'generate', 'destroy'
  77416. require 'rails/generators'
  77417. Rails::Generators.namespace = engine.railtie_namespace
  77418. engine.load_generators
  77419. require "rails/commands/#{command}"
  77420. when '--version', '-v'
  77421. ARGV.unshift '--version'
  77422. require 'rails/commands/application'
  77423. else
  77424. puts "Error: Command not recognized" unless %w(-h --help).include?(command)
  77425. puts <<-EOT
  77426. Usage: rails COMMAND [ARGS]
  77427. The common rails commands available for engines are:
  77428. generate Generate new code (short-cut alias: "g")
  77429. destroy Undo code generated with "generate" (short-cut alias: "d")
  77430. All commands can be run with -h for more information.
  77431. If you want to run any commands that need to be run in context
  77432. of the application, like `rails server` or `rails console`,
  77433. you should do it from application's directory (typically test/dummy).
  77434. EOT
  77435. exit(1)
  77436. end
  77437. require 'rails/railtie/configuration'
  77438. module Rails
  77439. class Engine
  77440. class Configuration < ::Rails::Railtie::Configuration
  77441. attr_reader :root
  77442. attr_writer :middleware, :autoload_once_paths, :autoload_paths
  77443. def initialize(root=nil)
  77444. super()
  77445. @root = root
  77446. @generators = app_generators.dup
  77447. end
  77448. # Returns the middleware stack for the engine.
  77449. def middleware
  77450. @middleware ||= Rails::Configuration::MiddlewareStackProxy.new
  77451. end
  77452. # Holds generators configuration:
  77453. #
  77454. # config.generators do |g|
  77455. # g.orm :data_mapper, migration: true
  77456. # g.template_engine :haml
  77457. # g.test_framework :rspec
  77458. # end
  77459. #
  77460. # If you want to disable color in console, do:
  77461. #
  77462. # config.generators.colorize_logging = false
  77463. #
  77464. def generators #:nodoc:
  77465. @generators ||= Rails::Configuration::Generators.new
  77466. yield(@generators) if block_given?
  77467. @generators
  77468. end
  77469. def paths
  77470. @paths ||= begin
  77471. paths = Rails::Paths::Root.new(@root)
  77472. paths.add "app", autoload: true, glob: "*"
  77473. paths.add "app/assets", glob: "*"
  77474. paths.add "app/controllers", autoload: true
  77475. paths.add "app/helpers", autoload: true
  77476. paths.add "app/models", autoload: true
  77477. paths.add "app/mailers", autoload: true
  77478. paths.add "app/views"
  77479. paths.add "app/controllers/concerns", autoload: true
  77480. paths.add "app/models/concerns", autoload: true
  77481. paths.add "lib", load_path: true
  77482. paths.add "lib/assets", glob: "*"
  77483. paths.add "lib/tasks", glob: "**/*.rake"
  77484. paths.add "config"
  77485. paths.add "config/environments", glob: "#{Rails.env}.rb"
  77486. paths.add "config/initializers", glob: "**/*.rb"
  77487. paths.add "config/locales", glob: "*.{rb,yml}"
  77488. paths.add "config/routes.rb"
  77489. paths.add "db"
  77490. paths.add "db/migrate"
  77491. paths.add "db/seeds.rb"
  77492. paths.add "vendor", load_path: true
  77493. paths.add "vendor/assets", glob: "*"
  77494. paths
  77495. end
  77496. end
  77497. def root=(value)
  77498. @root = paths.path = Pathname.new(value).expand_path
  77499. end
  77500. def eager_load_paths
  77501. ActiveSupport::Deprecation.warn "eager_load_paths is deprecated and all autoload_paths are now eagerly loaded."
  77502. autoload_paths
  77503. end
  77504. def eager_load_paths=(paths)
  77505. ActiveSupport::Deprecation.warn "eager_load_paths is deprecated and all autoload_paths are now eagerly loaded."
  77506. self.autoload_paths = paths
  77507. end
  77508. def autoload_once_paths
  77509. @autoload_once_paths ||= paths.autoload_once
  77510. end
  77511. def autoload_paths
  77512. @autoload_paths ||= paths.autoload_paths
  77513. end
  77514. end
  77515. end
  77516. end
  77517. module Rails
  77518. class Engine < Railtie
  77519. class Railties
  77520. include Enumerable
  77521. attr_reader :_all
  77522. def initialize
  77523. @_all ||= ::Rails::Railtie.subclasses.map(&:instance) +
  77524. ::Rails::Engine.subclasses.map(&:instance)
  77525. end
  77526. def self.engines
  77527. @engines ||= ::Rails::Engine.subclasses.map(&:instance)
  77528. end
  77529. def each(*args, &block)
  77530. _all.each(*args, &block)
  77531. end
  77532. def -(others)
  77533. _all - others
  77534. end
  77535. delegate :engines, to: "self.class"
  77536. end
  77537. end
  77538. end
  77539. ActiveSupport::Deprecation.deprecate_methods(Rails::Engine::Railties, :engines)
  77540. require 'rails/railtie'
  77541. require 'active_support/core_ext/module/delegation'
  77542. require 'pathname'
  77543. require 'rbconfig'
  77544. module Rails
  77545. # <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
  77546. # functionality and share it with other applications or within a larger packaged application.
  77547. # Since Rails 3.0, every <tt>Rails::Application</tt> is just an engine, which allows for simple
  77548. # feature and application sharing.
  77549. #
  77550. # Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same
  77551. # methods (like <tt>rake_tasks</tt> and +generators+) and configuration
  77552. # options that are available in railties can also be used in engines.
  77553. #
  77554. # == Creating an Engine
  77555. #
  77556. # In Rails versions prior to 3.0, your gems automatically behaved as engines, however,
  77557. # this coupled Rails to Rubygems. Since Rails 3.0, if you want a gem to automatically
  77558. # behave as an engine, you have to specify an +Engine+ for it somewhere inside
  77559. # your plugin's +lib+ folder (similar to how we specify a +Railtie+):
  77560. #
  77561. # # lib/my_engine.rb
  77562. # module MyEngine
  77563. # class Engine < Rails::Engine
  77564. # end
  77565. # end
  77566. #
  77567. # Then ensure that this file is loaded at the top of your <tt>config/application.rb</tt>
  77568. # (or in your +Gemfile+) and it will automatically load models, controllers and helpers
  77569. # inside +app+, load routes at <tt>config/routes.rb</tt>, load locales at
  77570. # <tt>config/locales/*</tt>, and load tasks at <tt>lib/tasks/*</tt>.
  77571. #
  77572. # == Configuration
  77573. #
  77574. # Besides the +Railtie+ configuration which is shared across the application, in a
  77575. # <tt>Rails::Engine</tt> you can access <tt>autoload_paths</tt> and <tt>autoload_once_paths</tt>,
  77576. # which, differently from a <tt>Railtie</tt>, are scoped to the current engine.
  77577. #
  77578. # class MyEngine < Rails::Engine
  77579. # # Add a load path for this specific Engine
  77580. # config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
  77581. #
  77582. # initializer "my_engine.add_middleware" do |app|
  77583. # app.middleware.use MyEngine::Middleware
  77584. # end
  77585. # end
  77586. #
  77587. # == Generators
  77588. #
  77589. # You can set up generators for engines with <tt>config.generators</tt> method:
  77590. #
  77591. # class MyEngine < Rails::Engine
  77592. # config.generators do |g|
  77593. # g.orm :active_record
  77594. # g.template_engine :erb
  77595. # g.test_framework :test_unit
  77596. # end
  77597. # end
  77598. #
  77599. # You can also set generators for an application by using <tt>config.app_generators</tt>:
  77600. #
  77601. # class MyEngine < Rails::Engine
  77602. # # note that you can also pass block to app_generators in the same way you
  77603. # # can pass it to generators method
  77604. # config.app_generators.orm :datamapper
  77605. # end
  77606. #
  77607. # == Paths
  77608. #
  77609. # Since Rails 3.0, applications and engines have more flexible path configuration (as
  77610. # opposed to the previous hardcoded path configuration). This means that you are not
  77611. # required to place your controllers at <tt>app/controllers</tt>, but in any place
  77612. # which you find convenient.
  77613. #
  77614. # For example, let's suppose you want to place your controllers in <tt>lib/controllers</tt>.
  77615. # You can set that as an option:
  77616. #
  77617. # class MyEngine < Rails::Engine
  77618. # paths["app/controllers"] = "lib/controllers"
  77619. # end
  77620. #
  77621. # You can also have your controllers loaded from both <tt>app/controllers</tt> and
  77622. # <tt>lib/controllers</tt>:
  77623. #
  77624. # class MyEngine < Rails::Engine
  77625. # paths["app/controllers"] << "lib/controllers"
  77626. # end
  77627. #
  77628. # The available paths in an engine are:
  77629. #
  77630. # class MyEngine < Rails::Engine
  77631. # paths["app"] # => ["app"]
  77632. # paths["app/controllers"] # => ["app/controllers"]
  77633. # paths["app/helpers"] # => ["app/helpers"]
  77634. # paths["app/models"] # => ["app/models"]
  77635. # paths["app/views"] # => ["app/views"]
  77636. # paths["lib"] # => ["lib"]
  77637. # paths["lib/tasks"] # => ["lib/tasks"]
  77638. # paths["config"] # => ["config"]
  77639. # paths["config/initializers"] # => ["config/initializers"]
  77640. # paths["config/locales"] # => ["config/locales"]
  77641. # paths["config/routes"] # => ["config/routes.rb"]
  77642. # end
  77643. #
  77644. # The <tt>Application</tt> class adds a couple more paths to this set. And as in your
  77645. # <tt>Application</tt>, all folders under +app+ are automatically added to the load path.
  77646. # If you have an <tt>app/services/tt> folder for example, it will be added by default.
  77647. #
  77648. # == Endpoint
  77649. #
  77650. # An engine can be also a rack application. It can be useful if you have a rack application that
  77651. # you would like to wrap with +Engine+ and provide some of the +Engine+'s features.
  77652. #
  77653. # To do that, use the +endpoint+ method:
  77654. #
  77655. # module MyEngine
  77656. # class Engine < Rails::Engine
  77657. # endpoint MyRackApplication
  77658. # end
  77659. # end
  77660. #
  77661. # Now you can mount your engine in application's routes just like that:
  77662. #
  77663. # MyRailsApp::Application.routes.draw do
  77664. # mount MyEngine::Engine => "/engine"
  77665. # end
  77666. #
  77667. # == Middleware stack
  77668. #
  77669. # As an engine can now be a rack endpoint, it can also have a middleware
  77670. # stack. The usage is exactly the same as in <tt>Application</tt>:
  77671. #
  77672. # module MyEngine
  77673. # class Engine < Rails::Engine
  77674. # middleware.use SomeMiddleware
  77675. # end
  77676. # end
  77677. #
  77678. # == Routes
  77679. #
  77680. # If you don't specify an endpoint, routes will be used as the default
  77681. # endpoint. You can use them just like you use an application's routes:
  77682. #
  77683. # # ENGINE/config/routes.rb
  77684. # MyEngine::Engine.routes.draw do
  77685. # get "/" => "posts#index"
  77686. # end
  77687. #
  77688. # == Mount priority
  77689. #
  77690. # Note that now there can be more than one router in your application, and it's better to avoid
  77691. # passing requests through many routers. Consider this situation:
  77692. #
  77693. # MyRailsApp::Application.routes.draw do
  77694. # mount MyEngine::Engine => "/blog"
  77695. # get "/blog/omg" => "main#omg"
  77696. # end
  77697. #
  77698. # +MyEngine+ is mounted at <tt>/blog</tt>, and <tt>/blog/omg</tt> points to application's
  77699. # controller. In such a situation, requests to <tt>/blog/omg</tt> will go through +MyEngine+,
  77700. # and if there is no such route in +Engine+'s routes, it will be dispatched to <tt>main#omg</tt>.
  77701. # It's much better to swap that:
  77702. #
  77703. # MyRailsApp::Application.routes.draw do
  77704. # get "/blog/omg" => "main#omg"
  77705. # mount MyEngine::Engine => "/blog"
  77706. # end
  77707. #
  77708. # Now, +Engine+ will get only requests that were not handled by +Application+.
  77709. #
  77710. # == Engine name
  77711. #
  77712. # There are some places where an Engine's name is used:
  77713. #
  77714. # * routes: when you mount an Engine with <tt>mount(MyEngine::Engine => '/my_engine')</tt>,
  77715. # it's used as default <tt>:as</tt> option
  77716. # * rake task for installing migrations <tt>my_engine:install:migrations</tt>
  77717. #
  77718. # Engine name is set by default based on class name. For <tt>MyEngine::Engine</tt> it will be
  77719. # <tt>my_engine_engine</tt>. You can change it manually using the <tt>engine_name</tt> method:
  77720. #
  77721. # module MyEngine
  77722. # class Engine < Rails::Engine
  77723. # engine_name "my_engine"
  77724. # end
  77725. # end
  77726. #
  77727. # == Isolated Engine
  77728. #
  77729. # Normally when you create controllers, helpers and models inside an engine, they are treated
  77730. # as if they were created inside the application itself. This means that all helpers and
  77731. # named routes from the application will be available to your engine's controllers as well.
  77732. #
  77733. # However, sometimes you want to isolate your engine from the application, especially if your engine
  77734. # has its own router. To do that, you simply need to call +isolate_namespace+. This method requires
  77735. # you to pass a module where all your controllers, helpers and models should be nested to:
  77736. #
  77737. # module MyEngine
  77738. # class Engine < Rails::Engine
  77739. # isolate_namespace MyEngine
  77740. # end
  77741. # end
  77742. #
  77743. # With such an engine, everything that is inside the +MyEngine+ module will be isolated from
  77744. # the application.
  77745. #
  77746. # Consider such controller:
  77747. #
  77748. # module MyEngine
  77749. # class FooController < ActionController::Base
  77750. # end
  77751. # end
  77752. #
  77753. # If an engine is marked as isolated, +FooController+ has access only to helpers from +Engine+ and
  77754. # <tt>url_helpers</tt> from <tt>MyEngine::Engine.routes</tt>.
  77755. #
  77756. # The next thing that changes in isolated engines is the behavior of routes. Normally, when you namespace
  77757. # your controllers, you also need to do namespace all your routes. With an isolated engine,
  77758. # the namespace is applied by default, so you can ignore it in routes:
  77759. #
  77760. # MyEngine::Engine.routes.draw do
  77761. # resources :articles
  77762. # end
  77763. #
  77764. # The routes above will automatically point to <tt>MyEngine::ArticlesController</tt>. Furthermore, you don't
  77765. # need to use longer url helpers like <tt>my_engine_articles_path</tt>. Instead, you should simply use
  77766. # <tt>articles_path</tt> as you would do with your application.
  77767. #
  77768. # To make that behavior consistent with other parts of the framework, an isolated engine also has influence on
  77769. # <tt>ActiveModel::Naming</tt>. When you use a namespaced model, like <tt>MyEngine::Article</tt>, it will normally
  77770. # use the prefix "my_engine". In an isolated engine, the prefix will be omitted in url helpers and
  77771. # form fields for convenience.
  77772. #
  77773. # polymorphic_url(MyEngine::Article.new) # => "articles_path"
  77774. #
  77775. # form_for(MyEngine::Article.new) do
  77776. # text_field :title # => <input type="text" name="article[title]" id="article_title" />
  77777. # end
  77778. #
  77779. # Additionally, an isolated engine will set its name according to namespace, so
  77780. # MyEngine::Engine.engine_name will be "my_engine". It will also set MyEngine.table_name_prefix
  77781. # to "my_engine_", changing the MyEngine::Article model to use the my_engine_articles table.
  77782. #
  77783. # == Using Engine's routes outside Engine
  77784. #
  77785. # Since you can now mount an engine inside application's routes, you do not have direct access to +Engine+'s
  77786. # <tt>url_helpers</tt> inside +Application+. When you mount an engine in an application's routes, a special helper is
  77787. # created to allow you to do that. Consider such a scenario:
  77788. #
  77789. # # config/routes.rb
  77790. # MyApplication::Application.routes.draw do
  77791. # mount MyEngine::Engine => "/my_engine", as: "my_engine"
  77792. # get "/foo" => "foo#index"
  77793. # end
  77794. #
  77795. # Now, you can use the <tt>my_engine</tt> helper inside your application:
  77796. #
  77797. # class FooController < ApplicationController
  77798. # def index
  77799. # my_engine.root_url #=> /my_engine/
  77800. # end
  77801. # end
  77802. #
  77803. # There is also a <tt>main_app</tt> helper that gives you access to application's routes inside Engine:
  77804. #
  77805. # module MyEngine
  77806. # class BarController
  77807. # def index
  77808. # main_app.foo_path #=> /foo
  77809. # end
  77810. # end
  77811. # end
  77812. #
  77813. # Note that the <tt>:as</tt> option given to mount takes the <tt>engine_name</tt> as default, so most of the time
  77814. # you can simply omit it.
  77815. #
  77816. # Finally, if you want to generate a url to an engine's route using
  77817. # <tt>polymorphic_url</tt>, you also need to pass the engine helper. Let's
  77818. # say that you want to create a form pointing to one of the engine's routes.
  77819. # All you need to do is pass the helper as the first element in array with
  77820. # attributes for url:
  77821. #
  77822. # form_for([my_engine, @user])
  77823. #
  77824. # This code will use <tt>my_engine.user_path(@user)</tt> to generate the proper route.
  77825. #
  77826. # == Isolated engine's helpers
  77827. #
  77828. # Sometimes you may want to isolate engine, but use helpers that are defined for it.
  77829. # If you want to share just a few specific helpers you can add them to application's
  77830. # helpers in ApplicationController:
  77831. #
  77832. # class ApplicationController < ActionController::Base
  77833. # helper MyEngine::SharedEngineHelper
  77834. # end
  77835. #
  77836. # If you want to include all of the engine's helpers, you can use #helper method on an engine's
  77837. # instance:
  77838. #
  77839. # class ApplicationController < ActionController::Base
  77840. # helper MyEngine::Engine.helpers
  77841. # end
  77842. #
  77843. # It will include all of the helpers from engine's directory. Take into account that this does
  77844. # not include helpers defined in controllers with helper_method or other similar solutions,
  77845. # only helpers defined in the helpers directory will be included.
  77846. #
  77847. # == Migrations & seed data
  77848. #
  77849. # Engines can have their own migrations. The default path for migrations is exactly the same
  77850. # as in application: <tt>db/migrate</tt>
  77851. #
  77852. # To use engine's migrations in application you can use rake task, which copies them to
  77853. # application's dir:
  77854. #
  77855. # rake ENGINE_NAME:install:migrations
  77856. #
  77857. # Note that some of the migrations may be skipped if a migration with the same name already exists
  77858. # in application. In such a situation you must decide whether to leave that migration or rename the
  77859. # migration in the application and rerun copying migrations.
  77860. #
  77861. # If your engine has migrations, you may also want to prepare data for the database in
  77862. # the <tt>db/seeds.rb</tt> file. You can load that data using the <tt>load_seed</tt> method, e.g.
  77863. #
  77864. # MyEngine::Engine.load_seed
  77865. #
  77866. # == Loading priority
  77867. #
  77868. # In order to change engine's priority you can use +config.railties_order+ in main application.
  77869. # It will affect the priority of loading views, helpers, assets and all the other files
  77870. # related to engine or application.
  77871. #
  77872. # # load Blog::Engine with highest priority, followed by application and other railties
  77873. # config.railties_order = [Blog::Engine, :main_app, :all]
  77874. class Engine < Railtie
  77875. autoload :Configuration, "rails/engine/configuration"
  77876. class << self
  77877. attr_accessor :called_from, :isolated
  77878. alias :isolated? :isolated
  77879. alias :engine_name :railtie_name
  77880. delegate :eager_load!, to: :instance
  77881. def inherited(base)
  77882. unless base.abstract_railtie?
  77883. Rails::Railtie::Configuration.eager_load_namespaces << base
  77884. base.called_from = begin
  77885. # Remove the line number from backtraces making sure we don't leave anything behind
  77886. call_stack = caller.map { |p| p.sub(/:\d+.*/, '') }
  77887. File.dirname(call_stack.detect { |p| p !~ %r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack] })
  77888. end
  77889. end
  77890. super
  77891. end
  77892. def endpoint(endpoint = nil)
  77893. @endpoint ||= nil
  77894. @endpoint = endpoint if endpoint
  77895. @endpoint
  77896. end
  77897. def isolate_namespace(mod)
  77898. engine_name(generate_railtie_name(mod))
  77899. self.routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) }
  77900. self.isolated = true
  77901. unless mod.respond_to?(:railtie_namespace)
  77902. name, railtie = engine_name, self
  77903. mod.singleton_class.instance_eval do
  77904. define_method(:railtie_namespace) { railtie }
  77905. unless mod.respond_to?(:table_name_prefix)
  77906. define_method(:table_name_prefix) { "#{name}_" }
  77907. end
  77908. unless mod.respond_to?(:use_relative_model_naming?)
  77909. class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__
  77910. end
  77911. unless mod.respond_to?(:railtie_helpers_paths)
  77912. define_method(:railtie_helpers_paths) { railtie.helpers_paths }
  77913. end
  77914. unless mod.respond_to?(:railtie_routes_url_helpers)
  77915. define_method(:railtie_routes_url_helpers) { railtie.routes.url_helpers }
  77916. end
  77917. end
  77918. end
  77919. end
  77920. # Finds engine with given path
  77921. def find(path)
  77922. expanded_path = File.expand_path path
  77923. Rails::Engine.subclasses.each do |klass|
  77924. engine = klass.instance
  77925. return engine if File.expand_path(engine.root) == expanded_path
  77926. end
  77927. nil
  77928. end
  77929. end
  77930. delegate :middleware, :root, :paths, to: :config
  77931. delegate :engine_name, :isolated?, to: :class
  77932. def initialize
  77933. @_all_autoload_paths = nil
  77934. @_all_load_paths = nil
  77935. @app = nil
  77936. @config = nil
  77937. @env_config = nil
  77938. @helpers = nil
  77939. @routes = nil
  77940. super
  77941. end
  77942. # Load console and invoke the registered hooks.
  77943. # Check <tt>Rails::Railtie.console</tt> for more info.
  77944. def load_console(app=self)
  77945. require "pp"
  77946. require "rails/console/app"
  77947. require "rails/console/helpers"
  77948. run_console_blocks(app)
  77949. self
  77950. end
  77951. # Load Rails runner and invoke the registered hooks.
  77952. # Check <tt>Rails::Railtie.runner</tt> for more info.
  77953. def load_runner(app=self)
  77954. run_runner_blocks(app)
  77955. self
  77956. end
  77957. # Load Rake, railties tasks and invoke the registered hooks.
  77958. # Check <tt>Rails::Railtie.rake_tasks</tt> for more info.
  77959. def load_tasks(app=self)
  77960. require "rake"
  77961. run_tasks_blocks(app)
  77962. self
  77963. end
  77964. # Load rails generators and invoke the registered hooks.
  77965. # Check <tt>Rails::Railtie.generators</tt> for more info.
  77966. def load_generators(app=self)
  77967. require "rails/generators"
  77968. run_generators_blocks(app)
  77969. Rails::Generators.configure!(app.config.generators)
  77970. self
  77971. end
  77972. # Eager load the application by loading all ruby
  77973. # files inside autoload_paths.
  77974. def eager_load!
  77975. config.autoload_paths.each do |load_path|
  77976. matcher = /\A#{Regexp.escape(load_path)}\/(.*)\.rb\Z/
  77977. Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
  77978. require_dependency file.sub(matcher, '\1')
  77979. end
  77980. end
  77981. end
  77982. def railties
  77983. @railties ||= self.class::Railties.new
  77984. end
  77985. # Returns a module with all the helpers defined for the engine.
  77986. def helpers
  77987. @helpers ||= begin
  77988. helpers = Module.new
  77989. all = ActionController::Base.all_helpers_from_path(helpers_paths)
  77990. ActionController::Base.modules_for_helpers(all).each do |mod|
  77991. helpers.send(:include, mod)
  77992. end
  77993. helpers
  77994. end
  77995. end
  77996. # Returns all registered helpers paths.
  77997. def helpers_paths
  77998. paths["app/helpers"].existent
  77999. end
  78000. # Returns the underlying rack application for this engine.
  78001. def app
  78002. @app ||= begin
  78003. config.middleware = config.middleware.merge_into(default_middleware_stack)
  78004. config.middleware.build(endpoint)
  78005. end
  78006. end
  78007. # Returns the endpoint for this engine. If none is registered,
  78008. # defaults to an ActionDispatch::Routing::RouteSet.
  78009. def endpoint
  78010. self.class.endpoint || routes
  78011. end
  78012. # Define the Rack API for this engine.
  78013. def call(env)
  78014. env.merge!(env_config)
  78015. if env['SCRIPT_NAME']
  78016. env.merge! "ROUTES_#{routes.object_id}_SCRIPT_NAME" => env['SCRIPT_NAME'].dup
  78017. end
  78018. app.call(env)
  78019. end
  78020. # Defines additional Rack env configuration that is added on each call.
  78021. def env_config
  78022. @env_config ||= {
  78023. 'action_dispatch.routes' => routes
  78024. }
  78025. end
  78026. # Defines the routes for this engine. If a block is given to
  78027. # routes, it is appended to the engine.
  78028. def routes
  78029. @routes ||= ActionDispatch::Routing::RouteSet.new
  78030. @routes.append(&Proc.new) if block_given?
  78031. @routes
  78032. end
  78033. # Define the configuration object for the engine.
  78034. def config
  78035. @config ||= Engine::Configuration.new(find_root_with_flag("lib"))
  78036. end
  78037. # Load data from db/seeds.rb file. It can be used in to load engines'
  78038. # seeds, e.g.:
  78039. #
  78040. # Blog::Engine.load_seed
  78041. def load_seed
  78042. seed_file = paths["db/seeds.rb"].existent.first
  78043. load(seed_file) if seed_file
  78044. end
  78045. # Add configured load paths to ruby load paths and remove duplicates.
  78046. initializer :set_load_path, before: :bootstrap_hook do
  78047. _all_load_paths.reverse_each do |path|
  78048. $LOAD_PATH.unshift(path) if File.directory?(path)
  78049. end
  78050. $LOAD_PATH.uniq!
  78051. end
  78052. # Set the paths from which Rails will automatically load source files,
  78053. # and the load_once paths.
  78054. #
  78055. # This needs to be an initializer, since it needs to run once
  78056. # per engine and get the engine as a block parameter
  78057. initializer :set_autoload_paths, before: :bootstrap_hook do |app|
  78058. ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
  78059. ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
  78060. # Freeze so future modifications will fail rather than do nothing mysteriously
  78061. config.autoload_paths.freeze
  78062. config.autoload_once_paths.freeze
  78063. end
  78064. initializer :add_routing_paths do |app|
  78065. paths = self.paths["config/routes.rb"].existent
  78066. if routes? || paths.any?
  78067. app.routes_reloader.paths.unshift(*paths)
  78068. app.routes_reloader.route_sets << routes
  78069. end
  78070. end
  78071. # I18n load paths are a special case since the ones added
  78072. # later have higher priority.
  78073. initializer :add_locales do
  78074. config.i18n.railties_load_path.concat(paths["config/locales"].existent)
  78075. end
  78076. initializer :add_view_paths do
  78077. views = paths["app/views"].existent
  78078. unless views.empty?
  78079. ActiveSupport.on_load(:action_controller){ prepend_view_path(views) if respond_to?(:prepend_view_path) }
  78080. ActiveSupport.on_load(:action_mailer){ prepend_view_path(views) }
  78081. end
  78082. end
  78083. initializer :load_environment_config, before: :load_environment_hook, group: :all do
  78084. paths["config/environments"].existent.each do |environment|
  78085. require environment
  78086. end
  78087. end
  78088. initializer :append_assets_path, group: :all do |app|
  78089. app.config.assets.paths.unshift(*paths["vendor/assets"].existent_directories)
  78090. app.config.assets.paths.unshift(*paths["lib/assets"].existent_directories)
  78091. app.config.assets.paths.unshift(*paths["app/assets"].existent_directories)
  78092. end
  78093. initializer :prepend_helpers_path do |app|
  78094. if !isolated? || (app == self)
  78095. app.config.helpers_paths.unshift(*paths["app/helpers"].existent)
  78096. end
  78097. end
  78098. initializer :load_config_initializers do
  78099. config.paths["config/initializers"].existent.sort.each do |initializer|
  78100. load(initializer)
  78101. end
  78102. end
  78103. initializer :engines_blank_point do
  78104. # We need this initializer so all extra initializers added in engines are
  78105. # consistently executed after all the initializers above across all engines.
  78106. end
  78107. rake_tasks do
  78108. next if self.is_a?(Rails::Application)
  78109. next unless has_migrations?
  78110. namespace railtie_name do
  78111. namespace :install do
  78112. desc "Copy migrations from #{railtie_name} to application"
  78113. task :migrations do
  78114. ENV["FROM"] = railtie_name
  78115. if Rake::Task.task_defined?("railties:install:migrations")
  78116. Rake::Task["railties:install:migrations"].invoke
  78117. else
  78118. Rake::Task["app:railties:install:migrations"].invoke
  78119. end
  78120. end
  78121. end
  78122. end
  78123. end
  78124. protected
  78125. def run_tasks_blocks(*) #:nodoc:
  78126. super
  78127. paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
  78128. end
  78129. def routes? #:nodoc:
  78130. @routes
  78131. end
  78132. def has_migrations? #:nodoc:
  78133. paths["db/migrate"].existent.any?
  78134. end
  78135. def find_root_with_flag(flag, default=nil) #:nodoc:
  78136. root_path = self.class.called_from
  78137. while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}")
  78138. parent = File.dirname(root_path)
  78139. root_path = parent != root_path && parent
  78140. end
  78141. root = File.exist?("#{root_path}/#{flag}") ? root_path : default
  78142. raise "Could not find root path for #{self}" unless root
  78143. Pathname.new File.realpath root
  78144. end
  78145. def default_middleware_stack #:nodoc:
  78146. ActionDispatch::MiddlewareStack.new
  78147. end
  78148. def _all_autoload_once_paths #:nodoc:
  78149. config.autoload_once_paths
  78150. end
  78151. def _all_autoload_paths #:nodoc:
  78152. @_all_autoload_paths ||= (config.autoload_paths + config.autoload_once_paths).uniq
  78153. end
  78154. def _all_load_paths #:nodoc:
  78155. @_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
  78156. end
  78157. end
  78158. end
  78159. require 'open-uri'
  78160. require 'rbconfig'
  78161. module Rails
  78162. module Generators
  78163. module Actions
  78164. def initialize(*) # :nodoc:
  78165. super
  78166. @in_group = nil
  78167. end
  78168. # Adds an entry into Gemfile for the supplied gem.
  78169. #
  78170. # gem "rspec", group: :test
  78171. # gem "technoweenie-restful-authentication", lib: "restful-authentication", source: "http://gems.github.com/"
  78172. # gem "rails", "3.0", git: "git://github.com/rails/rails"
  78173. def gem(*args)
  78174. options = args.extract_options!
  78175. name, version = args
  78176. # Set the message to be shown in logs. Uses the git repo if one is given,
  78177. # otherwise use name (version).
  78178. parts, message = [ name.inspect ], name
  78179. if version ||= options.delete(:version)
  78180. parts << version.inspect
  78181. message << " (#{version})"
  78182. end
  78183. message = options[:git] if options[:git]
  78184. log :gemfile, message
  78185. options.each do |option, value|
  78186. parts << "#{option}: #{value.inspect}"
  78187. end
  78188. in_root do
  78189. str = "gem #{parts.join(", ")}"
  78190. str = " " + str if @in_group
  78191. str = "\n" + str
  78192. append_file "Gemfile", str, verbose: false
  78193. end
  78194. end
  78195. # Wraps gem entries inside a group.
  78196. #
  78197. # gem_group :development, :test do
  78198. # gem "rspec-rails"
  78199. # end
  78200. def gem_group(*names, &block)
  78201. name = names.map(&:inspect).join(", ")
  78202. log :gemfile, "group #{name}"
  78203. in_root do
  78204. append_file "Gemfile", "\ngroup #{name} do", force: true
  78205. @in_group = true
  78206. instance_eval(&block)
  78207. @in_group = false
  78208. append_file "Gemfile", "\nend\n", force: true
  78209. end
  78210. end
  78211. # Add the given source to Gemfile
  78212. #
  78213. # add_source "http://gems.github.com/"
  78214. def add_source(source, options={})
  78215. log :source, source
  78216. in_root do
  78217. prepend_file "Gemfile", "source #{source.inspect}\n", verbose: false
  78218. end
  78219. end
  78220. # Adds a line inside the Application class for config/application.rb.
  78221. #
  78222. # If options :env is specified, the line is appended to the corresponding
  78223. # file in config/environments.
  78224. #
  78225. # environment do
  78226. # "config.autoload_paths += %W(#{config.root}/extras)"
  78227. # end
  78228. #
  78229. # environment(nil, env: "development") do
  78230. # "config.autoload_paths += %W(#{config.root}/extras)"
  78231. # end
  78232. def environment(data=nil, options={}, &block)
  78233. sentinel = /class [a-z_:]+ < Rails::Application/i
  78234. env_file_sentinel = /::Application\.configure do/
  78235. data = block.call if !data && block_given?
  78236. in_root do
  78237. if options[:env].nil?
  78238. inject_into_file 'config/application.rb', "\n #{data}", after: sentinel, verbose: false
  78239. else
  78240. Array(options[:env]).each do |env|
  78241. inject_into_file "config/environments/#{env}.rb", "\n #{data}", after: env_file_sentinel, verbose: false
  78242. end
  78243. end
  78244. end
  78245. end
  78246. alias :application :environment
  78247. # Run a command in git.
  78248. #
  78249. # git :init
  78250. # git add: "this.file that.rb"
  78251. # git add: "onefile.rb", rm: "badfile.cxx"
  78252. def git(commands={})
  78253. if commands.is_a?(Symbol)
  78254. run "git #{commands}"
  78255. else
  78256. commands.each do |cmd, options|
  78257. run "git #{cmd} #{options}"
  78258. end
  78259. end
  78260. end
  78261. # Create a new file in the vendor/ directory. Code can be specified
  78262. # in a block or a data string can be given.
  78263. #
  78264. # vendor("sekrit.rb") do
  78265. # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--"
  78266. # "salt = '#{sekrit_salt}'"
  78267. # end
  78268. #
  78269. # vendor("foreign.rb", "# Foreign code is fun")
  78270. def vendor(filename, data=nil, &block)
  78271. log :vendor, filename
  78272. create_file("vendor/#{filename}", data, verbose: false, &block)
  78273. end
  78274. # Create a new file in the lib/ directory. Code can be specified
  78275. # in a block or a data string can be given.
  78276. #
  78277. # lib("crypto.rb") do
  78278. # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
  78279. # end
  78280. #
  78281. # lib("foreign.rb", "# Foreign code is fun")
  78282. def lib(filename, data=nil, &block)
  78283. log :lib, filename
  78284. create_file("lib/#{filename}", data, verbose: false, &block)
  78285. end
  78286. # Create a new Rakefile with the provided code (either in a block or a string).
  78287. #
  78288. # rakefile("bootstrap.rake") do
  78289. # project = ask("What is the UNIX name of your project?")
  78290. #
  78291. # <<-TASK
  78292. # namespace :#{project} do
  78293. # task :bootstrap do
  78294. # puts "I like boots!"
  78295. # end
  78296. # end
  78297. # TASK
  78298. # end
  78299. #
  78300. # rakefile('seed.rake', 'puts "Planting seeds"')
  78301. def rakefile(filename, data=nil, &block)
  78302. log :rakefile, filename
  78303. create_file("lib/tasks/#{filename}", data, verbose: false, &block)
  78304. end
  78305. # Create a new initializer with the provided code (either in a block or a string).
  78306. #
  78307. # initializer("globals.rb") do
  78308. # data = ""
  78309. #
  78310. # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do |const|
  78311. # data << "#{const} = :entp\n"
  78312. # end
  78313. #
  78314. # data
  78315. # end
  78316. #
  78317. # initializer("api.rb", "API_KEY = '123456'")
  78318. def initializer(filename, data=nil, &block)
  78319. log :initializer, filename
  78320. create_file("config/initializers/#{filename}", data, verbose: false, &block)
  78321. end
  78322. # Generate something using a generator from Rails or a plugin.
  78323. # The second parameter is the argument string that is passed to
  78324. # the generator or an Array that is joined.
  78325. #
  78326. # generate(:authenticated, "user session")
  78327. def generate(what, *args)
  78328. log :generate, what
  78329. argument = args.map {|arg| arg.to_s }.flatten.join(" ")
  78330. in_root { run_ruby_script("bin/rails generate #{what} #{argument}", verbose: false) }
  78331. end
  78332. # Runs the supplied rake task
  78333. #
  78334. # rake("db:migrate")
  78335. # rake("db:migrate", env: "production")
  78336. # rake("gems:install", sudo: true)
  78337. def rake(command, options={})
  78338. log :rake, command
  78339. env = options[:env] || ENV["RAILS_ENV"] || 'development'
  78340. sudo = options[:sudo] && RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ ? 'sudo ' : ''
  78341. in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", verbose: false) }
  78342. end
  78343. # Just run the capify command in root
  78344. #
  78345. # capify!
  78346. def capify!
  78347. log :capify, ""
  78348. in_root { run("#{extify(:capify)} .", verbose: false) }
  78349. end
  78350. # Make an entry in Rails routing file config/routes.rb
  78351. #
  78352. # route "root :to => 'welcome#index'"
  78353. def route(routing_code)
  78354. log :route, routing_code
  78355. sentinel = /\.routes\.draw do\s*$/
  78356. in_root do
  78357. inject_into_file 'config/routes.rb', "\n #{routing_code}", { after: sentinel, verbose: false }
  78358. end
  78359. end
  78360. # Reads the given file at the source root and prints it in the console.
  78361. #
  78362. # readme "README"
  78363. def readme(path)
  78364. log File.read(find_in_source_paths(path))
  78365. end
  78366. protected
  78367. # Define log for backwards compatibility. If just one argument is sent,
  78368. # invoke say, otherwise invoke say_status. Differently from say and
  78369. # similarly to say_status, this method respects the quiet? option given.
  78370. def log(*args)
  78371. if args.size == 1
  78372. say args.first.to_s unless options.quiet?
  78373. else
  78374. args << (self.behavior == :invoke ? :green : :red)
  78375. say_status(*args)
  78376. end
  78377. end
  78378. # Add an extension to the given name based on the platform.
  78379. def extify(name)
  78380. if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
  78381. "#{name}.bat"
  78382. else
  78383. name
  78384. end
  78385. end
  78386. end
  78387. end
  78388. end
  78389. require 'active_support/deprecation'
  78390. module Rails
  78391. module Generators
  78392. # ActiveModel is a class to be implemented by each ORM to allow Rails to
  78393. # generate customized controller code.
  78394. #
  78395. # The API has the same methods as ActiveRecord, but each method returns a
  78396. # string that matches the ORM API.
  78397. #
  78398. # For example:
  78399. #
  78400. # ActiveRecord::Generators::ActiveModel.find(Foo, "params[:id]")
  78401. # # => "Foo.find(params[:id])"
  78402. #
  78403. # DataMapper::Generators::ActiveModel.find(Foo, "params[:id]")
  78404. # # => "Foo.get(params[:id])"
  78405. #
  78406. # On initialization, the ActiveModel accepts the instance name that will
  78407. # receive the calls:
  78408. #
  78409. # builder = ActiveRecord::Generators::ActiveModel.new "@foo"
  78410. # builder.save # => "@foo.save"
  78411. #
  78412. # The only exception in ActiveModel for ActiveRecord is the use of self.build
  78413. # instead of self.new.
  78414. #
  78415. class ActiveModel
  78416. attr_reader :name
  78417. def initialize(name)
  78418. @name = name
  78419. end
  78420. # GET index
  78421. def self.all(klass)
  78422. "#{klass}.all"
  78423. end
  78424. # GET show
  78425. # GET edit
  78426. # PATCH/PUT update
  78427. # DELETE destroy
  78428. def self.find(klass, params=nil)
  78429. "#{klass}.find(#{params})"
  78430. end
  78431. # GET new
  78432. # POST create
  78433. def self.build(klass, params=nil)
  78434. if params
  78435. "#{klass}.new(#{params})"
  78436. else
  78437. "#{klass}.new"
  78438. end
  78439. end
  78440. # POST create
  78441. def save
  78442. "#{name}.save"
  78443. end
  78444. # PATCH/PUT update
  78445. def update(params=nil)
  78446. "#{name}.update(#{params})"
  78447. end
  78448. def update_attributes(*args) # :nodoc:
  78449. ActiveSupport::Deprecation.warn("Calling '@orm_instance.update_attributes' " \
  78450. "is deprecated, please use '@orm_instance.update' instead.")
  78451. update(*args)
  78452. end
  78453. # POST create
  78454. # PATCH/PUT update
  78455. def errors
  78456. "#{name}.errors"
  78457. end
  78458. # DELETE destroy
  78459. def destroy
  78460. "#{name}.destroy"
  78461. end
  78462. end
  78463. end
  78464. end
  78465. require 'digest/md5'
  78466. require 'securerandom'
  78467. require 'active_support/core_ext/string/strip'
  78468. require 'rails/version' unless defined?(Rails::VERSION)
  78469. require 'rbconfig'
  78470. require 'open-uri'
  78471. require 'uri'
  78472. module Rails
  78473. module Generators
  78474. class AppBase < Base # :nodoc:
  78475. DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver )
  78476. JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
  78477. DATABASES.concat(JDBC_DATABASES)
  78478. attr_accessor :rails_template
  78479. add_shebang_option!
  78480. argument :app_path, type: :string
  78481. def self.add_shared_options_for(name)
  78482. class_option :builder, type: :string, aliases: '-b',
  78483. desc: "Path to some #{name} builder (can be a filesystem path or URL)"
  78484. class_option :template, type: :string, aliases: '-m',
  78485. desc: "Path to some #{name} template (can be a filesystem path or URL)"
  78486. class_option :skip_gemfile, type: :boolean, default: false,
  78487. desc: "Don't create a Gemfile"
  78488. class_option :skip_bundle, type: :boolean, aliases: '-B', default: false,
  78489. desc: "Don't run bundle install"
  78490. class_option :skip_git, type: :boolean, aliases: '-G', default: false,
  78491. desc: 'Skip .gitignore file'
  78492. class_option :skip_keeps, type: :boolean, default: false,
  78493. desc: 'Skip source control .keep files'
  78494. class_option :skip_active_record, type: :boolean, aliases: '-O', default: false,
  78495. desc: 'Skip Active Record files'
  78496. class_option :skip_sprockets, type: :boolean, aliases: '-S', default: false,
  78497. desc: 'Skip Sprockets files'
  78498. class_option :database, type: :string, aliases: '-d', default: 'sqlite3',
  78499. desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
  78500. class_option :javascript, type: :string, aliases: '-j', default: 'jquery',
  78501. desc: 'Preconfigure for selected JavaScript library'
  78502. class_option :skip_javascript, type: :boolean, aliases: '-J', default: false,
  78503. desc: 'Skip JavaScript files'
  78504. class_option :dev, type: :boolean, default: false,
  78505. desc: "Setup the #{name} with Gemfile pointing to your Rails checkout"
  78506. class_option :edge, type: :boolean, default: false,
  78507. desc: "Setup the #{name} with Gemfile pointing to Rails repository"
  78508. class_option :skip_test_unit, type: :boolean, aliases: '-T', default: false,
  78509. desc: 'Skip Test::Unit files'
  78510. class_option :rc, type: :string, default: false,
  78511. desc: "Path to file containing extra configuration options for rails command"
  78512. class_option :no_rc, type: :boolean, default: false,
  78513. desc: 'Skip loading of extra configuration options from .railsrc file'
  78514. class_option :help, type: :boolean, aliases: '-h', group: :rails,
  78515. desc: 'Show this help message and quit'
  78516. end
  78517. def initialize(*args)
  78518. @original_wd = Dir.pwd
  78519. super
  78520. convert_database_option_for_jruby
  78521. end
  78522. protected
  78523. def builder
  78524. @builder ||= begin
  78525. if path = options[:builder]
  78526. if URI(path).is_a?(URI::HTTP)
  78527. contents = open(path, "Accept" => "application/x-thor-template") {|io| io.read }
  78528. else
  78529. contents = open(File.expand_path(path, @original_wd)) {|io| io.read }
  78530. end
  78531. prok = eval("proc { #{contents} }", TOPLEVEL_BINDING, path, 1)
  78532. instance_eval(&prok)
  78533. end
  78534. builder_class = get_builder_class
  78535. builder_class.send(:include, ActionMethods)
  78536. builder_class.new(self)
  78537. end
  78538. end
  78539. def build(meth, *args)
  78540. builder.send(meth, *args) if builder.respond_to?(meth)
  78541. end
  78542. def create_root
  78543. self.destination_root = File.expand_path(app_path, destination_root)
  78544. valid_const?
  78545. empty_directory '.'
  78546. set_default_accessors!
  78547. FileUtils.cd(destination_root) unless options[:pretend]
  78548. end
  78549. def apply_rails_template
  78550. apply rails_template if rails_template
  78551. rescue Thor::Error, LoadError, Errno::ENOENT => e
  78552. raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}"
  78553. end
  78554. def set_default_accessors!
  78555. self.rails_template = case options[:template]
  78556. when /^https?:\/\//
  78557. options[:template]
  78558. when String
  78559. File.expand_path(options[:template], Dir.pwd)
  78560. else
  78561. options[:template]
  78562. end
  78563. end
  78564. def database_gemfile_entry
  78565. options[:skip_active_record] ? "" : "gem '#{gem_for_database}'"
  78566. end
  78567. def include_all_railties?
  78568. !options[:skip_active_record] && !options[:skip_test_unit] && !options[:skip_sprockets]
  78569. end
  78570. def comment_if(value)
  78571. options[value] ? '# ' : ''
  78572. end
  78573. def rails_gemfile_entry
  78574. if options.dev?
  78575. <<-GEMFILE.strip_heredoc
  78576. gem 'rails', path: '#{Rails::Generators::RAILS_DEV_PATH}'
  78577. gem 'arel', github: 'rails/arel'
  78578. gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders'
  78579. GEMFILE
  78580. elsif options.edge?
  78581. <<-GEMFILE.strip_heredoc
  78582. gem 'rails', github: 'rails/rails'
  78583. gem 'arel', github: 'rails/arel'
  78584. gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders'
  78585. GEMFILE
  78586. else
  78587. <<-GEMFILE.strip_heredoc
  78588. # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
  78589. gem 'rails', '#{Rails::VERSION::STRING}'
  78590. GEMFILE
  78591. end
  78592. end
  78593. def gem_for_database
  78594. # %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql )
  78595. case options[:database]
  78596. when "oracle" then "ruby-oci8"
  78597. when "postgresql" then "pg"
  78598. when "frontbase" then "ruby-frontbase"
  78599. when "mysql" then "mysql2"
  78600. when "sqlserver" then "activerecord-sqlserver-adapter"
  78601. when "jdbcmysql" then "activerecord-jdbcmysql-adapter"
  78602. when "jdbcsqlite3" then "activerecord-jdbcsqlite3-adapter"
  78603. when "jdbcpostgresql" then "activerecord-jdbcpostgresql-adapter"
  78604. when "jdbc" then "activerecord-jdbc-adapter"
  78605. else options[:database]
  78606. end
  78607. end
  78608. def convert_database_option_for_jruby
  78609. if defined?(JRUBY_VERSION)
  78610. case options[:database]
  78611. when "oracle" then options[:database].replace "jdbc"
  78612. when "postgresql" then options[:database].replace "jdbcpostgresql"
  78613. when "mysql" then options[:database].replace "jdbcmysql"
  78614. when "sqlite3" then options[:database].replace "jdbcsqlite3"
  78615. end
  78616. end
  78617. end
  78618. def assets_gemfile_entry
  78619. return if options[:skip_sprockets]
  78620. gemfile = if options.dev? || options.edge?
  78621. <<-GEMFILE
  78622. # Gems used only for assets and not required
  78623. # in production environments by default.
  78624. group :assets do
  78625. gem 'sprockets-rails', github: 'rails/sprockets-rails'
  78626. gem 'sass-rails', github: 'rails/sass-rails'
  78627. gem 'coffee-rails', github: 'rails/coffee-rails'
  78628. # See https://github.com/sstephenson/execjs#readme for more supported runtimes
  78629. #{javascript_runtime_gemfile_entry}
  78630. gem 'uglifier', '>= 1.0.3'
  78631. end
  78632. GEMFILE
  78633. else
  78634. <<-GEMFILE
  78635. # Gems used only for assets and not required
  78636. # in production environments by default.
  78637. group :assets do
  78638. gem 'sprockets-rails', '~> 2.0.0.rc1'
  78639. gem 'sass-rails', '~> 4.0.0.beta'
  78640. gem 'coffee-rails', '~> 4.0.0.beta'
  78641. # See https://github.com/sstephenson/execjs#readme for more supported runtimes
  78642. #{javascript_runtime_gemfile_entry}
  78643. gem 'uglifier', '>= 1.0.3'
  78644. end
  78645. GEMFILE
  78646. end
  78647. gemfile.strip_heredoc.gsub(/^[ \t]*$/, '')
  78648. end
  78649. def javascript_gemfile_entry
  78650. unless options[:skip_javascript]
  78651. <<-GEMFILE.strip_heredoc
  78652. gem '#{options[:javascript]}-rails'
  78653. # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
  78654. gem 'turbolinks'
  78655. GEMFILE
  78656. end
  78657. end
  78658. def javascript_runtime_gemfile_entry
  78659. if defined?(JRUBY_VERSION)
  78660. "gem 'therubyrhino'\n"
  78661. else
  78662. "# gem 'therubyracer', platforms: :ruby\n"
  78663. end
  78664. end
  78665. def bundle_command(command)
  78666. say_status :run, "bundle #{command}"
  78667. # We are going to shell out rather than invoking Bundler::CLI.new(command)
  78668. # because `rails new` loads the Thor gem and on the other hand bundler uses
  78669. # its own vendored Thor, which could be a different version. Running both
  78670. # things in the same process is a recipe for a night with paracetamol.
  78671. #
  78672. # We use backticks and #print here instead of vanilla #system because it
  78673. # is easier to silence stdout in the existing test suite this way. The
  78674. # end-user gets the bundler commands called anyway, so no big deal.
  78675. #
  78676. # We unset temporary bundler variables to load proper bundler and Gemfile.
  78677. #
  78678. # Thanks to James Tucker for the Gem tricks involved in this call.
  78679. _bundle_command = Gem.bin_path('bundler', 'bundle')
  78680. require 'bundler'
  78681. Bundler.with_clean_env do
  78682. print `"#{Gem.ruby}" "#{_bundle_command}" #{command}`
  78683. end
  78684. end
  78685. def run_bundle
  78686. bundle_command('install') unless options[:skip_gemfile] || options[:skip_bundle] || options[:pretend]
  78687. end
  78688. def empty_directory_with_keep_file(destination, config = {})
  78689. empty_directory(destination, config)
  78690. keep_file(destination)
  78691. end
  78692. def keep_file(destination)
  78693. create_file("#{destination}/.keep") unless options[:skip_keeps]
  78694. end
  78695. end
  78696. end
  78697. end
  78698. begin
  78699. require 'thor/group'
  78700. rescue LoadError
  78701. puts "Thor is not available.\nIf you ran this command from a git checkout " \
  78702. "of Rails, please make sure thor is installed,\nand run this command " \
  78703. "as `ruby #{$0} #{(ARGV | ['--dev']).join(" ")}`"
  78704. exit
  78705. end
  78706. require 'rails/generators/actions'
  78707. module Rails
  78708. module Generators
  78709. class Error < Thor::Error # :nodoc:
  78710. end
  78711. class Base < Thor::Group
  78712. include Thor::Actions
  78713. include Rails::Generators::Actions
  78714. add_runtime_options!
  78715. strict_args_position!
  78716. # Returns the source root for this generator using default_source_root as default.
  78717. def self.source_root(path=nil)
  78718. @_source_root = path if path
  78719. @_source_root ||= default_source_root
  78720. end
  78721. # Tries to get the description from a USAGE file one folder above the source
  78722. # root otherwise uses a default description.
  78723. def self.desc(description=nil)
  78724. return super if description
  78725. @desc ||= if usage_path
  78726. ERB.new(File.read(usage_path)).result(binding)
  78727. else
  78728. "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator."
  78729. end
  78730. end
  78731. # Convenience method to get the namespace from the class name. It's the
  78732. # same as Thor default except that the Generator at the end of the class
  78733. # is removed.
  78734. def self.namespace(name=nil)
  78735. return super if name
  78736. @namespace ||= super.sub(/_generator$/, '').sub(/:generators:/, ':')
  78737. end
  78738. # Convenience method to hide this generator from the available ones when
  78739. # running rails generator command.
  78740. def self.hide!
  78741. Rails::Generators.hide_namespace self.namespace
  78742. end
  78743. # Invoke a generator based on the value supplied by the user to the
  78744. # given option named "name". A class option is created when this method
  78745. # is invoked and you can set a hash to customize it.
  78746. #
  78747. # ==== Examples
  78748. #
  78749. # module Rails::Generators
  78750. # class ControllerGenerator < Base
  78751. # hook_for :test_framework, aliases: "-t"
  78752. # end
  78753. # end
  78754. #
  78755. # The example above will create a test framework option and will invoke
  78756. # a generator based on the user supplied value.
  78757. #
  78758. # For example, if the user invoke the controller generator as:
  78759. #
  78760. # rails generate controller Account --test-framework=test_unit
  78761. #
  78762. # The controller generator will then try to invoke the following generators:
  78763. #
  78764. # "rails:test_unit", "test_unit:controller", "test_unit"
  78765. #
  78766. # Notice that "rails:generators:test_unit" could be loaded as well, what
  78767. # Rails looks for is the first and last parts of the namespace. This is what
  78768. # allows any test framework to hook into Rails as long as it provides any
  78769. # of the hooks above.
  78770. #
  78771. # ==== Options
  78772. #
  78773. # The first and last part used to find the generator to be invoked are
  78774. # guessed based on class invokes hook_for, as noticed in the example above.
  78775. # This can be customized with two options: :base and :as.
  78776. #
  78777. # Let's suppose you are creating a generator that needs to invoke the
  78778. # controller generator from test unit. Your first attempt is:
  78779. #
  78780. # class AwesomeGenerator < Rails::Generators::Base
  78781. # hook_for :test_framework
  78782. # end
  78783. #
  78784. # The lookup in this case for test_unit as input is:
  78785. #
  78786. # "test_unit:awesome", "test_unit"
  78787. #
  78788. # Which is not the desired lookup. You can change it by providing the
  78789. # :as option:
  78790. #
  78791. # class AwesomeGenerator < Rails::Generators::Base
  78792. # hook_for :test_framework, as: :controller
  78793. # end
  78794. #
  78795. # And now it will lookup at:
  78796. #
  78797. # "test_unit:controller", "test_unit"
  78798. #
  78799. # Similarly, if you want it to also lookup in the rails namespace, you just
  78800. # need to provide the :base value:
  78801. #
  78802. # class AwesomeGenerator < Rails::Generators::Base
  78803. # hook_for :test_framework, in: :rails, as: :controller
  78804. # end
  78805. #
  78806. # And the lookup is exactly the same as previously:
  78807. #
  78808. # "rails:test_unit", "test_unit:controller", "test_unit"
  78809. #
  78810. # ==== Switches
  78811. #
  78812. # All hooks come with switches for user interface. If you do not want
  78813. # to use any test framework, you can do:
  78814. #
  78815. # rails generate controller Account --skip-test-framework
  78816. #
  78817. # Or similarly:
  78818. #
  78819. # rails generate controller Account --no-test-framework
  78820. #
  78821. # ==== Boolean hooks
  78822. #
  78823. # In some cases, you may want to provide a boolean hook. For example, webrat
  78824. # developers might want to have webrat available on controller generator.
  78825. # This can be achieved as:
  78826. #
  78827. # Rails::Generators::ControllerGenerator.hook_for :webrat, type: :boolean
  78828. #
  78829. # Then, if you want webrat to be invoked, just supply:
  78830. #
  78831. # rails generate controller Account --webrat
  78832. #
  78833. # The hooks lookup is similar as above:
  78834. #
  78835. # "rails:generators:webrat", "webrat:generators:controller", "webrat"
  78836. #
  78837. # ==== Custom invocations
  78838. #
  78839. # You can also supply a block to hook_for to customize how the hook is
  78840. # going to be invoked. The block receives two arguments, an instance
  78841. # of the current class and the class to be invoked.
  78842. #
  78843. # For example, in the resource generator, the controller should be invoked
  78844. # with a pluralized class name. But by default it is invoked with the same
  78845. # name as the resource generator, which is singular. To change this, we
  78846. # can give a block to customize how the controller can be invoked.
  78847. #
  78848. # hook_for :resource_controller do |instance, controller|
  78849. # instance.invoke controller, [ instance.name.pluralize ]
  78850. # end
  78851. #
  78852. def self.hook_for(*names, &block)
  78853. options = names.extract_options!
  78854. in_base = options.delete(:in) || base_name
  78855. as_hook = options.delete(:as) || generator_name
  78856. names.each do |name|
  78857. defaults = if options[:type] == :boolean
  78858. { }
  78859. elsif [true, false].include?(default_value_for_option(name, options))
  78860. { banner: "" }
  78861. else
  78862. { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
  78863. end
  78864. unless class_options.key?(name)
  78865. class_option(name, defaults.merge!(options))
  78866. end
  78867. hooks[name] = [ in_base, as_hook ]
  78868. invoke_from_option(name, options, &block)
  78869. end
  78870. end
  78871. # Remove a previously added hook.
  78872. #
  78873. # remove_hook_for :orm
  78874. def self.remove_hook_for(*names)
  78875. remove_invocation(*names)
  78876. names.each do |name|
  78877. hooks.delete(name)
  78878. end
  78879. end
  78880. # Make class option aware of Rails::Generators.options and Rails::Generators.aliases.
  78881. def self.class_option(name, options={}) #:nodoc:
  78882. options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc)
  78883. options[:aliases] = default_aliases_for_option(name, options)
  78884. options[:default] = default_value_for_option(name, options)
  78885. super(name, options)
  78886. end
  78887. # Returns the default source root for a given generator. This is used internally
  78888. # by rails to set its generators source root. If you want to customize your source
  78889. # root, you should use source_root.
  78890. def self.default_source_root
  78891. return unless base_name && generator_name
  78892. return unless default_generator_root
  78893. path = File.join(default_generator_root, 'templates')
  78894. path if File.exists?(path)
  78895. end
  78896. # Returns the base root for a common set of generators. This is used to dynamically
  78897. # guess the default source root.
  78898. def self.base_root
  78899. File.dirname(__FILE__)
  78900. end
  78901. # Cache source root and add lib/generators/base/generator/templates to
  78902. # source paths.
  78903. def self.inherited(base) #:nodoc:
  78904. super
  78905. # Invoke source_root so the default_source_root is set.
  78906. base.source_root
  78907. if base.name && base.name !~ /Base$/
  78908. Rails::Generators.subclasses << base
  78909. Rails::Generators.templates_path.each do |path|
  78910. if base.name.include?('::')
  78911. base.source_paths << File.join(path, base.base_name, base.generator_name)
  78912. else
  78913. base.source_paths << File.join(path, base.generator_name)
  78914. end
  78915. end
  78916. end
  78917. end
  78918. protected
  78919. # Check whether the given class names are already taken by user
  78920. # application or Ruby on Rails.
  78921. def class_collisions(*class_names) #:nodoc:
  78922. return unless behavior == :invoke
  78923. class_names.flatten.each do |class_name|
  78924. class_name = class_name.to_s
  78925. next if class_name.strip.empty?
  78926. # Split the class from its module nesting
  78927. nesting = class_name.split('::')
  78928. last_name = nesting.pop
  78929. # Extract the last Module in the nesting
  78930. last = nesting.inject(Object) do |last_module, nest|
  78931. break unless last_module.const_defined?(nest, false)
  78932. last_module.const_get(nest)
  78933. end
  78934. if last && last.const_defined?(last_name.camelize, false)
  78935. raise Error, "The name '#{class_name}' is either already used in your application " <<
  78936. "or reserved by Ruby on Rails. Please choose an alternative and run " <<
  78937. "this generator again."
  78938. end
  78939. end
  78940. end
  78941. # Use Rails default banner.
  78942. def self.banner
  78943. "rails generate #{namespace.sub(/^rails:/,'')} #{self.arguments.map{ |a| a.usage }.join(' ')} [options]".gsub(/\s+/, ' ')
  78944. end
  78945. # Sets the base_name taking into account the current class namespace.
  78946. def self.base_name
  78947. @base_name ||= begin
  78948. if base = name.to_s.split('::').first
  78949. base.underscore
  78950. end
  78951. end
  78952. end
  78953. # Removes the namespaces and get the generator name. For example,
  78954. # Rails::Generators::ModelGenerator will return "model" as generator name.
  78955. def self.generator_name
  78956. @generator_name ||= begin
  78957. if generator = name.to_s.split('::').last
  78958. generator.sub!(/Generator$/, '')
  78959. generator.underscore
  78960. end
  78961. end
  78962. end
  78963. # Return the default value for the option name given doing a lookup in
  78964. # Rails::Generators.options.
  78965. def self.default_value_for_option(name, options)
  78966. default_for_option(Rails::Generators.options, name, options, options[:default])
  78967. end
  78968. # Return default aliases for the option name given doing a lookup in
  78969. # Rails::Generators.aliases.
  78970. def self.default_aliases_for_option(name, options)
  78971. default_for_option(Rails::Generators.aliases, name, options, options[:aliases])
  78972. end
  78973. # Return default for the option name given doing a lookup in config.
  78974. def self.default_for_option(config, name, options, default)
  78975. if generator_name and c = config[generator_name.to_sym] and c.key?(name)
  78976. c[name]
  78977. elsif base_name and c = config[base_name.to_sym] and c.key?(name)
  78978. c[name]
  78979. elsif config[:rails].key?(name)
  78980. config[:rails][name]
  78981. else
  78982. default
  78983. end
  78984. end
  78985. # Keep hooks configuration that are used on prepare_for_invocation.
  78986. def self.hooks #:nodoc:
  78987. @hooks ||= from_superclass(:hooks, {})
  78988. end
  78989. # Prepare class invocation to search on Rails namespace if a previous
  78990. # added hook is being used.
  78991. def self.prepare_for_invocation(name, value) #:nodoc:
  78992. return super unless value.is_a?(String) || value.is_a?(Symbol)
  78993. if value && constants = self.hooks[name]
  78994. value = name if TrueClass === value
  78995. Rails::Generators.find_by_namespace(value, *constants)
  78996. elsif klass = Rails::Generators.find_by_namespace(value)
  78997. klass
  78998. else
  78999. super
  79000. end
  79001. end
  79002. # Small macro to add ruby as an option to the generator with proper
  79003. # default value plus an instance helper method called shebang.
  79004. def self.add_shebang_option!
  79005. class_option :ruby, type: :string, aliases: "-r", default: Thor::Util.ruby_command,
  79006. desc: "Path to the Ruby binary of your choice", banner: "PATH"
  79007. no_tasks {
  79008. define_method :shebang do
  79009. @shebang ||= begin
  79010. command = if options[:ruby] == Thor::Util.ruby_command
  79011. "/usr/bin/env #{File.basename(Thor::Util.ruby_command)}"
  79012. else
  79013. options[:ruby]
  79014. end
  79015. "#!#{command}"
  79016. end
  79017. end
  79018. }
  79019. end
  79020. def self.usage_path
  79021. paths = [
  79022. source_root && File.expand_path("../USAGE", source_root),
  79023. default_generator_root && File.join(default_generator_root, "USAGE")
  79024. ]
  79025. paths.compact.detect { |path| File.exists? path }
  79026. end
  79027. def self.default_generator_root
  79028. path = File.expand_path(File.join(base_name, generator_name), base_root)
  79029. path if File.exists?(path)
  79030. end
  79031. end
  79032. end
  79033. end
  79034. require "rails/generators/named_base"
  79035. module Css # :nodoc:
  79036. module Generators # :nodoc:
  79037. class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
  79038. source_root File.expand_path("../templates", __FILE__)
  79039. def copy_stylesheet
  79040. copy_file "stylesheet.css", File.join('app/assets/stylesheets', class_path, "#{file_name}.css")
  79041. end
  79042. end
  79043. end
  79044. end
  79045. require "rails/generators/named_base"
  79046. module Css # :nodoc:
  79047. module Generators # :nodoc:
  79048. class ScaffoldGenerator < Rails::Generators::NamedBase # :nodoc:
  79049. # In order to allow the Sass generators to pick up the default Rails CSS and
  79050. # transform it, we leave it in a standard location for the CSS stylesheet
  79051. # generators to handle. For the simple, default case, just copy it over.
  79052. def copy_stylesheet
  79053. dir = Rails::Generators::ScaffoldGenerator.source_root
  79054. file = File.join(dir, "scaffold.css")
  79055. create_file "app/assets/stylesheets/scaffold.css", File.read(file)
  79056. end
  79057. end
  79058. end
  79059. end
  79060. require 'rails/generators/erb'
  79061. module Erb # :nodoc:
  79062. module Generators # :nodoc:
  79063. class ControllerGenerator < Base # :nodoc:
  79064. argument :actions, type: :array, default: [], banner: "action action"
  79065. def copy_view_files
  79066. base_path = File.join("app/views", class_path, file_name)
  79067. empty_directory base_path
  79068. actions.each do |action|
  79069. @action = action
  79070. @path = File.join(base_path, filename_with_extensions(action))
  79071. template filename_with_extensions(:view), @path
  79072. end
  79073. end
  79074. end
  79075. end
  79076. end
  79077. require 'rails/generators/erb/controller/controller_generator'
  79078. module Erb # :nodoc:
  79079. module Generators # :nodoc:
  79080. class MailerGenerator < ControllerGenerator # :nodoc:
  79081. protected
  79082. def format
  79083. :text
  79084. end
  79085. end
  79086. end
  79087. end
  79088. require 'rails/generators/erb'
  79089. require 'rails/generators/resource_helpers'
  79090. module Erb # :nodoc:
  79091. module Generators # :nodoc:
  79092. class ScaffoldGenerator < Base # :nodoc:
  79093. include Rails::Generators::ResourceHelpers
  79094. argument :attributes, type: :array, default: [], banner: "field:type field:type"
  79095. def create_root_folder
  79096. empty_directory File.join("app/views", controller_file_path)
  79097. end
  79098. def copy_view_files
  79099. available_views.each do |view|
  79100. filename = filename_with_extensions(view)
  79101. template filename, File.join("app/views", controller_file_path, filename)
  79102. end
  79103. end
  79104. protected
  79105. def available_views
  79106. %w(index edit show new _form)
  79107. end
  79108. end
  79109. end
  79110. end
  79111. require 'rails/generators/named_base'
  79112. module Erb # :nodoc:
  79113. module Generators # :nodoc:
  79114. class Base < Rails::Generators::NamedBase #:nodoc:
  79115. protected
  79116. def format
  79117. :html
  79118. end
  79119. def handler
  79120. :erb
  79121. end
  79122. def filename_with_extensions(name)
  79123. [name, format, handler].compact.join(".")
  79124. end
  79125. end
  79126. end
  79127. end
  79128. require 'active_support/time'
  79129. module Rails
  79130. module Generators
  79131. class GeneratedAttribute # :nodoc:
  79132. INDEX_OPTIONS = %w(index uniq)
  79133. UNIQ_INDEX_OPTIONS = %w(uniq)
  79134. attr_accessor :name, :type
  79135. attr_reader :attr_options
  79136. attr_writer :index_name
  79137. class << self
  79138. def parse(column_definition)
  79139. name, type, has_index = column_definition.split(':')
  79140. # if user provided "name:index" instead of "name:string:index"
  79141. # type should be set blank so GeneratedAttribute's constructor
  79142. # could set it to :string
  79143. has_index, type = type, nil if INDEX_OPTIONS.include?(type)
  79144. type, attr_options = *parse_type_and_options(type)
  79145. type = type.to_sym if type
  79146. if type && reference?(type)
  79147. references_index = UNIQ_INDEX_OPTIONS.include?(has_index) ? { unique: true } : true
  79148. attr_options[:index] = references_index
  79149. end
  79150. new(name, type, has_index, attr_options)
  79151. end
  79152. def reference?(type)
  79153. [:references, :belongs_to].include? type
  79154. end
  79155. private
  79156. # parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
  79157. # when declaring options curly brackets should be used
  79158. def parse_type_and_options(type)
  79159. case type
  79160. when /(string|text|binary|integer)\{(\d+)\}/
  79161. return $1, limit: $2.to_i
  79162. when /decimal\{(\d+)[,.-](\d+)\}/
  79163. return :decimal, precision: $1.to_i, scale: $2.to_i
  79164. when /(references|belongs_to)\{polymorphic\}/
  79165. return $1, polymorphic: true
  79166. else
  79167. return type, {}
  79168. end
  79169. end
  79170. end
  79171. def initialize(name, type=nil, index_type=false, attr_options={})
  79172. @name = name
  79173. @type = type || :string
  79174. @has_index = INDEX_OPTIONS.include?(index_type)
  79175. @has_uniq_index = UNIQ_INDEX_OPTIONS.include?(index_type)
  79176. @attr_options = attr_options
  79177. end
  79178. def field_type
  79179. @field_type ||= case type
  79180. when :integer then :number_field
  79181. when :float, :decimal then :text_field
  79182. when :time then :time_select
  79183. when :datetime, :timestamp then :datetime_select
  79184. when :date then :date_select
  79185. when :text then :text_area
  79186. when :boolean then :check_box
  79187. else
  79188. :text_field
  79189. end
  79190. end
  79191. def default
  79192. @default ||= case type
  79193. when :integer then 1
  79194. when :float then 1.5
  79195. when :decimal then "9.99"
  79196. when :datetime, :timestamp, :time then Time.now.to_s(:db)
  79197. when :date then Date.today.to_s(:db)
  79198. when :string then name == "type" ? "" : "MyString"
  79199. when :text then "MyText"
  79200. when :boolean then false
  79201. when :references, :belongs_to then nil
  79202. else
  79203. ""
  79204. end
  79205. end
  79206. def plural_name
  79207. name.sub(/_id$/, '').pluralize
  79208. end
  79209. def human_name
  79210. name.humanize
  79211. end
  79212. def index_name
  79213. @index_name ||= if polymorphic?
  79214. %w(id type).map { |t| "#{name}_#{t}" }
  79215. else
  79216. column_name
  79217. end
  79218. end
  79219. def column_name
  79220. @column_name ||= reference? ? "#{name}_id" : name
  79221. end
  79222. def foreign_key?
  79223. !!(name =~ /_id$/)
  79224. end
  79225. def reference?
  79226. self.class.reference?(type)
  79227. end
  79228. def polymorphic?
  79229. self.attr_options.has_key?(:polymorphic)
  79230. end
  79231. def has_index?
  79232. @has_index
  79233. end
  79234. def has_uniq_index?
  79235. @has_uniq_index
  79236. end
  79237. def inject_options
  79238. "".tap { |s| @attr_options.each { |k,v| s << ", #{k}: #{v.inspect}" } }
  79239. end
  79240. def inject_index_options
  79241. has_uniq_index? ? ", unique: true" : ""
  79242. end
  79243. end
  79244. end
  79245. end
  79246. require "rails/generators/named_base"
  79247. module Js # :nodoc:
  79248. module Generators # :nodoc:
  79249. class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
  79250. source_root File.expand_path("../templates", __FILE__)
  79251. def copy_javascript
  79252. copy_file "javascript.js", File.join('app/assets/javascripts', class_path, "#{file_name}.js")
  79253. end
  79254. end
  79255. end
  79256. end
  79257. module Rails
  79258. module Generators
  79259. # Holds common methods for migrations. It assumes that migrations has the
  79260. # [0-9]*_name format and can be used by another frameworks (like Sequel)
  79261. # just by implementing the next migration version method.
  79262. module Migration
  79263. attr_reader :migration_number, :migration_file_name, :migration_class_name
  79264. def self.included(base) #:nodoc:
  79265. base.extend ClassMethods
  79266. end
  79267. module ClassMethods
  79268. def migration_lookup_at(dirname) #:nodoc:
  79269. Dir.glob("#{dirname}/[0-9]*_*.rb")
  79270. end
  79271. def migration_exists?(dirname, file_name) #:nodoc:
  79272. migration_lookup_at(dirname).grep(/\d+_#{file_name}.rb$/).first
  79273. end
  79274. def current_migration_number(dirname) #:nodoc:
  79275. migration_lookup_at(dirname).collect do |file|
  79276. File.basename(file).split("_").first.to_i
  79277. end.max.to_i
  79278. end
  79279. def next_migration_number(dirname) #:nodoc:
  79280. raise NotImplementedError
  79281. end
  79282. end
  79283. # Creates a migration template at the given destination. The difference
  79284. # to the default template method is that the migration version is appended
  79285. # to the destination file name.
  79286. #
  79287. # The migration version, migration file name, migration class name are
  79288. # available as instance variables in the template to be rendered.
  79289. #
  79290. # migration_template "migration.rb", "db/migrate/add_foo_to_bar.rb"
  79291. def migration_template(source, destination=nil, config={})
  79292. destination = File.expand_path(destination || source, self.destination_root)
  79293. migration_dir = File.dirname(destination)
  79294. @migration_number = self.class.next_migration_number(migration_dir)
  79295. @migration_file_name = File.basename(destination).sub(/\.rb$/, '')
  79296. @migration_class_name = @migration_file_name.camelize
  79297. destination = self.class.migration_exists?(migration_dir, @migration_file_name)
  79298. if !(destination && options[:skip]) && behavior == :invoke
  79299. if destination && options.force?
  79300. remove_file(destination)
  79301. elsif destination
  79302. raise Error, "Another migration is already named #{@migration_file_name}: #{destination}. Use --force to remove the old migration file and replace it."
  79303. end
  79304. destination = File.join(migration_dir, "#{@migration_number}_#{@migration_file_name}.rb")
  79305. end
  79306. template(source, destination, config)
  79307. end
  79308. end
  79309. end
  79310. end
  79311. require 'active_support/core_ext/module/introspection'
  79312. require 'rails/generators/base'
  79313. require 'rails/generators/generated_attribute'
  79314. module Rails
  79315. module Generators
  79316. class NamedBase < Base
  79317. argument :name, type: :string
  79318. class_option :skip_namespace, type: :boolean, default: false,
  79319. desc: "Skip namespace (affects only isolated applications)"
  79320. def initialize(args, *options) #:nodoc:
  79321. @inside_template = nil
  79322. # Unfreeze name in case it's given as a frozen string
  79323. args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen?
  79324. super
  79325. assign_names!(self.name)
  79326. parse_attributes! if respond_to?(:attributes)
  79327. end
  79328. no_tasks do
  79329. def template(source, *args, &block)
  79330. inside_template do
  79331. super
  79332. end
  79333. end
  79334. end
  79335. protected
  79336. attr_reader :file_name
  79337. alias :singular_name :file_name
  79338. # Wrap block with namespace of current application
  79339. # if namespace exists and is not skipped
  79340. def module_namespacing(&block)
  79341. content = capture(&block)
  79342. content = wrap_with_namespace(content) if namespaced?
  79343. concat(content)
  79344. end
  79345. def indent(content, multiplier = 2)
  79346. spaces = " " * multiplier
  79347. content = content.each_line.map {|line| line.blank? ? line : "#{spaces}#{line}" }.join
  79348. end
  79349. def wrap_with_namespace(content)
  79350. content = indent(content).chomp
  79351. "module #{namespace.name}\n#{content}\nend\n"
  79352. end
  79353. def inside_template
  79354. @inside_template = true
  79355. yield
  79356. ensure
  79357. @inside_template = false
  79358. end
  79359. def inside_template?
  79360. @inside_template
  79361. end
  79362. def namespace
  79363. Rails::Generators.namespace
  79364. end
  79365. def namespaced?
  79366. !options[:skip_namespace] && namespace
  79367. end
  79368. def file_path
  79369. @file_path ||= (class_path + [file_name]).join('/')
  79370. end
  79371. def class_path
  79372. inside_template? || !namespaced? ? regular_class_path : namespaced_class_path
  79373. end
  79374. def regular_class_path
  79375. @class_path
  79376. end
  79377. def namespaced_file_path
  79378. @namespaced_file_path ||= namespaced_class_path.join("/")
  79379. end
  79380. def namespaced_class_path
  79381. @namespaced_class_path ||= [namespaced_path] + @class_path
  79382. end
  79383. def namespaced_path
  79384. @namespaced_path ||= namespace.name.split("::").map {|m| m.underscore }[0]
  79385. end
  79386. def class_name
  79387. (class_path + [file_name]).map!{ |m| m.camelize }.join('::')
  79388. end
  79389. def human_name
  79390. @human_name ||= singular_name.humanize
  79391. end
  79392. def plural_name
  79393. @plural_name ||= singular_name.pluralize
  79394. end
  79395. def i18n_scope
  79396. @i18n_scope ||= file_path.tr('/', '.')
  79397. end
  79398. def table_name
  79399. @table_name ||= begin
  79400. base = pluralize_table_names? ? plural_name : singular_name
  79401. (class_path + [base]).join('_')
  79402. end
  79403. end
  79404. def uncountable?
  79405. singular_name == plural_name
  79406. end
  79407. def index_helper
  79408. uncountable? ? "#{plural_table_name}_index" : plural_table_name
  79409. end
  79410. def singular_table_name
  79411. @singular_table_name ||= (pluralize_table_names? ? table_name.singularize : table_name)
  79412. end
  79413. def plural_table_name
  79414. @plural_table_name ||= (pluralize_table_names? ? table_name : table_name.pluralize)
  79415. end
  79416. def plural_file_name
  79417. @plural_file_name ||= file_name.pluralize
  79418. end
  79419. def route_url
  79420. @route_url ||= class_path.collect {|dname| "/" + dname }.join + "/" + plural_file_name
  79421. end
  79422. # Tries to retrieve the application name or simple return application.
  79423. def application_name
  79424. if defined?(Rails) && Rails.application
  79425. Rails.application.class.name.split('::').first.underscore
  79426. else
  79427. "application"
  79428. end
  79429. end
  79430. def assign_names!(name) #:nodoc:
  79431. @class_path = name.include?('/') ? name.split('/') : name.split('::')
  79432. @class_path.map! { |m| m.underscore }
  79433. @file_name = @class_path.pop
  79434. end
  79435. # Convert attributes array into GeneratedAttribute objects.
  79436. def parse_attributes! #:nodoc:
  79437. self.attributes = (attributes || []).map do |attr|
  79438. Rails::Generators::GeneratedAttribute.parse(attr)
  79439. end
  79440. end
  79441. def attributes_names
  79442. @attributes_names ||= attributes.each_with_object([]) do |a, names|
  79443. names << a.column_name
  79444. names << "#{a.name}_type" if a.polymorphic?
  79445. end
  79446. end
  79447. def pluralize_table_names?
  79448. !defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names
  79449. end
  79450. # Add a class collisions name to be checked on class initialization. You
  79451. # can supply a hash with a :prefix or :suffix to be tested.
  79452. #
  79453. # ==== Examples
  79454. #
  79455. # check_class_collision suffix: "Decorator"
  79456. #
  79457. # If the generator is invoked with class name Admin, it will check for
  79458. # the presence of "AdminDecorator".
  79459. #
  79460. def self.check_class_collision(options={})
  79461. define_method :check_class_collision do
  79462. name = if self.respond_to?(:controller_class_name) # for ScaffoldBase
  79463. controller_class_name
  79464. else
  79465. class_name
  79466. end
  79467. class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}"
  79468. end
  79469. end
  79470. end
  79471. end
  79472. end
  79473. require 'rails/generators/app_base'
  79474. module Rails
  79475. module ActionMethods # :nodoc:
  79476. attr_reader :options
  79477. def initialize(generator)
  79478. @generator = generator
  79479. @options = generator.options
  79480. end
  79481. private
  79482. %w(template copy_file directory empty_directory inside
  79483. empty_directory_with_keep_file create_file chmod shebang).each do |method|
  79484. class_eval <<-RUBY, __FILE__, __LINE__ + 1
  79485. def #{method}(*args, &block)
  79486. @generator.send(:#{method}, *args, &block)
  79487. end
  79488. RUBY
  79489. end
  79490. # TODO: Remove once this is fully in place
  79491. def method_missing(meth, *args, &block)
  79492. @generator.send(meth, *args, &block)
  79493. end
  79494. end
  79495. # The application builder allows you to override elements of the application
  79496. # generator without being forced to reverse the operations of the default
  79497. # generator.
  79498. #
  79499. # This allows you to override entire operations, like the creation of the
  79500. # Gemfile, README, or JavaScript files, without needing to know exactly
  79501. # what those operations do so you can create another template action.
  79502. class AppBuilder
  79503. def rakefile
  79504. template "Rakefile"
  79505. end
  79506. def readme
  79507. copy_file "README.rdoc", "README.rdoc"
  79508. end
  79509. def gemfile
  79510. template "Gemfile"
  79511. end
  79512. def configru
  79513. template "config.ru"
  79514. end
  79515. def gitignore
  79516. copy_file "gitignore", ".gitignore"
  79517. end
  79518. def app
  79519. directory 'app'
  79520. keep_file 'app/mailers'
  79521. keep_file 'app/models'
  79522. keep_file 'app/controllers/concerns'
  79523. keep_file 'app/models/concerns'
  79524. end
  79525. def bin
  79526. directory "bin" do |content|
  79527. "#{shebang}\n" + content
  79528. end
  79529. chmod "bin", 0755, verbose: false
  79530. end
  79531. def config
  79532. empty_directory "config"
  79533. inside "config" do
  79534. template "routes.rb"
  79535. template "application.rb"
  79536. template "environment.rb"
  79537. directory "environments"
  79538. directory "initializers"
  79539. directory "locales"
  79540. end
  79541. end
  79542. def database_yml
  79543. template "config/databases/#{options[:database]}.yml", "config/database.yml"
  79544. end
  79545. def db
  79546. directory "db"
  79547. end
  79548. def lib
  79549. empty_directory 'lib'
  79550. empty_directory_with_keep_file 'lib/tasks'
  79551. empty_directory_with_keep_file 'lib/assets'
  79552. end
  79553. def log
  79554. empty_directory_with_keep_file 'log'
  79555. end
  79556. def public_directory
  79557. directory "public", "public", recursive: false
  79558. end
  79559. def test
  79560. empty_directory_with_keep_file 'test/fixtures'
  79561. empty_directory_with_keep_file 'test/controllers'
  79562. empty_directory_with_keep_file 'test/mailers'
  79563. empty_directory_with_keep_file 'test/models'
  79564. empty_directory_with_keep_file 'test/helpers'
  79565. empty_directory_with_keep_file 'test/integration'
  79566. template 'test/test_helper.rb'
  79567. end
  79568. def tmp
  79569. empty_directory "tmp/cache"
  79570. empty_directory "tmp/cache/assets"
  79571. end
  79572. def vendor
  79573. vendor_javascripts
  79574. vendor_stylesheets
  79575. end
  79576. def vendor_javascripts
  79577. empty_directory_with_keep_file 'vendor/assets/javascripts'
  79578. end
  79579. def vendor_stylesheets
  79580. empty_directory_with_keep_file 'vendor/assets/stylesheets'
  79581. end
  79582. end
  79583. module Generators
  79584. # We need to store the RAILS_DEV_PATH in a constant, otherwise the path
  79585. # can change in Ruby 1.8.7 when we FileUtils.cd.
  79586. RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__))
  79587. RESERVED_NAMES = %w[application destroy plugin runner test]
  79588. class AppGenerator < AppBase # :nodoc:
  79589. add_shared_options_for "application"
  79590. # Add bin/rails options
  79591. class_option :version, type: :boolean, aliases: "-v", group: :rails,
  79592. desc: "Show Rails version number and quit"
  79593. def initialize(*args)
  79594. raise Error, "Options should be given after the application name. For details run: rails --help" if args[0].blank?
  79595. super
  79596. if !options[:skip_active_record] && !DATABASES.include?(options[:database])
  79597. raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
  79598. end
  79599. end
  79600. public_task :create_root
  79601. def create_root_files
  79602. build(:readme)
  79603. build(:rakefile)
  79604. build(:configru)
  79605. build(:gitignore) unless options[:skip_git]
  79606. build(:gemfile) unless options[:skip_gemfile]
  79607. end
  79608. def create_app_files
  79609. build(:app)
  79610. end
  79611. def create_bin_files
  79612. build(:bin)
  79613. end
  79614. def create_config_files
  79615. build(:config)
  79616. end
  79617. def create_boot_file
  79618. template "config/boot.rb"
  79619. end
  79620. def create_active_record_files
  79621. return if options[:skip_active_record]
  79622. build(:database_yml)
  79623. end
  79624. def create_db_files
  79625. build(:db)
  79626. end
  79627. def create_lib_files
  79628. build(:lib)
  79629. end
  79630. def create_log_files
  79631. build(:log)
  79632. end
  79633. def create_public_files
  79634. build(:public_directory)
  79635. end
  79636. def create_test_files
  79637. build(:test) unless options[:skip_test_unit]
  79638. end
  79639. def create_tmp_files
  79640. build(:tmp)
  79641. end
  79642. def create_vendor_files
  79643. build(:vendor)
  79644. end
  79645. def finish_template
  79646. build(:leftovers)
  79647. end
  79648. public_task :apply_rails_template, :run_bundle
  79649. protected
  79650. def self.banner
  79651. "rails new #{self.arguments.map(&:usage).join(' ')} [options]"
  79652. end
  79653. # Define file as an alias to create_file for backwards compatibility.
  79654. def file(*args, &block)
  79655. create_file(*args, &block)
  79656. end
  79657. def app_name
  79658. @app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)).tr(".", "_")
  79659. end
  79660. def defined_app_name
  79661. defined_app_const_base.underscore
  79662. end
  79663. def defined_app_const_base
  79664. Rails.respond_to?(:application) && defined?(Rails::Application) &&
  79665. Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "")
  79666. end
  79667. alias :defined_app_const_base? :defined_app_const_base
  79668. def app_const_base
  79669. @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, '_').squeeze('_').camelize
  79670. end
  79671. alias :camelized :app_const_base
  79672. def app_const
  79673. @app_const ||= "#{app_const_base}::Application"
  79674. end
  79675. def valid_const?
  79676. if app_const =~ /^\d/
  79677. raise Error, "Invalid application name #{app_name}. Please give a name which does not start with numbers."
  79678. elsif RESERVED_NAMES.include?(app_name)
  79679. raise Error, "Invalid application name #{app_name}. Please give a name which does not match one of the reserved rails words."
  79680. elsif Object.const_defined?(app_const_base)
  79681. raise Error, "Invalid application name #{app_name}, constant #{app_const_base} is already in use. Please choose another application name."
  79682. end
  79683. end
  79684. def app_secret
  79685. SecureRandom.hex(64)
  79686. end
  79687. def mysql_socket
  79688. @mysql_socket ||= [
  79689. "/tmp/mysql.sock", # default
  79690. "/var/run/mysqld/mysqld.sock", # debian/gentoo
  79691. "/var/tmp/mysql.sock", # freebsd
  79692. "/var/lib/mysql/mysql.sock", # fedora
  79693. "/opt/local/lib/mysql/mysql.sock", # fedora
  79694. "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
  79695. "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
  79696. "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
  79697. "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
  79698. ].find { |f| File.exist?(f) } unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
  79699. end
  79700. def get_builder_class
  79701. defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder
  79702. end
  79703. end
  79704. end
  79705. end
  79706. module ApplicationHelper
  79707. end
  79708. require File.expand_path('../boot', __FILE__)
  79709. <% if include_all_railties? -%>
  79710. require 'rails/all'
  79711. <% else -%>
  79712. # Pick the frameworks you want:
  79713. <%= comment_if :skip_active_record %>require "active_record/railtie"
  79714. require "action_controller/railtie"
  79715. require "action_mailer/railtie"
  79716. <%= comment_if :skip_sprockets %>require "sprockets/railtie"
  79717. <%= comment_if :skip_test_unit %>require "rails/test_unit/railtie"
  79718. <% end -%>
  79719. # Assets should be precompiled for production (so we don't need the gems loaded then)
  79720. Bundler.require(*Rails.groups(assets: %w(development test)))
  79721. module <%= app_const_base %>
  79722. class Application < Rails::Application
  79723. # Settings in config/environments/* take precedence over those specified here.
  79724. # Application configuration should go into files in config/initializers
  79725. # -- all .rb files in that directory are automatically loaded.
  79726. # Custom directories with classes and modules you want to be autoloadable.
  79727. # config.autoload_paths += %W(#{config.root}/extras)
  79728. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
  79729. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
  79730. # config.time_zone = 'Central Time (US & Canada)'
  79731. # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
  79732. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
  79733. # config.i18n.default_locale = :de
  79734. <% if options.skip_sprockets? -%>
  79735. # Disable the asset pipeline.
  79736. config.assets.enabled = false
  79737. <% end -%>
  79738. end
  79739. end
  79740. # Set up gems listed in the Gemfile.
  79741. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
  79742. require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
  79743. # Load the rails application.
  79744. require File.expand_path('../application', __FILE__)
  79745. # Initialize the rails application.
  79746. <%= app_const %>.initialize!
  79747. # Be sure to restart your server when you modify this file.
  79748. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
  79749. # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
  79750. # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
  79751. # Rails.backtrace_cleaner.remove_silencers!
  79752. # Be sure to restart your server when you modify this file.
  79753. # Configure sensitive parameters which will be filtered from the log file.
  79754. Rails.application.config.filter_parameters += [:password]
  79755. # Be sure to restart your server when you modify this file.
  79756. # Add new inflection rules using the following format. Inflections
  79757. # are locale specific, and you may define rules for as many different
  79758. # locales as you wish. All of these examples are active by default:
  79759. # ActiveSupport::Inflector.inflections(:en) do |inflect|
  79760. # inflect.plural /^(ox)$/i, '\1en'
  79761. # inflect.singular /^(ox)en/i, '\1'
  79762. # inflect.irregular 'person', 'people'
  79763. # inflect.uncountable %w( fish sheep )
  79764. # end
  79765. # These inflection rules are supported but not enabled by default:
  79766. # ActiveSupport::Inflector.inflections(:en) do |inflect|
  79767. # inflect.acronym 'RESTful'
  79768. # end
  79769. # Be sure to restart your server when you modify this file.
  79770. # Add new mime types for use in respond_to blocks:
  79771. # Mime::Type.register "text/richtext", :rtf
  79772. # Mime::Type.register_alias "text/html", :iphone
  79773. <%= app_const %>.routes.draw do
  79774. # The priority is based upon order of creation: first created -> highest priority.
  79775. # See how all your routes lay out with "rake routes".
  79776. # You can have the root of your site routed with "root"
  79777. # root to: 'welcome#index'
  79778. # Example of regular route:
  79779. # get 'products/:id' => 'catalog#view'
  79780. # Example of named route that can be invoked with purchase_url(id: product.id)
  79781. # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
  79782. # Example resource route (maps HTTP verbs to controller actions automatically):
  79783. # resources :products
  79784. # Example resource route with options:
  79785. # resources :products do
  79786. # member do
  79787. # get 'short'
  79788. # post 'toggle'
  79789. # end
  79790. #
  79791. # collection do
  79792. # get 'sold'
  79793. # end
  79794. # end
  79795. # Example resource route with sub-resources:
  79796. # resources :products do
  79797. # resources :comments, :sales
  79798. # resource :seller
  79799. # end
  79800. # Example resource route with more complex sub-resources:
  79801. # resources :products do
  79802. # resources :comments
  79803. # resources :sales do
  79804. # get 'recent', on: :collection
  79805. # end
  79806. # end
  79807. # Example resource route within a namespace:
  79808. # namespace :admin do
  79809. # # Directs /admin/products/* to Admin::ProductsController
  79810. # # (app/controllers/admin/products_controller.rb)
  79811. # resources :products
  79812. # end
  79813. end
  79814. module Rails
  79815. module Generators
  79816. class AssetsGenerator < NamedBase # :nodoc:
  79817. class_option :javascripts, type: :boolean, desc: "Generate JavaScripts"
  79818. class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
  79819. class_option :javascript_engine, desc: "Engine for JavaScripts"
  79820. class_option :stylesheet_engine, desc: "Engine for Stylesheets"
  79821. protected
  79822. def asset_name
  79823. file_name
  79824. end
  79825. hook_for :javascript_engine do |javascript_engine|
  79826. invoke javascript_engine, [name] if options[:javascripts]
  79827. end
  79828. hook_for :stylesheet_engine do |stylesheet_engine|
  79829. invoke stylesheet_engine, [name] if options[:stylesheets]
  79830. end
  79831. end
  79832. end
  79833. end
  79834. module Rails
  79835. module Generators
  79836. class ControllerGenerator < NamedBase # :nodoc:
  79837. argument :actions, type: :array, default: [], banner: "action action"
  79838. check_class_collision suffix: "Controller"
  79839. def create_controller_files
  79840. template 'controller.rb', File.join('app/controllers', class_path, "#{file_name}_controller.rb")
  79841. end
  79842. def add_routes
  79843. actions.reverse.each do |action|
  79844. route %{get "#{file_name}/#{action}"}
  79845. end
  79846. end
  79847. hook_for :template_engine, :test_framework, :helper, :assets
  79848. end
  79849. end
  79850. end
  79851. <% if namespaced? -%>
  79852. require_dependency "<%= namespaced_path %>/application_controller"
  79853. <% end -%>
  79854. <% module_namespacing do -%>
  79855. class <%= class_name %>Controller < ApplicationController
  79856. <% actions.each do |action| -%>
  79857. def <%= action %>
  79858. end
  79859. <%= "\n" unless action == actions.last -%>
  79860. <% end -%>
  79861. end
  79862. <% end -%>
  79863. module Rails
  79864. module Generators
  79865. class GeneratorGenerator < NamedBase # :nodoc:
  79866. check_class_collision suffix: "Generator"
  79867. class_option :namespace, type: :boolean, default: true,
  79868. desc: "Namespace generator under lib/generators/name"
  79869. def create_generator_files
  79870. directory '.', generator_dir
  79871. end
  79872. protected
  79873. def generator_dir
  79874. if options[:namespace]
  79875. File.join("lib", "generators", regular_class_path, file_name)
  79876. else
  79877. File.join("lib", "generators", regular_class_path)
  79878. end
  79879. end
  79880. end
  79881. end
  79882. end
  79883. module Rails
  79884. module Generators
  79885. class HelperGenerator < NamedBase # :nodoc:
  79886. check_class_collision suffix: "Helper"
  79887. def create_helper_files
  79888. template 'helper.rb', File.join('app/helpers', class_path, "#{file_name}_helper.rb")
  79889. end
  79890. hook_for :test_framework
  79891. end
  79892. end
  79893. end
  79894. <% module_namespacing do -%>
  79895. module <%= class_name %>Helper
  79896. end
  79897. <% end -%>
  79898. module Rails
  79899. module Generators
  79900. class IntegrationTestGenerator < NamedBase # :nodoc:
  79901. hook_for :integration_tool, as: :integration
  79902. end
  79903. end
  79904. end
  79905. module Rails
  79906. module Generators
  79907. class MigrationGenerator < NamedBase # :nodoc:
  79908. argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
  79909. hook_for :orm, required: true
  79910. end
  79911. end
  79912. end
  79913. module Rails
  79914. module Generators
  79915. class ModelGenerator < NamedBase # :nodoc:
  79916. argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
  79917. hook_for :orm, required: true
  79918. end
  79919. end
  79920. end
  79921. require 'active_support/core_ext/hash/slice'
  79922. require "rails/generators/rails/app/app_generator"
  79923. require 'date'
  79924. module Rails
  79925. # The plugin builder allows you to override elements of the plugin
  79926. # generator without being forced to reverse the operations of the default
  79927. # generator.
  79928. #
  79929. # This allows you to override entire operations, like the creation of the
  79930. # Gemfile, README, or JavaScript files, without needing to know exactly
  79931. # what those operations do so you can create another template action.
  79932. class PluginBuilder
  79933. def rakefile
  79934. template "Rakefile"
  79935. end
  79936. def app
  79937. if mountable?
  79938. directory 'app'
  79939. empty_directory_with_keep_file "app/assets/images/#{name}"
  79940. elsif full?
  79941. empty_directory_with_keep_file 'app/models'
  79942. empty_directory_with_keep_file 'app/controllers'
  79943. empty_directory_with_keep_file 'app/views'
  79944. empty_directory_with_keep_file 'app/helpers'
  79945. empty_directory_with_keep_file 'app/mailers'
  79946. empty_directory_with_keep_file "app/assets/images/#{name}"
  79947. end
  79948. end
  79949. def readme
  79950. template "README.rdoc"
  79951. end
  79952. def gemfile
  79953. template "Gemfile"
  79954. end
  79955. def license
  79956. template "MIT-LICENSE"
  79957. end
  79958. def gemspec
  79959. template "%name%.gemspec"
  79960. end
  79961. def gitignore
  79962. template "gitignore", ".gitignore"
  79963. end
  79964. def lib
  79965. template "lib/%name%.rb"
  79966. template "lib/tasks/%name%_tasks.rake"
  79967. template "lib/%name%/version.rb"
  79968. template "lib/%name%/engine.rb" if engine?
  79969. end
  79970. def config
  79971. template "config/routes.rb" if engine?
  79972. end
  79973. def test
  79974. template "test/test_helper.rb"
  79975. template "test/%name%_test.rb"
  79976. append_file "Rakefile", <<-EOF
  79977. #{rakefile_test_tasks}
  79978. task default: :test
  79979. EOF
  79980. if engine?
  79981. template "test/integration/navigation_test.rb"
  79982. end
  79983. end
  79984. PASSTHROUGH_OPTIONS = [
  79985. :skip_active_record, :skip_javascript, :database, :javascript, :quiet, :pretend, :force, :skip
  79986. ]
  79987. def generate_test_dummy(force = false)
  79988. opts = (options || {}).slice(*PASSTHROUGH_OPTIONS)
  79989. opts[:force] = force
  79990. opts[:skip_bundle] = true
  79991. invoke Rails::Generators::AppGenerator,
  79992. [ File.expand_path(dummy_path, destination_root) ], opts
  79993. end
  79994. def test_dummy_config
  79995. template "rails/boot.rb", "#{dummy_path}/config/boot.rb", force: true
  79996. template "rails/application.rb", "#{dummy_path}/config/application.rb", force: true
  79997. if mountable?
  79998. template "rails/routes.rb", "#{dummy_path}/config/routes.rb", force: true
  79999. end
  80000. end
  80001. def test_dummy_clean
  80002. inside dummy_path do
  80003. remove_file ".gitignore"
  80004. remove_file "db/seeds.rb"
  80005. remove_file "doc"
  80006. remove_file "Gemfile"
  80007. remove_file "lib/tasks"
  80008. remove_file "app/assets/images/rails.png"
  80009. remove_file "public/index.html"
  80010. remove_file "public/robots.txt"
  80011. remove_file "README"
  80012. remove_file "test"
  80013. remove_file "vendor"
  80014. end
  80015. end
  80016. def stylesheets
  80017. if mountable?
  80018. copy_file "#{app_templates_dir}/app/assets/stylesheets/application.css",
  80019. "app/assets/stylesheets/#{name}/application.css"
  80020. elsif full?
  80021. empty_directory_with_keep_file "app/assets/stylesheets/#{name}"
  80022. end
  80023. end
  80024. def javascripts
  80025. return if options.skip_javascript?
  80026. if mountable?
  80027. template "#{app_templates_dir}/app/assets/javascripts/application.js.tt",
  80028. "app/assets/javascripts/#{name}/application.js"
  80029. elsif full?
  80030. empty_directory_with_keep_file "app/assets/javascripts/#{name}"
  80031. end
  80032. end
  80033. def bin(force = false)
  80034. return unless engine?
  80035. directory "bin", force: force do |content|
  80036. "#{shebang}\n" + content
  80037. end
  80038. chmod "bin", 0755, verbose: false
  80039. end
  80040. def gemfile_entry
  80041. return unless inside_application?
  80042. gemfile_in_app_path = File.join(rails_app_path, "Gemfile")
  80043. if File.exist? gemfile_in_app_path
  80044. entry = "gem '#{name}', path: '#{relative_path}'"
  80045. append_file gemfile_in_app_path, entry
  80046. end
  80047. end
  80048. end
  80049. module Generators
  80050. class PluginNewGenerator < AppBase # :nodoc:
  80051. add_shared_options_for "plugin"
  80052. alias_method :plugin_path, :app_path
  80053. class_option :dummy_path, type: :string, default: "test/dummy",
  80054. desc: "Create dummy application at given path"
  80055. class_option :full, type: :boolean, default: false,
  80056. desc: "Generate a rails engine with bundled Rails application for testing"
  80057. class_option :mountable, type: :boolean, default: false,
  80058. desc: "Generate mountable isolated application"
  80059. class_option :skip_gemspec, type: :boolean, default: false,
  80060. desc: "Skip gemspec file"
  80061. class_option :skip_gemfile_entry, type: :boolean, default: false,
  80062. desc: "If creating plugin in application's directory " +
  80063. "skip adding entry to Gemfile"
  80064. def initialize(*args)
  80065. raise Error, "Options should be given after the plugin name. For details run: rails plugin new --help" if args[0].blank?
  80066. @dummy_path = nil
  80067. super
  80068. end
  80069. public_task :create_root
  80070. def create_root_files
  80071. build(:readme)
  80072. build(:rakefile)
  80073. build(:gemspec) unless options[:skip_gemspec]
  80074. build(:license)
  80075. build(:gitignore) unless options[:skip_git]
  80076. build(:gemfile) unless options[:skip_gemfile]
  80077. end
  80078. def create_app_files
  80079. build(:app)
  80080. end
  80081. def create_config_files
  80082. build(:config)
  80083. end
  80084. def create_lib_files
  80085. build(:lib)
  80086. end
  80087. def create_public_stylesheets_files
  80088. build(:stylesheets)
  80089. end
  80090. def create_javascript_files
  80091. build(:javascripts)
  80092. end
  80093. def create_images_directory
  80094. build(:images)
  80095. end
  80096. def create_bin_files
  80097. build(:bin)
  80098. end
  80099. def create_test_files
  80100. build(:test) unless options[:skip_test_unit]
  80101. end
  80102. def create_test_dummy_files
  80103. return unless with_dummy_app?
  80104. create_dummy_app
  80105. end
  80106. def update_gemfile
  80107. build(:gemfile_entry) unless options[:skip_gemfile_entry]
  80108. end
  80109. def finish_template
  80110. build(:leftovers)
  80111. end
  80112. public_task :apply_rails_template, :run_bundle
  80113. def name
  80114. @name ||= begin
  80115. # same as ActiveSupport::Inflector#underscore except not replacing '-'
  80116. underscored = original_name.dup
  80117. underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
  80118. underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
  80119. underscored.downcase!
  80120. underscored
  80121. end
  80122. end
  80123. protected
  80124. def app_templates_dir
  80125. "../../app/templates"
  80126. end
  80127. def create_dummy_app(path = nil)
  80128. dummy_path(path) if path
  80129. say_status :vendor_app, dummy_path
  80130. mute do
  80131. build(:generate_test_dummy)
  80132. store_application_definition!
  80133. build(:test_dummy_config)
  80134. build(:test_dummy_clean)
  80135. # ensure that bin/rails has proper dummy_path
  80136. build(:bin, true)
  80137. end
  80138. end
  80139. def engine?
  80140. full? || mountable?
  80141. end
  80142. def full?
  80143. options[:full]
  80144. end
  80145. def mountable?
  80146. options[:mountable]
  80147. end
  80148. def with_dummy_app?
  80149. options[:skip_test_unit].blank? || options[:dummy_path] != 'test/dummy'
  80150. end
  80151. def self.banner
  80152. "rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]"
  80153. end
  80154. def original_name
  80155. @original_name ||= File.basename(destination_root)
  80156. end
  80157. def camelized
  80158. @camelized ||= name.gsub(/\W/, '_').squeeze('_').camelize
  80159. end
  80160. def valid_const?
  80161. if original_name =~ /[^0-9a-zA-Z_]+/
  80162. raise Error, "Invalid plugin name #{original_name}. Please give a name which use only alphabetic or numeric or \"_\" characters."
  80163. elsif camelized =~ /^\d/
  80164. raise Error, "Invalid plugin name #{original_name}. Please give a name which does not start with numbers."
  80165. elsif RESERVED_NAMES.include?(name)
  80166. raise Error, "Invalid plugin name #{original_name}. Please give a name which does not match one of the reserved rails words."
  80167. elsif Object.const_defined?(camelized)
  80168. raise Error, "Invalid plugin name #{original_name}, constant #{camelized} is already in use. Please choose another plugin name."
  80169. end
  80170. end
  80171. def application_definition
  80172. @application_definition ||= begin
  80173. dummy_application_path = File.expand_path("#{dummy_path}/config/application.rb", destination_root)
  80174. unless options[:pretend] || !File.exists?(dummy_application_path)
  80175. contents = File.read(dummy_application_path)
  80176. contents[(contents.index(/module ([\w]+)\n(.*)class Application/m))..-1]
  80177. end
  80178. end
  80179. end
  80180. alias :store_application_definition! :application_definition
  80181. def get_builder_class
  80182. defined?(::PluginBuilder) ? ::PluginBuilder : Rails::PluginBuilder
  80183. end
  80184. def rakefile_test_tasks
  80185. <<-RUBY
  80186. require 'rake/testtask'
  80187. Rake::TestTask.new(:test) do |t|
  80188. t.libs << 'lib'
  80189. t.libs << 'test'
  80190. t.pattern = 'test/**/*_test.rb'
  80191. t.verbose = false
  80192. end
  80193. RUBY
  80194. end
  80195. def dummy_path(path = nil)
  80196. @dummy_path = path if path
  80197. @dummy_path || options[:dummy_path]
  80198. end
  80199. def mute(&block)
  80200. shell.mute(&block)
  80201. end
  80202. def rails_app_path
  80203. APP_PATH.sub("/config/application", "") if defined?(APP_PATH)
  80204. end
  80205. def inside_application?
  80206. rails_app_path && app_path =~ /^#{rails_app_path}/
  80207. end
  80208. def relative_path
  80209. return unless inside_application?
  80210. app_path.sub(/^#{rails_app_path}\//, '')
  80211. end
  80212. end
  80213. end
  80214. end
  80215. <% if mountable? -%>
  80216. <%= camelized %>::Engine.routes.draw do
  80217. <% else -%>
  80218. Rails.application.routes.draw do
  80219. <% end -%>
  80220. end
  80221. module <%= camelized %>
  80222. class Engine < ::Rails::Engine
  80223. <% if mountable? -%>
  80224. isolate_namespace <%= camelized %>
  80225. <% end -%>
  80226. end
  80227. end
  80228. module <%= camelized %>
  80229. VERSION = "0.0.1"
  80230. end
  80231. <% if engine? -%>
  80232. require "<%= name %>/engine"
  80233. <% end -%>
  80234. module <%= camelized %>
  80235. end
  80236. require File.expand_path('../boot', __FILE__)
  80237. <% if include_all_railties? -%>
  80238. require 'rails/all'
  80239. <% else -%>
  80240. # Pick the frameworks you want:
  80241. <%= comment_if :skip_active_record %>require "active_record/railtie"
  80242. require "action_controller/railtie"
  80243. require "action_mailer/railtie"
  80244. <%= comment_if :skip_sprockets %>require "sprockets/railtie"
  80245. <%= comment_if :skip_test_unit %>require "rails/test_unit/railtie"
  80246. <% end -%>
  80247. Bundler.require(*Rails.groups)
  80248. require "<%= name %>"
  80249. <%= application_definition %>
  80250. gemfile = File.expand_path('../../../../Gemfile', __FILE__)
  80251. if File.exist?(gemfile)
  80252. ENV['BUNDLE_GEMFILE'] = gemfile
  80253. require 'bundler'
  80254. Bundler.setup
  80255. end
  80256. $:.unshift File.expand_path('../../../../lib', __FILE__)Rails.application.routes.draw do
  80257. mount <%= camelized %>::Engine => "/<%= name %>"
  80258. end
  80259. require 'rails/generators/resource_helpers'
  80260. require 'rails/generators/rails/model/model_generator'
  80261. require 'active_support/core_ext/object/blank'
  80262. module Rails
  80263. module Generators
  80264. class ResourceGenerator < ModelGenerator # :nodoc:
  80265. include ResourceHelpers
  80266. hook_for :resource_controller, required: true do |controller|
  80267. invoke controller, [ controller_name, options[:actions] ]
  80268. end
  80269. class_option :actions, type: :array, banner: "ACTION ACTION", default: [],
  80270. desc: "Actions for the resource controller"
  80271. hook_for :resource_route, required: true
  80272. end
  80273. end
  80274. end
  80275. module Rails
  80276. module Generators
  80277. class ResourceRouteGenerator < NamedBase # :nodoc:
  80278. # Properly nests namespaces passed into a generator
  80279. #
  80280. # $ rails generate resource admin/users/products
  80281. #
  80282. # should give you
  80283. #
  80284. # namespace :admin do
  80285. # namespace :users
  80286. # resources :products
  80287. # end
  80288. # end
  80289. def add_resource_route
  80290. return if options[:actions].present?
  80291. # iterates over all namespaces and opens up blocks
  80292. regular_class_path.each_with_index do |namespace, index|
  80293. write("namespace :#{namespace} do", index + 1)
  80294. end
  80295. # inserts the primary resource
  80296. write("resources :#{file_name.pluralize}", route_length + 1)
  80297. # ends blocks
  80298. regular_class_path.each_index do |index|
  80299. write("end", route_length - index)
  80300. end
  80301. # route prepends two spaces onto the front of the string that is passed, this corrects that
  80302. route route_string[2..-1]
  80303. end
  80304. private
  80305. def route_string
  80306. @route_string ||= ""
  80307. end
  80308. def write(str, indent)
  80309. route_string << "#{" " * indent}#{str}\n"
  80310. end
  80311. def route_length
  80312. regular_class_path.length
  80313. end
  80314. end
  80315. end
  80316. end
  80317. require 'rails/generators/rails/resource/resource_generator'
  80318. module Rails
  80319. module Generators
  80320. class ScaffoldGenerator < ResourceGenerator # :nodoc:
  80321. remove_hook_for :resource_controller
  80322. remove_class_option :actions
  80323. class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
  80324. class_option :stylesheet_engine, desc: "Engine for Stylesheets"
  80325. def handle_skip
  80326. @options = @options.merge(stylesheet_engine: false) unless options[:stylesheets]
  80327. end
  80328. hook_for :scaffold_controller, required: true
  80329. hook_for :assets do |assets|
  80330. invoke assets, [controller_name]
  80331. end
  80332. hook_for :stylesheet_engine do |stylesheet_engine|
  80333. if behavior == :invoke
  80334. invoke stylesheet_engine, [controller_name]
  80335. end
  80336. end
  80337. end
  80338. end
  80339. end
  80340. require 'rails/generators/resource_helpers'
  80341. module Rails
  80342. module Generators
  80343. class ScaffoldControllerGenerator < NamedBase # :nodoc:
  80344. include ResourceHelpers
  80345. check_class_collision suffix: "Controller"
  80346. class_option :orm, banner: "NAME", type: :string, required: true,
  80347. desc: "ORM to generate the controller for"
  80348. argument :attributes, type: :array, default: [], banner: "field:type field:type"
  80349. def create_controller_files
  80350. template "controller.rb", File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb")
  80351. end
  80352. hook_for :template_engine, :test_framework, as: :scaffold
  80353. # Invoke the helper using the controller name (pluralized)
  80354. hook_for :helper, as: :scaffold do |invoked|
  80355. invoke invoked, [ controller_name ]
  80356. end
  80357. end
  80358. end
  80359. end
  80360. <% if namespaced? -%>
  80361. require_dependency "<%= namespaced_file_path %>/application_controller"
  80362. <% end -%>
  80363. <% module_namespacing do -%>
  80364. class <%= controller_class_name %>Controller < ApplicationController
  80365. before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
  80366. # GET <%= route_url %>
  80367. def index
  80368. @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
  80369. end
  80370. # GET <%= route_url %>/1
  80371. def show
  80372. end
  80373. # GET <%= route_url %>/new
  80374. def new
  80375. @<%= singular_table_name %> = <%= orm_class.build(class_name) %>
  80376. end
  80377. # GET <%= route_url %>/1/edit
  80378. def edit
  80379. end
  80380. # POST <%= route_url %>
  80381. def create
  80382. @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
  80383. if @<%= orm_instance.save %>
  80384. redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %>
  80385. else
  80386. render action: 'new'
  80387. end
  80388. end
  80389. # PATCH/PUT <%= route_url %>/1
  80390. def update
  80391. if @<%= orm_instance.update("#{singular_table_name}_params") %>
  80392. redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %>
  80393. else
  80394. render action: 'edit'
  80395. end
  80396. end
  80397. # DELETE <%= route_url %>/1
  80398. def destroy
  80399. @<%= orm_instance.destroy %>
  80400. redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %>
  80401. end
  80402. private
  80403. # Use callbacks to share common setup or constraints between actions.
  80404. def set_<%= singular_table_name %>
  80405. @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
  80406. end
  80407. # Never trust parameters from the scary internet, only allow the white list through.
  80408. def <%= "#{singular_table_name}_params" %>
  80409. <%- if attributes_names.empty? -%>
  80410. params[<%= ":#{singular_table_name}" %>]
  80411. <%- else -%>
  80412. params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
  80413. <%- end -%>
  80414. end
  80415. end
  80416. <% end -%>
  80417. module Rails
  80418. module Generators
  80419. class TaskGenerator < NamedBase # :nodoc:
  80420. argument :actions, type: :array, default: [], banner: "action action"
  80421. def create_task_files
  80422. template 'task.rb', File.join('lib/tasks', "#{file_name}.rake")
  80423. end
  80424. end
  80425. end
  80426. end
  80427. namespace :<%= file_name %> do
  80428. <% actions.each do |action| -%>
  80429. desc "TODO"
  80430. task <%= action %>: :environment do
  80431. end
  80432. <% end -%>
  80433. end
  80434. require 'rails/generators/active_model'
  80435. module Rails
  80436. module Generators
  80437. # Deal with controller names on scaffold and add some helpers to deal with
  80438. # ActiveModel.
  80439. module ResourceHelpers # :nodoc:
  80440. mattr_accessor :skip_warn
  80441. def self.included(base) #:nodoc:
  80442. base.class_option :force_plural, type: :boolean, desc: "Forces the use of a plural ModelName"
  80443. end
  80444. # Set controller variables on initialization.
  80445. def initialize(*args) #:nodoc:
  80446. super
  80447. if name == name.pluralize && name.singularize != name.pluralize && !options[:force_plural]
  80448. unless ResourceHelpers.skip_warn
  80449. say "Plural version of the model detected, using singularized version. Override with --force-plural."
  80450. ResourceHelpers.skip_warn = true
  80451. end
  80452. name.replace name.singularize
  80453. assign_names!(name)
  80454. end
  80455. @controller_name = name.pluralize
  80456. end
  80457. protected
  80458. attr_reader :controller_name
  80459. def controller_class_path
  80460. class_path
  80461. end
  80462. def controller_file_name
  80463. @controller_file_name ||= file_name.pluralize
  80464. end
  80465. def controller_file_path
  80466. @controller_file_path ||= (controller_class_path + [controller_file_name]).join('/')
  80467. end
  80468. def controller_class_name
  80469. (controller_class_path + [controller_file_name]).map!{ |m| m.camelize }.join('::')
  80470. end
  80471. def controller_i18n_scope
  80472. @controller_i18n_scope ||= controller_file_path.tr('/', '.')
  80473. end
  80474. # Loads the ORM::Generators::ActiveModel class. This class is responsible
  80475. # to tell scaffold entities how to generate an specific method for the
  80476. # ORM. Check Rails::Generators::ActiveModel for more information.
  80477. def orm_class
  80478. @orm_class ||= begin
  80479. # Raise an error if the class_option :orm was not defined.
  80480. unless self.class.class_options[:orm]
  80481. raise "You need to have :orm as class option to invoke orm_class and orm_instance"
  80482. end
  80483. begin
  80484. "#{options[:orm].to_s.camelize}::Generators::ActiveModel".constantize
  80485. rescue NameError
  80486. Rails::Generators::ActiveModel
  80487. end
  80488. end
  80489. end
  80490. # Initialize ORM::Generators::ActiveModel to access instance methods.
  80491. def orm_instance(name=singular_table_name)
  80492. @orm_instance ||= orm_class.new(name)
  80493. end
  80494. end
  80495. end
  80496. end
  80497. require 'active_support/core_ext/class/attribute'
  80498. require 'active_support/core_ext/module/delegation'
  80499. require 'active_support/core_ext/hash/reverse_merge'
  80500. require 'active_support/core_ext/kernel/reporting'
  80501. require 'rails/generators'
  80502. require 'fileutils'
  80503. module Rails
  80504. module Generators
  80505. # Disable color in output. Easier to debug.
  80506. no_color!
  80507. # This class provides a TestCase for testing generators. To setup, you need
  80508. # just to configure the destination and set which generator is being tested:
  80509. #
  80510. # class AppGeneratorTest < Rails::Generators::TestCase
  80511. # tests AppGenerator
  80512. # destination File.expand_path("../tmp", File.dirname(__FILE__))
  80513. # end
  80514. #
  80515. # If you want to ensure your destination root is clean before running each test,
  80516. # you can set a setup callback:
  80517. #
  80518. # class AppGeneratorTest < Rails::Generators::TestCase
  80519. # tests AppGenerator
  80520. # destination File.expand_path("../tmp", File.dirname(__FILE__))
  80521. # setup :prepare_destination
  80522. # end
  80523. class TestCase < ActiveSupport::TestCase
  80524. include FileUtils
  80525. class_attribute :destination_root, :current_path, :generator_class, :default_arguments
  80526. # Generators frequently change the current path using +FileUtils.cd+.
  80527. # So we need to store the path at file load and revert back to it after each test.
  80528. self.current_path = File.expand_path(Dir.pwd)
  80529. self.default_arguments = []
  80530. def setup # :nodoc:
  80531. destination_root_is_set?
  80532. ensure_current_path
  80533. super
  80534. end
  80535. def teardown # :nodoc:
  80536. ensure_current_path
  80537. super
  80538. end
  80539. # Sets which generator should be tested:
  80540. #
  80541. # tests AppGenerator
  80542. def self.tests(klass)
  80543. self.generator_class = klass
  80544. end
  80545. # Sets default arguments on generator invocation. This can be overwritten when
  80546. # invoking it.
  80547. #
  80548. # arguments %w(app_name --skip-active-record)
  80549. def self.arguments(array)
  80550. self.default_arguments = array
  80551. end
  80552. # Sets the destination of generator files:
  80553. #
  80554. # destination File.expand_path("../tmp", File.dirname(__FILE__))
  80555. def self.destination(path)
  80556. self.destination_root = path
  80557. end
  80558. # Asserts a given file exists. You need to supply an absolute path or a path relative
  80559. # to the configured destination:
  80560. #
  80561. # assert_file "config/environment.rb"
  80562. #
  80563. # You can also give extra arguments. If the argument is a regexp, it will check if the
  80564. # regular expression matches the given file content. If it's a string, it compares the
  80565. # file with the given string:
  80566. #
  80567. # assert_file "config/environment.rb", /initialize/
  80568. #
  80569. # Finally, when a block is given, it yields the file content:
  80570. #
  80571. # assert_file "app/controllers/products_controller.rb" do |controller|
  80572. # assert_instance_method :index, controller do |index|
  80573. # assert_match(/Product\.all/, index)
  80574. # end
  80575. # end
  80576. def assert_file(relative, *contents)
  80577. absolute = File.expand_path(relative, destination_root)
  80578. assert File.exists?(absolute), "Expected file #{relative.inspect} to exist, but does not"
  80579. read = File.read(absolute) if block_given? || !contents.empty?
  80580. yield read if block_given?
  80581. contents.each do |content|
  80582. case content
  80583. when String
  80584. assert_equal content, read
  80585. when Regexp
  80586. assert_match content, read
  80587. end
  80588. end
  80589. end
  80590. alias :assert_directory :assert_file
  80591. # Asserts a given file does not exist. You need to supply an absolute path or a
  80592. # path relative to the configured destination:
  80593. #
  80594. # assert_no_file "config/random.rb"
  80595. def assert_no_file(relative)
  80596. absolute = File.expand_path(relative, destination_root)
  80597. assert !File.exists?(absolute), "Expected file #{relative.inspect} to not exist, but does"
  80598. end
  80599. alias :assert_no_directory :assert_no_file
  80600. # Asserts a given migration exists. You need to supply an absolute path or a
  80601. # path relative to the configured destination:
  80602. #
  80603. # assert_migration "db/migrate/create_products.rb"
  80604. #
  80605. # This method manipulates the given path and tries to find any migration which
  80606. # matches the migration name. For example, the call above is converted to:
  80607. #
  80608. # assert_file "db/migrate/003_create_products.rb"
  80609. #
  80610. # Consequently, assert_migration accepts the same arguments has assert_file.
  80611. def assert_migration(relative, *contents, &block)
  80612. file_name = migration_file_name(relative)
  80613. assert file_name, "Expected migration #{relative} to exist, but was not found"
  80614. assert_file file_name, *contents, &block
  80615. end
  80616. # Asserts a given migration does not exist. You need to supply an absolute path or a
  80617. # path relative to the configured destination:
  80618. #
  80619. # assert_no_migration "db/migrate/create_products.rb"
  80620. def assert_no_migration(relative)
  80621. file_name = migration_file_name(relative)
  80622. assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}"
  80623. end
  80624. # Asserts the given class method exists in the given content. This method does not detect
  80625. # class methods inside (class << self), only class methods which starts with "self.".
  80626. # When a block is given, it yields the content of the method.
  80627. #
  80628. # assert_migration "db/migrate/create_products.rb" do |migration|
  80629. # assert_class_method :up, migration do |up|
  80630. # assert_match(/create_table/, up)
  80631. # end
  80632. # end
  80633. def assert_class_method(method, content, &block)
  80634. assert_instance_method "self.#{method}", content, &block
  80635. end
  80636. # Asserts the given method exists in the given content. When a block is given,
  80637. # it yields the content of the method.
  80638. #
  80639. # assert_file "app/controllers/products_controller.rb" do |controller|
  80640. # assert_instance_method :index, controller do |index|
  80641. # assert_match(/Product\.all/, index)
  80642. # end
  80643. # end
  80644. def assert_instance_method(method, content)
  80645. assert content =~ /(\s+)def #{method}(\(.+\))?(.*?)\n\1end/m, "Expected to have method #{method}"
  80646. yield $3.strip if block_given?
  80647. end
  80648. alias :assert_method :assert_instance_method
  80649. # Asserts the given attribute type gets translated to a field type
  80650. # properly:
  80651. #
  80652. # assert_field_type :date, :date_select
  80653. def assert_field_type(attribute_type, field_type)
  80654. assert_equal(field_type, create_generated_attribute(attribute_type).field_type)
  80655. end
  80656. # Asserts the given attribute type gets a proper default value:
  80657. #
  80658. # assert_field_default_value :string, "MyString"
  80659. def assert_field_default_value(attribute_type, value)
  80660. assert_equal(value, create_generated_attribute(attribute_type).default)
  80661. end
  80662. # Runs the generator configured for this class. The first argument is an array like
  80663. # command line arguments:
  80664. #
  80665. # class AppGeneratorTest < Rails::Generators::TestCase
  80666. # tests AppGenerator
  80667. # destination File.expand_path("../tmp", File.dirname(__FILE__))
  80668. # teardown :cleanup_destination_root
  80669. #
  80670. # test "database.yml is not created when skipping Active Record" do
  80671. # run_generator %w(myapp --skip-active-record)
  80672. # assert_no_file "config/database.yml"
  80673. # end
  80674. # end
  80675. #
  80676. # You can provide a configuration hash as second argument. This method returns the output
  80677. # printed by the generator.
  80678. def run_generator(args=self.default_arguments, config={})
  80679. capture(:stdout) { self.generator_class.start(args, config.reverse_merge(destination_root: destination_root)) }
  80680. end
  80681. # Instantiate the generator.
  80682. def generator(args=self.default_arguments, options={}, config={})
  80683. @generator ||= self.generator_class.new(args, options, config.reverse_merge(destination_root: destination_root))
  80684. end
  80685. # Create a Rails::Generators::GeneratedAttribute by supplying the
  80686. # attribute type and, optionally, the attribute name:
  80687. #
  80688. # create_generated_attribute(:string, 'name')
  80689. def create_generated_attribute(attribute_type, name = 'test', index = nil)
  80690. Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(':'))
  80691. end
  80692. protected
  80693. def destination_root_is_set? # :nodoc:
  80694. raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root
  80695. end
  80696. def ensure_current_path # :nodoc:
  80697. cd current_path
  80698. end
  80699. def prepare_destination # :nodoc:
  80700. rm_rf(destination_root)
  80701. mkdir_p(destination_root)
  80702. end
  80703. def migration_file_name(relative) # :nodoc:
  80704. absolute = File.expand_path(relative, destination_root)
  80705. dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, '')
  80706. Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
  80707. end
  80708. end
  80709. end
  80710. end
  80711. require 'rails/generators/test_unit'
  80712. module TestUnit # :nodoc:
  80713. module Generators # :nodoc:
  80714. class ControllerGenerator < Base # :nodoc:
  80715. argument :actions, type: :array, default: [], banner: "action action"
  80716. check_class_collision suffix: "ControllerTest"
  80717. def create_test_files
  80718. template 'functional_test.rb',
  80719. File.join('test/controllers', class_path, "#{file_name}_controller_test.rb")
  80720. end
  80721. end
  80722. end
  80723. end
  80724. require 'test_helper'
  80725. <% module_namespacing do -%>
  80726. class <%= class_name %>ControllerTest < ActionController::TestCase
  80727. <% if actions.empty? -%>
  80728. # test "the truth" do
  80729. # assert true
  80730. # end
  80731. <% else -%>
  80732. <% actions.each do |action| -%>
  80733. test "should get <%= action %>" do
  80734. get :<%= action %>
  80735. assert_response :success
  80736. end
  80737. <% end -%>
  80738. <% end -%>
  80739. end
  80740. <% end -%>
  80741. require 'rails/generators/test_unit'
  80742. module TestUnit # :nodoc:
  80743. module Generators # :nodoc:
  80744. class HelperGenerator < Base # :nodoc:
  80745. check_class_collision suffix: "HelperTest"
  80746. def create_helper_files
  80747. template 'helper_test.rb', File.join('test/helpers', class_path, "#{file_name}_helper_test.rb")
  80748. end
  80749. end
  80750. end
  80751. end
  80752. require 'test_helper'
  80753. <% module_namespacing do -%>
  80754. class <%= class_name %>HelperTest < ActionView::TestCase
  80755. end
  80756. <% end -%>
  80757. require 'rails/generators/test_unit'
  80758. module TestUnit # :nodoc:
  80759. module Generators # :nodoc:
  80760. class IntegrationGenerator < Base # :nodoc:
  80761. check_class_collision suffix: "Test"
  80762. def create_test_files
  80763. template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb")
  80764. end
  80765. end
  80766. end
  80767. end
  80768. require 'test_helper'
  80769. class <%= class_name %>Test < ActionDispatch::IntegrationTest
  80770. # test "the truth" do
  80771. # assert true
  80772. # end
  80773. end
  80774. require 'rails/generators/test_unit'
  80775. module TestUnit # :nodoc:
  80776. module Generators # :nodoc:
  80777. class MailerGenerator < Base # :nodoc:
  80778. argument :actions, type: :array, default: [], banner: "method method"
  80779. check_class_collision suffix: "Test"
  80780. def create_test_files
  80781. template "functional_test.rb", File.join('test/mailers', class_path, "#{file_name}_test.rb")
  80782. end
  80783. end
  80784. end
  80785. end
  80786. require 'test_helper'
  80787. <% module_namespacing do -%>
  80788. class <%= class_name %>Test < ActionMailer::TestCase
  80789. <% actions.each do |action| -%>
  80790. test "<%= action %>" do
  80791. mail = <%= class_name %>.<%= action %>
  80792. assert_equal <%= action.to_s.humanize.inspect %>, mail.subject
  80793. assert_equal ["to@example.org"], mail.to
  80794. assert_equal ["from@example.com"], mail.from
  80795. assert_match "Hi", mail.body.encoded
  80796. end
  80797. <% end -%>
  80798. <% if actions.blank? -%>
  80799. # test "the truth" do
  80800. # assert true
  80801. # end
  80802. <% end -%>
  80803. end
  80804. <% end -%>
  80805. require 'rails/generators/test_unit'
  80806. module TestUnit # :nodoc:
  80807. module Generators # :nodoc:
  80808. class ModelGenerator < Base # :nodoc:
  80809. RESERVED_YAML_KEYWORDS = %w(y yes n no true false on off null)
  80810. argument :attributes, type: :array, default: [], banner: "field:type field:type"
  80811. class_option :fixture, type: :boolean
  80812. check_class_collision suffix: "Test"
  80813. def create_test_file
  80814. template 'unit_test.rb', File.join('test/models', class_path, "#{file_name}_test.rb")
  80815. end
  80816. hook_for :fixture_replacement
  80817. def create_fixture_file
  80818. if options[:fixture] && options[:fixture_replacement].nil?
  80819. template 'fixtures.yml', File.join('test/fixtures', class_path, "#{plural_file_name}.yml")
  80820. end
  80821. end
  80822. private
  80823. def yaml_key_value(key, value)
  80824. if RESERVED_YAML_KEYWORDS.include?(key.downcase)
  80825. "'#{key}': #{value}"
  80826. else
  80827. "#{key}: #{value}"
  80828. end
  80829. end
  80830. end
  80831. end
  80832. end
  80833. require 'test_helper'
  80834. <% module_namespacing do -%>
  80835. class <%= class_name %>Test < ActiveSupport::TestCase
  80836. # test "the truth" do
  80837. # assert true
  80838. # end
  80839. end
  80840. <% end -%>
  80841. require 'rails/generators/test_unit'
  80842. module TestUnit # :nodoc:
  80843. module Generators # :nodoc:
  80844. class PluginGenerator < Base # :nodoc:
  80845. check_class_collision suffix: "Test"
  80846. def create_test_files
  80847. directory '.', 'test'
  80848. end
  80849. end
  80850. end
  80851. end
  80852. require 'active_support/testing/autorun'
  80853. require 'active_support'
  80854. require 'rails/generators/test_unit'
  80855. require 'rails/generators/resource_helpers'
  80856. module TestUnit # :nodoc:
  80857. module Generators # :nodoc:
  80858. class ScaffoldGenerator < Base # :nodoc:
  80859. include Rails::Generators::ResourceHelpers
  80860. check_class_collision suffix: "ControllerTest"
  80861. argument :attributes, type: :array, default: [], banner: "field:type field:type"
  80862. def create_test_files
  80863. template "functional_test.rb",
  80864. File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb")
  80865. end
  80866. private
  80867. def attributes_hash
  80868. return if attributes_names.empty?
  80869. attributes_names.map do |name|
  80870. "#{name}: @#{singular_table_name}.#{name}"
  80871. end.sort.join(', ')
  80872. end
  80873. end
  80874. end
  80875. end
  80876. require 'test_helper'
  80877. <% module_namespacing do -%>
  80878. class <%= controller_class_name %>ControllerTest < ActionController::TestCase
  80879. setup do
  80880. @<%= singular_table_name %> = <%= table_name %>(:one)
  80881. end
  80882. test "should get index" do
  80883. get :index
  80884. assert_response :success
  80885. assert_not_nil assigns(:<%= table_name %>)
  80886. end
  80887. test "should get new" do
  80888. get :new
  80889. assert_response :success
  80890. end
  80891. test "should create <%= singular_table_name %>" do
  80892. assert_difference('<%= class_name %>.count') do
  80893. post :create, <%= "#{singular_table_name}: { #{attributes_hash} }" %>
  80894. end
  80895. assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>))
  80896. end
  80897. test "should show <%= singular_table_name %>" do
  80898. get :show, id: <%= "@#{singular_table_name}" %>
  80899. assert_response :success
  80900. end
  80901. test "should get edit" do
  80902. get :edit, id: <%= "@#{singular_table_name}" %>
  80903. assert_response :success
  80904. end
  80905. test "should update <%= singular_table_name %>" do
  80906. patch :update, id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %>
  80907. assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>))
  80908. end
  80909. test "should destroy <%= singular_table_name %>" do
  80910. assert_difference('<%= class_name %>.count', -1) do
  80911. delete :destroy, id: <%= "@#{singular_table_name}" %>
  80912. end
  80913. assert_redirected_to <%= index_helper %>_path
  80914. end
  80915. end
  80916. <% end -%>
  80917. require 'rails/generators/named_base'
  80918. module TestUnit # :nodoc:
  80919. module Generators # :nodoc:
  80920. class Base < Rails::Generators::NamedBase # :nodoc:
  80921. end
  80922. end
  80923. end
  80924. activesupport_path = File.expand_path('../../../../activesupport/lib', __FILE__)
  80925. $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
  80926. require 'active_support'
  80927. require 'active_support/core_ext/object/blank'
  80928. require 'active_support/core_ext/kernel/singleton_class'
  80929. require 'active_support/core_ext/array/extract_options'
  80930. require 'active_support/core_ext/hash/deep_merge'
  80931. require 'active_support/core_ext/module/attribute_accessors'
  80932. require 'active_support/core_ext/string/inflections'
  80933. require 'rails/generators/base'
  80934. module Rails
  80935. module Generators
  80936. autoload :Actions, 'rails/generators/actions'
  80937. autoload :ActiveModel, 'rails/generators/active_model'
  80938. autoload :Migration, 'rails/generators/migration'
  80939. autoload :NamedBase, 'rails/generators/named_base'
  80940. autoload :ResourceHelpers, 'rails/generators/resource_helpers'
  80941. autoload :TestCase, 'rails/generators/test_case'
  80942. mattr_accessor :namespace
  80943. DEFAULT_ALIASES = {
  80944. rails: {
  80945. actions: '-a',
  80946. orm: '-o',
  80947. javascripts: '-j',
  80948. javascript_engine: '-je',
  80949. resource_controller: '-c',
  80950. scaffold_controller: '-c',
  80951. stylesheets: '-y',
  80952. stylesheet_engine: '-se',
  80953. template_engine: '-e',
  80954. test_framework: '-t'
  80955. },
  80956. test_unit: {
  80957. fixture_replacement: '-r',
  80958. }
  80959. }
  80960. DEFAULT_OPTIONS = {
  80961. rails: {
  80962. assets: true,
  80963. force_plural: false,
  80964. helper: true,
  80965. integration_tool: nil,
  80966. javascripts: true,
  80967. javascript_engine: :js,
  80968. orm: false,
  80969. resource_controller: :controller,
  80970. resource_route: true,
  80971. scaffold_controller: :scaffold_controller,
  80972. stylesheets: true,
  80973. stylesheet_engine: :css,
  80974. test_framework: false,
  80975. template_engine: :erb
  80976. }
  80977. }
  80978. def self.configure!(config) #:nodoc:
  80979. no_color! unless config.colorize_logging
  80980. aliases.deep_merge! config.aliases
  80981. options.deep_merge! config.options
  80982. fallbacks.merge! config.fallbacks
  80983. templates_path.concat config.templates
  80984. templates_path.uniq!
  80985. hide_namespaces(*config.hidden_namespaces)
  80986. end
  80987. def self.templates_path #:nodoc:
  80988. @templates_path ||= []
  80989. end
  80990. def self.aliases #:nodoc:
  80991. @aliases ||= DEFAULT_ALIASES.dup
  80992. end
  80993. def self.options #:nodoc:
  80994. @options ||= DEFAULT_OPTIONS.dup
  80995. end
  80996. # Hold configured generators fallbacks. If a plugin developer wants a
  80997. # generator group to fallback to another group in case of missing generators,
  80998. # they can add a fallback.
  80999. #
  81000. # For example, shoulda is considered a test_framework and is an extension
  81001. # of test_unit. However, most part of shoulda generators are similar to
  81002. # test_unit ones.
  81003. #
  81004. # Shoulda then can tell generators to search for test_unit generators when
  81005. # some of them are not available by adding a fallback:
  81006. #
  81007. # Rails::Generators.fallbacks[:shoulda] = :test_unit
  81008. def self.fallbacks
  81009. @fallbacks ||= {}
  81010. end
  81011. # Remove the color from output.
  81012. def self.no_color!
  81013. Thor::Base.shell = Thor::Shell::Basic
  81014. end
  81015. # Track all generators subclasses.
  81016. def self.subclasses
  81017. @subclasses ||= []
  81018. end
  81019. # Rails finds namespaces similar to thor, it only adds one rule:
  81020. #
  81021. # Generators names must end with "_generator.rb". This is required because Rails
  81022. # looks in load paths and loads the generator just before it's going to be used.
  81023. #
  81024. # find_by_namespace :webrat, :rails, :integration
  81025. #
  81026. # Will search for the following generators:
  81027. #
  81028. # "rails:webrat", "webrat:integration", "webrat"
  81029. #
  81030. # Notice that "rails:generators:webrat" could be loaded as well, what
  81031. # Rails looks for is the first and last parts of the namespace.
  81032. def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
  81033. lookups = []
  81034. lookups << "#{base}:#{name}" if base
  81035. lookups << "#{name}:#{context}" if context
  81036. unless base || context
  81037. unless name.to_s.include?(?:)
  81038. lookups << "#{name}:#{name}"
  81039. lookups << "rails:#{name}"
  81040. end
  81041. lookups << "#{name}"
  81042. end
  81043. lookup(lookups)
  81044. namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
  81045. lookups.each do |namespace|
  81046. klass = namespaces[namespace]
  81047. return klass if klass
  81048. end
  81049. invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
  81050. end
  81051. # Receives a namespace, arguments and the behavior to invoke the generator.
  81052. # It's used as the default entry point for generate, destroy and update
  81053. # commands.
  81054. def self.invoke(namespace, args=ARGV, config={})
  81055. names = namespace.to_s.split(':')
  81056. if klass = find_by_namespace(names.pop, names.any? && names.join(':'))
  81057. args << "--help" if args.empty? && klass.arguments.any? { |a| a.required? }
  81058. klass.start(args, config)
  81059. else
  81060. puts "Could not find generator #{namespace}."
  81061. end
  81062. end
  81063. def self.hidden_namespaces
  81064. @hidden_namespaces ||= begin
  81065. orm = options[:rails][:orm]
  81066. test = options[:rails][:test_framework]
  81067. template = options[:rails][:template_engine]
  81068. css = options[:rails][:stylesheet_engine]
  81069. [
  81070. "rails",
  81071. "resource_route",
  81072. "#{orm}:migration",
  81073. "#{orm}:model",
  81074. "#{test}:controller",
  81075. "#{test}:helper",
  81076. "#{test}:integration",
  81077. "#{test}:mailer",
  81078. "#{test}:model",
  81079. "#{test}:scaffold",
  81080. "#{test}:view",
  81081. "#{template}:controller",
  81082. "#{template}:scaffold",
  81083. "#{template}:mailer",
  81084. "#{css}:scaffold",
  81085. "#{css}:assets",
  81086. "css:assets",
  81087. "css:scaffold"
  81088. ]
  81089. end
  81090. end
  81091. class << self
  81092. def hide_namespaces(*namespaces)
  81093. hidden_namespaces.concat(namespaces)
  81094. end
  81095. alias hide_namespace hide_namespaces
  81096. end
  81097. # Show help message with available generators.
  81098. def self.help(command = 'generate')
  81099. lookup!
  81100. namespaces = subclasses.map{ |k| k.namespace }
  81101. namespaces.sort!
  81102. groups = Hash.new { |h,k| h[k] = [] }
  81103. namespaces.each do |namespace|
  81104. base = namespace.split(':').first
  81105. groups[base] << namespace
  81106. end
  81107. puts "Usage: rails #{command} GENERATOR [args] [options]"
  81108. puts
  81109. puts "General options:"
  81110. puts " -h, [--help] # Print generator's options and usage"
  81111. puts " -p, [--pretend] # Run but do not make any changes"
  81112. puts " -f, [--force] # Overwrite files that already exist"
  81113. puts " -s, [--skip] # Skip files that already exist"
  81114. puts " -q, [--quiet] # Suppress status output"
  81115. puts
  81116. puts "Please choose a generator below."
  81117. puts
  81118. # Print Rails defaults first.
  81119. rails = groups.delete("rails")
  81120. rails.map! { |n| n.sub(/^rails:/, '') }
  81121. rails.delete("app")
  81122. rails.delete("plugin_new")
  81123. print_list("rails", rails)
  81124. hidden_namespaces.each { |n| groups.delete(n.to_s) }
  81125. groups.sort.each { |b, n| print_list(b, n) }
  81126. end
  81127. protected
  81128. # Prints a list of generators.
  81129. def self.print_list(base, namespaces) #:nodoc:
  81130. namespaces = namespaces.reject do |n|
  81131. hidden_namespaces.include?(n)
  81132. end
  81133. return if namespaces.empty?
  81134. puts "#{base.camelize}:"
  81135. namespaces.each do |namespace|
  81136. puts(" #{namespace}")
  81137. end
  81138. puts
  81139. end
  81140. # Try fallbacks for the given base.
  81141. def self.invoke_fallbacks_for(name, base) #:nodoc:
  81142. return nil unless base && fallbacks[base.to_sym]
  81143. invoked_fallbacks = []
  81144. Array(fallbacks[base.to_sym]).each do |fallback|
  81145. next if invoked_fallbacks.include?(fallback)
  81146. invoked_fallbacks << fallback
  81147. klass = find_by_namespace(name, fallback)
  81148. return klass if klass
  81149. end
  81150. nil
  81151. end
  81152. # Receives namespaces in an array and tries to find matching generators
  81153. # in the load path.
  81154. def self.lookup(namespaces) #:nodoc:
  81155. paths = namespaces_to_paths(namespaces)
  81156. paths.each do |raw_path|
  81157. ["rails/generators", "generators"].each do |base|
  81158. path = "#{base}/#{raw_path}_generator"
  81159. begin
  81160. require path
  81161. return
  81162. rescue LoadError => e
  81163. raise unless e.message =~ /#{Regexp.escape(path)}$/
  81164. rescue Exception => e
  81165. warn "[WARNING] Could not load generator #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
  81166. end
  81167. end
  81168. end
  81169. end
  81170. # This will try to load any generator in the load path to show in help.
  81171. def self.lookup! #:nodoc:
  81172. $LOAD_PATH.each do |base|
  81173. Dir[File.join(base, "{rails/generators,generators}", "**", "*_generator.rb")].each do |path|
  81174. begin
  81175. path = path.sub("#{base}/", "")
  81176. require path
  81177. rescue Exception
  81178. # No problem
  81179. end
  81180. end
  81181. end
  81182. end
  81183. # Convert namespaces to paths by replacing ":" for "/" and adding
  81184. # an extra lookup. For example, "rails:model" should be searched
  81185. # in both: "rails/model/model_generator" and "rails/model_generator".
  81186. def self.namespaces_to_paths(namespaces) #:nodoc:
  81187. paths = []
  81188. namespaces.each do |namespace|
  81189. pieces = namespace.split(":")
  81190. paths << pieces.dup.push(pieces.last).join("/")
  81191. paths << pieces.join("/")
  81192. end
  81193. paths.uniq!
  81194. paths
  81195. end
  81196. end
  81197. end
  81198. require "cgi"
  81199. module Rails
  81200. module Info
  81201. mattr_accessor :properties
  81202. class << (@@properties = [])
  81203. def names
  81204. map {|val| val.first }
  81205. end
  81206. def value_for(property_name)
  81207. if property = assoc(property_name)
  81208. property.last
  81209. end
  81210. end
  81211. end
  81212. class << self #:nodoc:
  81213. def property(name, value = nil)
  81214. value ||= yield
  81215. properties << [name, value] if value
  81216. rescue Exception
  81217. end
  81218. def frameworks
  81219. %w( active_record action_pack action_mailer active_support )
  81220. end
  81221. def framework_version(framework)
  81222. if Object.const_defined?(framework.classify)
  81223. require "#{framework}/version"
  81224. "#{framework.classify}::VERSION::STRING".constantize
  81225. end
  81226. end
  81227. def to_s
  81228. column_width = properties.names.map {|name| name.length}.max
  81229. info = properties.map do |name, value|
  81230. value = value.join(", ") if value.is_a?(Array)
  81231. "%-#{column_width}s %s" % [name, value]
  81232. end
  81233. info.unshift "About your application's environment"
  81234. info * "\n"
  81235. end
  81236. alias inspect to_s
  81237. def to_html
  81238. '<table>'.tap do |table|
  81239. properties.each do |(name, value)|
  81240. table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
  81241. formatted_value = if value.kind_of?(Array)
  81242. "<ul>" + value.map { |v| "<li>#{CGI.escapeHTML(v.to_s)}</li>" }.join + "</ul>"
  81243. else
  81244. CGI.escapeHTML(value.to_s)
  81245. end
  81246. table << %(<td class="value">#{formatted_value}</td></tr>)
  81247. end
  81248. table << '</table>'
  81249. end
  81250. end
  81251. end
  81252. # The Ruby version and platform, e.g. "1.8.2 (powerpc-darwin8.2.0)".
  81253. property 'Ruby version', "#{RUBY_VERSION} (#{RUBY_PLATFORM})"
  81254. # The RubyGems version, if it's installed.
  81255. property 'RubyGems version' do
  81256. Gem::RubyGemsVersion
  81257. end
  81258. property 'Rack version' do
  81259. ::Rack.release
  81260. end
  81261. # The Rails version.
  81262. property 'Rails version' do
  81263. Rails::VERSION::STRING
  81264. end
  81265. property 'JavaScript Runtime' do
  81266. ExecJS.runtime.name
  81267. end
  81268. # Versions of each Rails framework (Active Record, Action Pack,
  81269. # Action Mailer, and Active Support).
  81270. frameworks.each do |framework|
  81271. property "#{framework.titlecase} version" do
  81272. framework_version(framework)
  81273. end
  81274. end
  81275. property 'Middleware' do
  81276. Rails.configuration.middleware.map(&:inspect)
  81277. end
  81278. # The application's location on the filesystem.
  81279. property 'Application root' do
  81280. File.expand_path(Rails.root)
  81281. end
  81282. # The current Rails environment (development, test, or production).
  81283. property 'Environment' do
  81284. Rails.env
  81285. end
  81286. # The name of the database adapter for the current environment.
  81287. property 'Database adapter' do
  81288. ActiveRecord::Base.configurations[Rails.env]['adapter']
  81289. end
  81290. property 'Database schema version' do
  81291. ActiveRecord::Migrator.current_version rescue nil
  81292. end
  81293. end
  81294. end
  81295. require 'action_dispatch/routing/inspector'
  81296. class Rails::InfoController < ActionController::Base # :nodoc:
  81297. self.view_paths = File.expand_path('../templates', __FILE__)
  81298. prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
  81299. layout -> { request.xhr? ? nil : 'application' }
  81300. before_filter :require_local!
  81301. def index
  81302. redirect_to action: :routes
  81303. end
  81304. def properties
  81305. @info = Rails::Info.to_html
  81306. end
  81307. def routes
  81308. @routes_inspector = ActionDispatch::Routing::RoutesInspector.new(_routes.routes)
  81309. end
  81310. protected
  81311. def require_local!
  81312. unless local_request?
  81313. render text: '<p>For security purposes, this information is only available to local requests.</p>', status: :forbidden
  81314. end
  81315. end
  81316. def local_request?
  81317. Rails.application.config.consider_all_requests_local || request.local?
  81318. end
  81319. end
  81320. require 'tsort'
  81321. module Rails
  81322. module Initializable
  81323. def self.included(base) #:nodoc:
  81324. base.extend ClassMethods
  81325. end
  81326. class Initializer
  81327. attr_reader :name, :block
  81328. def initialize(name, context, options, &block)
  81329. options[:group] ||= :default
  81330. @name, @context, @options, @block = name, context, options, block
  81331. end
  81332. def before
  81333. @options[:before]
  81334. end
  81335. def after
  81336. @options[:after]
  81337. end
  81338. def belongs_to?(group)
  81339. @options[:group] == group || @options[:group] == :all
  81340. end
  81341. def run(*args)
  81342. @context.instance_exec(*args, &block)
  81343. end
  81344. def bind(context)
  81345. return self if @context
  81346. Initializer.new(@name, context, @options, &block)
  81347. end
  81348. end
  81349. class Collection < Array
  81350. include TSort
  81351. alias :tsort_each_node :each
  81352. def tsort_each_child(initializer, &block)
  81353. select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block)
  81354. end
  81355. def +(other)
  81356. Collection.new(to_a + other.to_a)
  81357. end
  81358. end
  81359. def run_initializers(group=:default, *args)
  81360. return if instance_variable_defined?(:@ran)
  81361. initializers.tsort_each do |initializer|
  81362. initializer.run(*args) if initializer.belongs_to?(group)
  81363. end
  81364. @ran = true
  81365. end
  81366. def initializers
  81367. @initializers ||= self.class.initializers_for(self)
  81368. end
  81369. module ClassMethods
  81370. def initializers
  81371. @initializers ||= Collection.new
  81372. end
  81373. def initializers_chain
  81374. initializers = Collection.new
  81375. ancestors.reverse_each do |klass|
  81376. next unless klass.respond_to?(:initializers)
  81377. initializers = initializers + klass.initializers
  81378. end
  81379. initializers
  81380. end
  81381. def initializers_for(binding)
  81382. Collection.new(initializers_chain.map { |i| i.bind(binding) })
  81383. end
  81384. def initializer(name, opts = {}, &blk)
  81385. raise ArgumentError, "A block must be passed when defining an initializer" unless blk
  81386. opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
  81387. initializers << Initializer.new(name, nil, opts, &blk)
  81388. end
  81389. end
  81390. end
  81391. end
  81392. module Rails
  81393. module Paths
  81394. # This object is an extended hash that behaves as root of the <tt>Rails::Paths</tt> system.
  81395. # It allows you to collect information about how you want to structure your application
  81396. # paths by a Hash like API. It requires you to give a physical path on initialization.
  81397. #
  81398. # root = Root.new "/rails"
  81399. # root.add "app/controllers", autoload: true
  81400. #
  81401. # The command above creates a new root object and add "app/controllers" as a path.
  81402. # This means we can get a <tt>Rails::Paths::Path</tt> object back like below:
  81403. #
  81404. # path = root["app/controllers"]
  81405. # path.autoload? # => true
  81406. # path.is_a?(Rails::Paths::Path) # => true
  81407. #
  81408. # The +Path+ object is simply an enumerable and allows you to easily add extra paths:
  81409. #
  81410. # path.is_a?(Enumerable) # => true
  81411. # path.to_ary.inspect # => ["app/controllers"]
  81412. #
  81413. # path << "lib/controllers"
  81414. # path.to_ary.inspect # => ["app/controllers", "lib/controllers"]
  81415. #
  81416. # Notice that when you add a path using +add+, the path object created already
  81417. # contains the path with the same path value given to +add+. In some situations,
  81418. # you may not want this behavior, so you can give :with as option.
  81419. #
  81420. # root.add "config/routes", with: "config/routes.rb"
  81421. # root["config/routes"].inspect # => ["config/routes.rb"]
  81422. #
  81423. # The +add+ method accepts the following options as arguments:
  81424. # autoload, autoload_once and glob.
  81425. #
  81426. # Finally, the +Path+ object also provides a few helpers:
  81427. #
  81428. # root = Root.new "/rails"
  81429. # root.add "app/controllers"
  81430. #
  81431. # root["app/controllers"].expanded # => ["/rails/app/controllers"]
  81432. # root["app/controllers"].existent # => ["/rails/app/controllers"]
  81433. #
  81434. # Check the <tt>Rails::Paths::Path</tt> documentation for more information.
  81435. class Root
  81436. attr_accessor :path
  81437. def initialize(path)
  81438. @current = nil
  81439. @path = path
  81440. @root = {}
  81441. end
  81442. def []=(path, value)
  81443. glob = self[path] ? self[path].glob : nil
  81444. add(path, with: value, glob: glob)
  81445. end
  81446. def add(path, options = {})
  81447. with = Array(options.fetch(:with, path))
  81448. @root[path] = Path.new(self, path, with, options)
  81449. end
  81450. def [](path)
  81451. @root[path]
  81452. end
  81453. def values
  81454. @root.values
  81455. end
  81456. def keys
  81457. @root.keys
  81458. end
  81459. def values_at(*list)
  81460. @root.values_at(*list)
  81461. end
  81462. def all_paths
  81463. values.tap { |v| v.uniq! }
  81464. end
  81465. def autoload_once
  81466. filter_by(:autoload_once?)
  81467. end
  81468. def eager_load
  81469. ActiveSupport::Deprecation.warn "eager_load is deprecated and all autoload_paths are now eagerly loaded."
  81470. filter_by(:autoload?)
  81471. end
  81472. def autoload_paths
  81473. filter_by(:autoload?)
  81474. end
  81475. def load_paths
  81476. filter_by(:load_path?)
  81477. end
  81478. protected
  81479. def filter_by(constraint)
  81480. all = []
  81481. all_paths.each do |path|
  81482. if path.send(constraint)
  81483. paths = path.existent
  81484. paths -= path.children.map { |p| p.send(constraint) ? [] : p.existent }.flatten
  81485. all.concat(paths)
  81486. end
  81487. end
  81488. all.uniq!
  81489. all
  81490. end
  81491. end
  81492. class Path
  81493. include Enumerable
  81494. attr_accessor :glob
  81495. def initialize(root, current, paths, options = {})
  81496. @paths = paths
  81497. @current = current
  81498. @root = root
  81499. @glob = options[:glob]
  81500. options[:autoload_once] ? autoload_once! : skip_autoload_once!
  81501. options[:autoload] ? autoload! : skip_autoload!
  81502. options[:load_path] ? load_path! : skip_load_path!
  81503. if !options.key?(:autoload) && options.key?(:eager_load)
  81504. ActiveSupport::Deprecation.warn "the :eager_load option is deprecated and all :autoload paths are now eagerly loaded."
  81505. options[:eager_load] ? autoload! : skip_autoload!
  81506. end
  81507. end
  81508. def children
  81509. keys = @root.keys.select { |k| k.include?(@current) }
  81510. keys.delete(@current)
  81511. @root.values_at(*keys.sort)
  81512. end
  81513. def first
  81514. expanded.first
  81515. end
  81516. def last
  81517. expanded.last
  81518. end
  81519. %w(autoload_once autoload load_path).each do |m|
  81520. class_eval <<-RUBY, __FILE__, __LINE__ + 1
  81521. def #{m}! # def autoload!
  81522. @#{m} = true # @autoload = true
  81523. end # end
  81524. #
  81525. def skip_#{m}! # def skip_autoload!
  81526. @#{m} = false # @autoload = false
  81527. end # end
  81528. #
  81529. def #{m}? # def autoload?
  81530. @#{m} # @autoload
  81531. end # end
  81532. RUBY
  81533. end
  81534. def eager_load!
  81535. ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
  81536. autoload!
  81537. end
  81538. def skip_eager_load!
  81539. ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
  81540. skip_autoload!
  81541. end
  81542. def eager_load?
  81543. ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
  81544. autoload?
  81545. end
  81546. def each(&block)
  81547. @paths.each(&block)
  81548. end
  81549. def <<(path)
  81550. @paths << path
  81551. end
  81552. alias :push :<<
  81553. def concat(paths)
  81554. @paths.concat paths
  81555. end
  81556. def unshift(path)
  81557. @paths.unshift path
  81558. end
  81559. def to_ary
  81560. @paths
  81561. end
  81562. # Expands all paths against the root and return all unique values.
  81563. def expanded
  81564. raise "You need to set a path root" unless @root.path
  81565. result = []
  81566. each do |p|
  81567. path = File.expand_path(p, @root.path)
  81568. if @glob && File.directory?(path)
  81569. Dir.chdir(path) do
  81570. result.concat(Dir.glob(@glob).map { |file| File.join path, file }.sort)
  81571. end
  81572. else
  81573. result << path
  81574. end
  81575. end
  81576. result.uniq!
  81577. result
  81578. end
  81579. # Returns all expanded paths but only if they exist in the filesystem.
  81580. def existent
  81581. expanded.select { |f| File.exists?(f) }
  81582. end
  81583. def existent_directories
  81584. expanded.select { |d| File.directory?(d) }
  81585. end
  81586. alias to_a expanded
  81587. end
  81588. end
  81589. end
  81590. module Rails
  81591. module Rack
  81592. class Debugger
  81593. def initialize(app)
  81594. @app = app
  81595. ARGV.clear # clear ARGV so that rails server options aren't passed to IRB
  81596. require 'debugger'
  81597. ::Debugger.start
  81598. ::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings)
  81599. puts "=> Debugger enabled"
  81600. rescue LoadError
  81601. puts "You're missing the 'debugger' gem. Add it to your Gemfile, bundle, and try again."
  81602. exit
  81603. end
  81604. def call(env)
  81605. @app.call(env)
  81606. end
  81607. end
  81608. end
  81609. end
  81610. module Rails
  81611. module Rack
  81612. class LogTailer
  81613. def initialize(app, log = nil)
  81614. @app = app
  81615. path = Pathname.new(log || "#{::File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath
  81616. @cursor = @file = nil
  81617. if ::File.exists?(path)
  81618. @cursor = ::File.size(path)
  81619. @file = ::File.open(path, 'r')
  81620. end
  81621. end
  81622. def call(env)
  81623. response = @app.call(env)
  81624. tail!
  81625. response
  81626. end
  81627. def tail!
  81628. return unless @cursor
  81629. @file.seek @cursor
  81630. unless @file.eof?
  81631. contents = @file.read
  81632. @cursor = @file.tell
  81633. $stdout.print contents
  81634. end
  81635. end
  81636. end
  81637. end
  81638. end
  81639. require 'active_support/core_ext/time/conversions'
  81640. require 'active_support/core_ext/object/blank'
  81641. require 'active_support/log_subscriber'
  81642. require 'action_dispatch/http/request'
  81643. require 'rack/body_proxy'
  81644. module Rails
  81645. module Rack
  81646. # Sets log tags, logs the request, calls the app, and flushes the logs.
  81647. class Logger < ActiveSupport::LogSubscriber
  81648. def initialize(app, taggers = nil)
  81649. @app = app
  81650. @taggers = taggers || []
  81651. @instrumenter = ActiveSupport::Notifications.instrumenter
  81652. end
  81653. def call(env)
  81654. request = ActionDispatch::Request.new(env)
  81655. if logger.respond_to?(:tagged)
  81656. logger.tagged(compute_tags(request)) { call_app(request, env) }
  81657. else
  81658. call_app(request, env)
  81659. end
  81660. end
  81661. protected
  81662. def call_app(request, env)
  81663. # Put some space between requests in development logs.
  81664. if development?
  81665. logger.debug ''
  81666. logger.debug ''
  81667. end
  81668. @instrumenter.start 'action_dispatch.request', request: request
  81669. logger.info started_request_message(request)
  81670. resp = @app.call(env)
  81671. resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
  81672. resp
  81673. rescue
  81674. finish(request)
  81675. raise
  81676. ensure
  81677. ActiveSupport::LogSubscriber.flush_all!
  81678. end
  81679. # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
  81680. def started_request_message(request)
  81681. 'Started %s "%s" for %s at %s' % [
  81682. request.request_method,
  81683. request.filtered_path,
  81684. request.ip,
  81685. Time.now.to_default_s ]
  81686. end
  81687. def compute_tags(request)
  81688. @taggers.collect do |tag|
  81689. case tag
  81690. when Proc
  81691. tag.call(request)
  81692. when Symbol
  81693. request.send(tag)
  81694. else
  81695. tag
  81696. end
  81697. end
  81698. end
  81699. private
  81700. def finish(request)
  81701. @instrumenter.finish 'action_dispatch.request', request: request
  81702. end
  81703. def development?
  81704. Rails.env.development?
  81705. end
  81706. def logger
  81707. Rails.logger
  81708. end
  81709. end
  81710. end
  81711. end
  81712. module Rails
  81713. module Rack
  81714. autoload :Debugger, "rails/rack/debugger"
  81715. autoload :Logger, "rails/rack/logger"
  81716. autoload :LogTailer, "rails/rack/log_tailer"
  81717. end
  81718. end
  81719. require 'active_support/concern'
  81720. module Rails
  81721. class Railtie
  81722. module Configurable
  81723. extend ActiveSupport::Concern
  81724. module ClassMethods
  81725. delegate :config, to: :instance
  81726. def inherited(base)
  81727. raise "You cannot inherit from a #{self.superclass.name} child"
  81728. end
  81729. def instance
  81730. @instance ||= new
  81731. end
  81732. def respond_to?(*args)
  81733. super || instance.respond_to?(*args)
  81734. end
  81735. def configure(&block)
  81736. class_eval(&block)
  81737. end
  81738. protected
  81739. def method_missing(*args, &block)
  81740. instance.send(*args, &block)
  81741. end
  81742. end
  81743. end
  81744. end
  81745. end
  81746. require 'rails/configuration'
  81747. module Rails
  81748. class Railtie
  81749. class Configuration
  81750. def initialize
  81751. @@options ||= {}
  81752. end
  81753. # Expose the eager_load_namespaces at "module" level for convenience.
  81754. def self.eager_load_namespaces #:nodoc:
  81755. @@eager_load_namespaces ||= []
  81756. end
  81757. # All namespaces that are eager loaded
  81758. def eager_load_namespaces
  81759. @@eager_load_namespaces ||= []
  81760. end
  81761. # Add files that should be watched for change.
  81762. def watchable_files
  81763. @@watchable_files ||= []
  81764. end
  81765. # Add directories that should be watched for change.
  81766. # The key of the hashes should be directories and the values should
  81767. # be an array of extensions to match in each directory.
  81768. def watchable_dirs
  81769. @@watchable_dirs ||= {}
  81770. end
  81771. # This allows you to modify the application's middlewares from Engines.
  81772. #
  81773. # All operations you run on the app_middleware will be replayed on the
  81774. # application once it is defined and the default_middlewares are
  81775. # created
  81776. def app_middleware
  81777. @@app_middleware ||= Rails::Configuration::MiddlewareStackProxy.new
  81778. end
  81779. # This allows you to modify application's generators from Railties.
  81780. #
  81781. # Values set on app_generators will become defaults for application, unless
  81782. # application overwrites them.
  81783. def app_generators
  81784. @@app_generators ||= Rails::Configuration::Generators.new
  81785. yield(@@app_generators) if block_given?
  81786. @@app_generators
  81787. end
  81788. # First configurable block to run. Called before any initializers are run.
  81789. def before_configuration(&block)
  81790. ActiveSupport.on_load(:before_configuration, yield: true, &block)
  81791. end
  81792. # Third configurable block to run. Does not run if +config.cache_classes+
  81793. # set to false.
  81794. def before_eager_load(&block)
  81795. ActiveSupport.on_load(:before_eager_load, yield: true, &block)
  81796. end
  81797. # Second configurable block to run. Called before frameworks initialize.
  81798. def before_initialize(&block)
  81799. ActiveSupport.on_load(:before_initialize, yield: true, &block)
  81800. end
  81801. # Last configurable block to run. Called after frameworks initialize.
  81802. def after_initialize(&block)
  81803. ActiveSupport.on_load(:after_initialize, yield: true, &block)
  81804. end
  81805. # Array of callbacks defined by #to_prepare.
  81806. def to_prepare_blocks
  81807. @@to_prepare_blocks ||= []
  81808. end
  81809. # Defines generic callbacks to run before #after_initialize. Useful for
  81810. # Rails::Railtie subclasses.
  81811. def to_prepare(&blk)
  81812. to_prepare_blocks << blk if blk
  81813. end
  81814. def respond_to?(name)
  81815. super || @@options.key?(name.to_sym)
  81816. end
  81817. private
  81818. def method_missing(name, *args, &blk)
  81819. if name.to_s =~ /=$/
  81820. @@options[$`.to_sym] = args.first
  81821. elsif @@options.key?(name)
  81822. @@options[name]
  81823. else
  81824. super
  81825. end
  81826. end
  81827. end
  81828. end
  81829. end
  81830. require 'rails/initializable'
  81831. require 'rails/configuration'
  81832. require 'active_support/inflector'
  81833. require 'active_support/core_ext/module/introspection'
  81834. require 'active_support/core_ext/module/delegation'
  81835. module Rails
  81836. # Railtie is the core of the Rails framework and provides several hooks to extend
  81837. # Rails and/or modify the initialization process.
  81838. #
  81839. # Every major component of Rails (Action Mailer, Action Controller,
  81840. # Action View and Active Record) is a Railtie. Each of
  81841. # them is responsible for their own initialization. This makes Rails itself
  81842. # absent of any component hooks, allowing other components to be used in
  81843. # place of any of the Rails defaults.
  81844. #
  81845. # Developing a Rails extension does _not_ require any implementation of
  81846. # Railtie, but if you need to interact with the Rails framework during
  81847. # or after boot, then Railtie is needed.
  81848. #
  81849. # For example, an extension doing any of the following would require Railtie:
  81850. #
  81851. # * creating initializers
  81852. # * configuring a Rails framework for the application, like setting a generator
  81853. # * +adding config.*+ keys to the environment
  81854. # * setting up a subscriber with ActiveSupport::Notifications
  81855. # * adding rake tasks
  81856. #
  81857. # == Creating your Railtie
  81858. #
  81859. # To extend Rails using Railtie, create a Railtie class which inherits
  81860. # from Rails::Railtie within your extension's namespace. This class must be
  81861. # loaded during the Rails boot process.
  81862. #
  81863. # The following example demonstrates an extension which can be used with or without Rails.
  81864. #
  81865. # # lib/my_gem/railtie.rb
  81866. # module MyGem
  81867. # class Railtie < Rails::Railtie
  81868. # end
  81869. # end
  81870. #
  81871. # # lib/my_gem.rb
  81872. # require 'my_gem/railtie' if defined?(Rails)
  81873. #
  81874. # == Initializers
  81875. #
  81876. # To add an initialization step from your Railtie to Rails boot process, you just need
  81877. # to create an initializer block:
  81878. #
  81879. # class MyRailtie < Rails::Railtie
  81880. # initializer "my_railtie.configure_rails_initialization" do
  81881. # # some initialization behavior
  81882. # end
  81883. # end
  81884. #
  81885. # If specified, the block can also receive the application object, in case you
  81886. # need to access some application specific configuration, like middleware:
  81887. #
  81888. # class MyRailtie < Rails::Railtie
  81889. # initializer "my_railtie.configure_rails_initialization" do |app|
  81890. # app.middleware.use MyRailtie::Middleware
  81891. # end
  81892. # end
  81893. #
  81894. # Finally, you can also pass :before and :after as option to initializer, in case
  81895. # you want to couple it with a specific step in the initialization process.
  81896. #
  81897. # == Configuration
  81898. #
  81899. # Inside the Railtie class, you can access a config object which contains configuration
  81900. # shared by all railties and the application:
  81901. #
  81902. # class MyRailtie < Rails::Railtie
  81903. # # Customize the ORM
  81904. # config.app_generators.orm :my_railtie_orm
  81905. #
  81906. # # Add a to_prepare block which is executed once in production
  81907. # # and before each request in development
  81908. # config.to_prepare do
  81909. # MyRailtie.setup!
  81910. # end
  81911. # end
  81912. #
  81913. # == Loading rake tasks and generators
  81914. #
  81915. # If your railtie has rake tasks, you can tell Rails to load them through the method
  81916. # rake_tasks:
  81917. #
  81918. # class MyRailtie < Rails::Railtie
  81919. # rake_tasks do
  81920. # load "path/to/my_railtie.tasks"
  81921. # end
  81922. # end
  81923. #
  81924. # By default, Rails load generators from your load path. However, if you want to place
  81925. # your generators at a different location, you can specify in your Railtie a block which
  81926. # will load them during normal generators lookup:
  81927. #
  81928. # class MyRailtie < Rails::Railtie
  81929. # generators do
  81930. # require "path/to/my_railtie_generator"
  81931. # end
  81932. # end
  81933. #
  81934. # == Application and Engine
  81935. #
  81936. # A Rails::Engine is nothing more than a Railtie with some initializers already set.
  81937. # And since Rails::Application is an engine, the same configuration described here
  81938. # can be used in both.
  81939. #
  81940. # Be sure to look at the documentation of those specific classes for more information.
  81941. #
  81942. class Railtie
  81943. autoload :Configurable, "rails/railtie/configurable"
  81944. autoload :Configuration, "rails/railtie/configuration"
  81945. include Initializable
  81946. ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Engine Rails::Application)
  81947. class << self
  81948. private :new
  81949. def subclasses
  81950. @subclasses ||= []
  81951. end
  81952. def inherited(base)
  81953. unless base.abstract_railtie?
  81954. base.send(:include, Railtie::Configurable)
  81955. subclasses << base
  81956. end
  81957. end
  81958. def rake_tasks(&blk)
  81959. @rake_tasks ||= []
  81960. @rake_tasks << blk if blk
  81961. @rake_tasks
  81962. end
  81963. def console(&blk)
  81964. @load_console ||= []
  81965. @load_console << blk if blk
  81966. @load_console
  81967. end
  81968. def runner(&blk)
  81969. @load_runner ||= []
  81970. @load_runner << blk if blk
  81971. @load_runner
  81972. end
  81973. def generators(&blk)
  81974. @generators ||= []
  81975. @generators << blk if blk
  81976. @generators
  81977. end
  81978. def abstract_railtie?
  81979. ABSTRACT_RAILTIES.include?(name)
  81980. end
  81981. def railtie_name(name = nil)
  81982. @railtie_name = name.to_s if name
  81983. @railtie_name ||= generate_railtie_name(self.name)
  81984. end
  81985. protected
  81986. def generate_railtie_name(class_or_module)
  81987. ActiveSupport::Inflector.underscore(class_or_module).tr("/", "_")
  81988. end
  81989. end
  81990. delegate :railtie_name, to: :class
  81991. def config
  81992. @config ||= Railtie::Configuration.new
  81993. end
  81994. def railtie_namespace
  81995. @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
  81996. end
  81997. protected
  81998. def run_console_blocks(app) #:nodoc:
  81999. self.class.console.each { |block| block.call(app) }
  82000. end
  82001. def run_generators_blocks(app) #:nodoc:
  82002. self.class.generators.each { |block| block.call(app) }
  82003. end
  82004. def run_runner_blocks(app) #:nodoc:
  82005. self.class.runner.each { |block| block.call(app) }
  82006. end
  82007. def run_tasks_blocks(app) #:nodoc:
  82008. extend Rake::DSL
  82009. self.class.rake_tasks.each { |block| instance_exec(app, &block) }
  82010. # Load also tasks from all superclasses
  82011. klass = self.class.superclass
  82012. while klass.respond_to?(:rake_tasks)
  82013. klass.rake_tasks.each { |t| instance_exec(app, &t) }
  82014. klass = klass.superclass
  82015. end
  82016. end
  82017. end
  82018. end
  82019. if RUBY_VERSION < '1.9.3'
  82020. desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
  82021. abort <<-end_message
  82022. Rails 4 prefers to run on Ruby 2.0.
  82023. You're running
  82024. #{desc}
  82025. Please upgrade to Ruby 1.9.3 or newer to continue.
  82026. end_message
  82027. end
  82028. require 'prof'
  82029. module Prof #:nodoc:
  82030. # Adapted from Shugo Maeda's unprof.rb
  82031. def self.print_profile(results, io = $stderr)
  82032. total = results.detect { |i|
  82033. i.method_class.nil? && i.method_id == :"#toplevel"
  82034. }.total_time
  82035. total = 0.001 if total < 0.001
  82036. io.puts " %% cumulative self self total"
  82037. io.puts " time seconds seconds calls ms/call ms/call name"
  82038. sum = 0.0
  82039. results.each do |r|
  82040. sum += r.self_time
  82041. name = if r.method_class.nil?
  82042. r.method_id.to_s
  82043. elsif r.method_class.is_a?(Class)
  82044. "#{r.method_class}##{r.method_id}"
  82045. else
  82046. "#{r.method_class}.#{r.method_id}"
  82047. end
  82048. io.printf "%6.2f %8.3f %8.3f %8d %8.2f %8.2f %s\n",
  82049. r.self_time / total * 100,
  82050. sum,
  82051. r.self_time,
  82052. r.count,
  82053. r.self_time * 1000 / r.count,
  82054. r.total_time * 1000 / r.count,
  82055. name
  82056. end
  82057. end
  82058. end
  82059. # Implements the logic behind the rake tasks for annotations like
  82060. #
  82061. # rake notes
  82062. # rake notes:optimize
  82063. #
  82064. # and friends. See <tt>rake -T notes</tt> and <tt>railties/lib/tasks/annotations.rake</tt>.
  82065. #
  82066. # Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
  82067. # represent the line where the annotation lives, its tag, and its text. Note
  82068. # the filename is not stored.
  82069. #
  82070. # Annotations are looked for in comments and modulus whitespace they have to
  82071. # start with the tag optionally followed by a colon. Everything up to the end
  82072. # of the line (or closing ERB comment tag) is considered to be their text.
  82073. class SourceAnnotationExtractor
  82074. class Annotation < Struct.new(:line, :tag, :text)
  82075. def self.directories
  82076. @@directories ||= %w(app config db lib test) + (ENV['SOURCE_ANNOTATION_DIRECTORIES'] || '').split(',')
  82077. end
  82078. # Returns a representation of the annotation that looks like this:
  82079. #
  82080. # [126] [TODO] This algorithm is simple and clearly correct, make it faster.
  82081. #
  82082. # If +options+ has a flag <tt>:tag</tt> the tag is shown as in the example above.
  82083. # Otherwise the string contains just line and text.
  82084. def to_s(options={})
  82085. s = "[#{line.to_s.rjust(options[:indent])}] "
  82086. s << "[#{tag}] " if options[:tag]
  82087. s << text
  82088. end
  82089. end
  82090. # Prints all annotations with tag +tag+ under the root directories +app+,
  82091. # +config+, +db+, +lib+, and +test+ (recursively).
  82092. #
  82093. # Additional directories may be added using a comma-delimited list set using
  82094. # <tt>ENV['SOURCE_ANNOTATION_DIRECTORIES']</tt>.
  82095. #
  82096. # Directories may also be explicitly set using the <tt>:dirs</tt> key in +options+.
  82097. #
  82098. # SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
  82099. #
  82100. # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+.
  82101. #
  82102. # See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
  82103. #
  82104. # This class method is the single entry point for the rake tasks.
  82105. def self.enumerate(tag, options={})
  82106. extractor = new(tag)
  82107. dirs = options.delete(:dirs) || Annotation.directories
  82108. extractor.display(extractor.find(dirs), options)
  82109. end
  82110. attr_reader :tag
  82111. def initialize(tag)
  82112. @tag = tag
  82113. end
  82114. # Returns a hash that maps filenames under +dirs+ (recursively) to arrays
  82115. # with their annotations.
  82116. def find(dirs)
  82117. dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
  82118. end
  82119. # Returns a hash that maps filenames under +dir+ (recursively) to arrays
  82120. # with their annotations. Only files with annotations are included. Files
  82121. # with extension +.builder+, +.rb+, +.erb+, +.haml+, +.slim+, +.css+,
  82122. # +.scss+, +.js+, +.coffee+, and +.rake+
  82123. # are taken into account.
  82124. def find_in(dir)
  82125. results = {}
  82126. Dir.glob("#{dir}/*") do |item|
  82127. next if File.basename(item)[0] == ?.
  82128. if File.directory?(item)
  82129. results.update(find_in(item))
  82130. else
  82131. pattern =
  82132. case item
  82133. when /\.(builder|rb|coffee|rake)$/
  82134. /#\s*(#{tag}):?\s*(.*)$/
  82135. when /\.(css|scss|js)$/
  82136. /\/\/\s*(#{tag}):?\s*(.*)$/
  82137. when /\.erb$/
  82138. /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/
  82139. when /\.haml$/
  82140. /-\s*#\s*(#{tag}):?\s*(.*)$/
  82141. when /\.slim$/
  82142. /\/\s*\s*(#{tag}):?\s*(.*)$/
  82143. else nil
  82144. end
  82145. results.update(extract_annotations_from(item, pattern)) if pattern
  82146. end
  82147. end
  82148. results
  82149. end
  82150. # If +file+ is the filename of a file that contains annotations this method returns
  82151. # a hash with a single entry that maps +file+ to an array of its annotations.
  82152. # Otherwise it returns an empty hash.
  82153. def extract_annotations_from(file, pattern)
  82154. lineno = 0
  82155. result = File.readlines(file).inject([]) do |list, line|
  82156. lineno += 1
  82157. next list unless line =~ pattern
  82158. list << Annotation.new(lineno, $1, $2)
  82159. end
  82160. result.empty? ? {} : { file => result }
  82161. end
  82162. # Prints the mapping from filenames to annotations in +results+ ordered by filename.
  82163. # The +options+ hash is passed to each annotation's +to_s+.
  82164. def display(results, options={})
  82165. options[:indent] = results.map { |f, a| a.map(&:line) }.flatten.max.to_s.size
  82166. results.keys.sort.each do |file|
  82167. puts "#{file}:"
  82168. results[file].each do |note|
  82169. puts " * #{note.to_s(options)}"
  82170. end
  82171. puts
  82172. end
  82173. end
  82174. end
  82175. $VERBOSE = nil
  82176. # Load Rails rakefile extensions
  82177. %w(
  82178. annotations
  82179. documentation
  82180. framework
  82181. log
  82182. middleware
  82183. misc
  82184. routes
  82185. statistics
  82186. tmp
  82187. ).each do |task|
  82188. load "rails/tasks/#{task}.rake"
  82189. end
  82190. # Make double-sure the RAILS_ENV is not set to production,
  82191. # so fixtures aren't loaded into that environment
  82192. abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
  82193. require 'active_support/testing/autorun'
  82194. require 'active_support/test_case'
  82195. require 'action_controller/test_case'
  82196. require 'action_dispatch/testing/integration'
  82197. # Config Rails backtrace in tests.
  82198. require 'rails/backtrace_cleaner'
  82199. MiniTest.backtrace_filter = Rails.backtrace_cleaner
  82200. if defined?(ActiveRecord::Base)
  82201. class ActiveSupport::TestCase
  82202. include ActiveRecord::TestFixtures
  82203. self.fixture_path = "#{Rails.root}/test/fixtures/"
  82204. end
  82205. ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
  82206. def create_fixtures(*fixture_set_names, &block)
  82207. FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, {}, &block)
  82208. end
  82209. end
  82210. class ActionController::TestCase
  82211. setup do
  82212. @routes = Rails.application.routes
  82213. end
  82214. end
  82215. class ActionDispatch::IntegrationTest
  82216. setup do
  82217. @routes = Rails.application.routes
  82218. end
  82219. end
  82220. module Rails
  82221. class TestUnitRailtie < Rails::Railtie
  82222. config.app_generators do |c|
  82223. c.test_framework :test_unit, fixture: true,
  82224. fixture_replacement: nil
  82225. c.integration_tool :test_unit
  82226. end
  82227. rake_tasks do
  82228. load "rails/test_unit/testing.rake"
  82229. end
  82230. end
  82231. end
  82232. module Rails
  82233. # Silence the default description to cut down on `rake -T` noise.
  82234. class SubTestTask < Rake::TestTask
  82235. def desc(string)
  82236. # Ignore the description.
  82237. end
  82238. end
  82239. end
  82240. module Rails
  82241. module VERSION #:nodoc:
  82242. MAJOR = 4
  82243. MINOR = 0
  82244. TINY = 0
  82245. PRE = "beta"
  82246. STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
  82247. end
  82248. end
  82249. class Rails::WelcomeController < ActionController::Base # :nodoc:
  82250. self.view_paths = File.expand_path('../templates', __FILE__)
  82251. layout nil
  82252. def index
  82253. end
  82254. end
  82255. require 'rails/ruby_version_check'
  82256. require 'pathname'
  82257. require 'active_support'
  82258. require 'active_support/core_ext/kernel/reporting'
  82259. require 'active_support/core_ext/array/extract_options'
  82260. require 'rails/application'
  82261. require 'rails/version'
  82262. require 'rails/deprecation'
  82263. require 'active_support/railtie'
  82264. require 'action_dispatch/railtie'
  82265. # For Ruby 1.9, UTF-8 is the default internal and external encoding.
  82266. silence_warnings do
  82267. Encoding.default_external = Encoding::UTF_8
  82268. Encoding.default_internal = Encoding::UTF_8
  82269. end
  82270. module Rails
  82271. autoload :Info, 'rails/info'
  82272. autoload :InfoController, 'rails/info_controller'
  82273. autoload :WelcomeController, 'rails/welcome_controller'
  82274. class << self
  82275. attr_accessor :application, :cache, :logger
  82276. # The Configuration instance used to configure the Rails environment
  82277. def configuration
  82278. application.config
  82279. end
  82280. def initialize!
  82281. application.initialize!
  82282. end
  82283. def initialized?
  82284. application.initialized?
  82285. end
  82286. def backtrace_cleaner
  82287. @backtrace_cleaner ||= begin
  82288. # Relies on Active Support, so we have to lazy load to postpone definition until AS has been loaded
  82289. require 'rails/backtrace_cleaner'
  82290. Rails::BacktraceCleaner.new
  82291. end
  82292. end
  82293. def root
  82294. application && application.config.root
  82295. end
  82296. def env
  82297. @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
  82298. end
  82299. def env=(environment)
  82300. @_env = ActiveSupport::StringInquirer.new(environment)
  82301. end
  82302. # Returns all rails groups for loading based on:
  82303. #
  82304. # * The Rails environment;
  82305. # * The environment variable RAILS_GROUPS;
  82306. # * The optional envs given as argument and the hash with group dependencies;
  82307. #
  82308. # groups assets: [:development, :test]
  82309. #
  82310. # # Returns
  82311. # # => [:default, :development, :assets] for Rails.env == "development"
  82312. # # => [:default, :production] for Rails.env == "production"
  82313. def groups(*groups)
  82314. hash = groups.extract_options!
  82315. env = Rails.env
  82316. groups.unshift(:default, env)
  82317. groups.concat ENV["RAILS_GROUPS"].to_s.split(",")
  82318. groups.concat hash.map { |k,v| k if v.map(&:to_s).include?(env) }
  82319. groups.compact!
  82320. groups.uniq!
  82321. groups
  82322. end
  82323. def version
  82324. VERSION::STRING
  82325. end
  82326. def public_path
  82327. application && Pathname.new(application.paths["public"].first)
  82328. end
  82329. end
  82330. end
  82331. class ApplicationController < ActionController::Base
  82332. # Prevent CSRF attacks by raising an exception.
  82333. # For APIs, you may want to use :reset_session instead.
  82334. protect_from_forgery :with => :exception
  82335. endclass UsersController < ApplicationController
  82336. # GET /users
  82337. # GET /users.json
  82338. def index
  82339. @users = User.all
  82340. respond_to do |format|
  82341. format.html # index.html.erb
  82342. format.json { render json: @users }
  82343. end
  82344. end
  82345. # GET /users/1
  82346. # GET /users/1.json
  82347. def show
  82348. @user = User.find(params[:id])
  82349. respond_to do |format|
  82350. format.html # show.html.erb
  82351. format.json { render json: @user }
  82352. end
  82353. end
  82354. # GET /users/new
  82355. # GET /users/new.json
  82356. def new
  82357. @user = User.new
  82358. respond_to do |format|
  82359. format.html # new.html.erb
  82360. format.json { render json: @user }
  82361. end
  82362. end
  82363. # GET /users/1/edit
  82364. def edit
  82365. @user = User.find(params[:id])
  82366. end
  82367. # POST /users
  82368. # POST /users.json
  82369. def create
  82370. @user = User.new(params[:user])
  82371. respond_to do |format|
  82372. if @user.save
  82373. format.html { redirect_to @user, notice: 'User was successfully created.' }
  82374. format.json { render json: @user, status: :created, location: @user }
  82375. else
  82376. format.html { render action: "new" }
  82377. format.json { render json: @user.errors, status: :unprocessable_entity }
  82378. end
  82379. end
  82380. end
  82381. # PATCH/PUT /users/1
  82382. # PATCH/PUT /users/1.json
  82383. def update
  82384. @user = User.find(params[:id])
  82385. respond_to do |format|
  82386. if @user.update_attributes(params[:user])
  82387. format.html { redirect_to @user, notice: 'User was successfully updated.' }
  82388. format.json { head :no_content }
  82389. else
  82390. format.html { render action: "edit" }
  82391. format.json { render json: @user.errors, status: :unprocessable_entity }
  82392. end
  82393. end
  82394. end
  82395. # DELETE /users/1
  82396. # DELETE /users/1.json
  82397. def destroy
  82398. @user = User.find(params[:id])
  82399. @user.destroy
  82400. respond_to do |format|
  82401. format.html { redirect_to users_url }
  82402. format.json { head :no_content }
  82403. end
  82404. end
  82405. end
  82406. module ApplicationHelper
  82407. end
  82408. module UsersHelper
  82409. end
  82410. class User < ActiveRecord::Base
  82411. attr_accessible :password, :username
  82412. end
  82413. require File.expand_path('../boot', __FILE__)
  82414. require 'rails/all'
  82415. if defined?(Bundler)
  82416. # If you precompile assets before deploying to production, use this line.
  82417. Bundler.require(*Rails.groups(:assets => %w(development test)))
  82418. # If you want your assets lazily compiled in production, use this line.
  82419. # Bundler.require(:default, :assets, Rails.env)
  82420. end
  82421. module AppTemplate
  82422. class Application < Rails::Application
  82423. # Settings in config/environments/* take precedence over those specified here.
  82424. # Application configuration should go into files in config/initializers
  82425. # -- all .rb files in that directory are automatically loaded.
  82426. # Custom directories with classes and modules you want to be autoloadable.
  82427. # config.autoload_paths += %W(#{config.root}/extras)
  82428. # Activate observers that should always be running.
  82429. # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
  82430. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
  82431. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
  82432. # config.time_zone = 'Central Time (US & Canada)'
  82433. # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
  82434. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
  82435. # config.i18n.default_locale = :de
  82436. # Configure the default encoding used in templates for Ruby 1.9.
  82437. config.encoding = "utf-8"
  82438. # Configure sensitive parameters which will be filtered from the log file.
  82439. config.filter_parameters += [:password]
  82440. # Enable escaping HTML in JSON. The default is false.
  82441. # config.active_support.escape_html_entities_in_json = true
  82442. # Use SQL instead of Active Record's schema dumper when creating the database.
  82443. # This is necessary if your schema can't be completely dumped by the schema dumper,
  82444. # like if you have constraints or database-specific column types.
  82445. # config.active_record.schema_format = :sql
  82446. # Enforce whitelist mode for mass assignment.
  82447. # This will create an empty whitelist of attributes available for mass-assignment for all models
  82448. # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
  82449. # parameters by using an attr_accessible or attr_protected declaration.
  82450. config.active_record.whitelist_attributes = true
  82451. # Specifies whether or not has_many or has_one association option :dependent => :restrict raises
  82452. # an exception. If set to true, then an ActiveRecord::DeleteRestrictionError exception would be
  82453. # raised. If set to false, then an error will be added on the model instead.
  82454. config.active_record.dependent_restrict_raises = false
  82455. # Enable the asset pipeline.
  82456. config.assets.enabled = true
  82457. # Version of your assets, change this if you want to expire all your assets.
  82458. config.assets.version = '1.0'
  82459. config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
  82460. config.session_store :cookie_store, :key => "_myapp_session"
  82461. config.active_support.deprecation = :log
  82462. config.action_controller.allow_forgery_protection = false
  82463. end
  82464. end
  82465. Dir.chdir('/Users/murphy/ruby/rails') do
  82466. require '/Users/murphy/ruby/rails/load_paths'
  82467. end
  82468. require 'rails/all'
  82469. # Load the rails application
  82470. require File.expand_path('../application', __FILE__)
  82471. # Initialize the rails application
  82472. AppTemplate::Application.initialize!
  82473. AppTemplate::Application.routes.draw do
  82474. resources :users
  82475. # The priority is based upon order of creation:
  82476. # first created -> highest priority.
  82477. # Sample of regular route:
  82478. # get 'products/:id' => 'catalog#view'
  82479. # Keep in mind you can assign values other than :controller and :action
  82480. # Sample of named route:
  82481. # get 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
  82482. # This route can be invoked with purchase_url(:id => product.id)
  82483. # Sample resource route (maps HTTP verbs to controller actions automatically):
  82484. # resources :products
  82485. # Sample resource route with options:
  82486. # resources :products do
  82487. # member do
  82488. # get 'short'
  82489. # post 'toggle'
  82490. # end
  82491. #
  82492. # collection do
  82493. # get 'sold'
  82494. # end
  82495. # end
  82496. # Sample resource route with sub-resources:
  82497. # resources :products do
  82498. # resources :comments, :sales
  82499. # resource :seller
  82500. # end
  82501. # Sample resource route with more complex sub-resources
  82502. # resources :products do
  82503. # resources :comments
  82504. # resources :sales do
  82505. # get 'recent', :on => :collection
  82506. # end
  82507. # end
  82508. # Sample resource route within a namespace:
  82509. # namespace :admin do
  82510. # # Directs /admin/products/* to Admin::ProductsController
  82511. # # (app/controllers/admin/products_controller.rb)
  82512. # resources :products
  82513. # end
  82514. # You can have the root of your site routed with "root"
  82515. # just remember to delete public/index.html.
  82516. # root :to => 'welcome#index'
  82517. # See how all your routes lay out with "rake routes"
  82518. match ':controller(/:action(/:id))(.:format)', :via => :all
  82519. end
  82520. class CreateUsers < ActiveRecord::Migration
  82521. def change
  82522. create_table :users do |t|
  82523. t.string :username
  82524. t.string :password
  82525. t.timestamps
  82526. end
  82527. end
  82528. end
  82529. # encoding: UTF-8
  82530. # This file is auto-generated from the current state of the database. Instead
  82531. # of editing this file, please use the migrations feature of Active Record to
  82532. # incrementally modify your database, and then regenerate this schema definition.
  82533. #
  82534. # Note that this schema.rb definition is the authoritative source for your
  82535. # database schema. If you need to create the application database on another
  82536. # system, you should be using db:schema:load, not running all the migrations
  82537. # from scratch. The latter is a flawed and unsustainable approach (the more migrations
  82538. # you'll amass, the slower it'll run and the greater likelihood for issues).
  82539. #
  82540. # It's strongly recommended that you check this file into your version control system.
  82541. ActiveRecord::Schema.define(:version => 20120513121623) do
  82542. create_table "users", :force => true do |t|
  82543. t.string "username"
  82544. t.string "password"
  82545. t.datetime "created_at", :null => false
  82546. t.datetime "updated_at", :null => false
  82547. end
  82548. end
  82549. # This file should contain all the record creation needed to seed the database with its default values.
  82550. # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
  82551. #
  82552. # Examples:
  82553. #
  82554. # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
  82555. # Mayor.create(name: 'Emanuel', city: cities.first)
  82556. class ApplicationController < ActionController::Base
  82557. # Prevent CSRF attacks by raising an exception.
  82558. # For APIs, you may want to use :reset_session instead.
  82559. protect_from_forgery :with => :exception
  82560. endmodule ApplicationHelper
  82561. end
  82562. require File.expand_path('../boot', __FILE__)
  82563. require 'rails/all'
  82564. if defined?(Bundler)
  82565. # If you precompile assets before deploying to production, use this line.
  82566. Bundler.require(*Rails.groups(:assets => %w(development test)))
  82567. # If you want your assets lazily compiled in production, use this line.
  82568. # Bundler.require(:default, :assets, Rails.env)
  82569. end
  82570. module AppTemplate
  82571. class Application < Rails::Application
  82572. # Settings in config/environments/* take precedence over those specified here.
  82573. # Application configuration should go into files in config/initializers
  82574. # -- all .rb files in that directory are automatically loaded.
  82575. # Custom directories with classes and modules you want to be autoloadable.
  82576. # config.autoload_paths += %W(#{config.root}/extras)
  82577. # Activate observers that should always be running.
  82578. # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
  82579. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
  82580. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
  82581. # config.time_zone = 'Central Time (US & Canada)'
  82582. # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
  82583. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
  82584. # config.i18n.default_locale = :de
  82585. # Configure the default encoding used in templates for Ruby 1.9.
  82586. config.encoding = "utf-8"
  82587. # Configure sensitive parameters which will be filtered from the log file.
  82588. config.filter_parameters += [:password]
  82589. # Enable escaping HTML in JSON. The default is false.
  82590. # config.active_support.escape_html_entities_in_json = true
  82591. # Use SQL instead of Active Record's schema dumper when creating the database.
  82592. # This is necessary if your schema can't be completely dumped by the schema dumper,
  82593. # like if you have constraints or database-specific column types.
  82594. # config.active_record.schema_format = :sql
  82595. # Enforce whitelist mode for mass assignment.
  82596. # This will create an empty whitelist of attributes available for mass-assignment for all models
  82597. # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
  82598. # parameters by using an attr_accessible or attr_protected declaration.
  82599. config.active_record.whitelist_attributes = true
  82600. # Specifies whether or not has_many or has_one association option :dependent => :restrict raises
  82601. # an exception. If set to true, then an ActiveRecord::DeleteRestrictionError exception would be
  82602. # raised. If set to false, then an error will be added on the model instead.
  82603. config.active_record.dependent_restrict_raises = false
  82604. # Enable the asset pipeline.
  82605. config.assets.enabled = true
  82606. # Version of your assets, change this if you want to expire all your assets.
  82607. config.assets.version = '1.0'
  82608. end
  82609. end
  82610. Dir.chdir('/Users/murphy/ruby/rails') do
  82611. require '/Users/murphy/ruby/rails/load_paths'
  82612. end
  82613. require 'rails/all'
  82614. # Load the rails application
  82615. require File.expand_path('../application', __FILE__)
  82616. # Initialize the rails application
  82617. AppTemplate::Application.initialize!
  82618. AppTemplate::Application.configure do
  82619. # Settings specified here will take precedence over those in config/application.rb.
  82620. # In the development environment your application's code is reloaded on
  82621. # every request. This slows down response time but is perfect for development
  82622. # since you don't have to restart the web server when you make code changes.
  82623. config.cache_classes = false
  82624. # Show full error reports and disable caching.
  82625. config.consider_all_requests_local = true
  82626. config.action_controller.perform_caching = false
  82627. # Don't care if the mailer can't send.
  82628. config.action_mailer.raise_delivery_errors = false
  82629. # Print deprecation notices to the Rails logger.
  82630. config.active_support.deprecation = :log
  82631. # Only use best-standards-support built into browsers.
  82632. config.action_dispatch.best_standards_support = :builtin
  82633. # Raise exception on mass assignment protection for Active Record models.
  82634. config.active_record.mass_assignment_sanitizer = :strict
  82635. # Log the query plan for queries taking more than this (works
  82636. # with SQLite, MySQL, and PostgreSQL).
  82637. config.active_record.auto_explain_threshold_in_seconds = 0.5
  82638. # Do not compress assets.
  82639. config.assets.compress = false
  82640. # Expands the lines which load the assets.
  82641. config.assets.debug = true
  82642. # In development, use an in-memory queue for queueing
  82643. config.queue = Rails::Queueing::Queue
  82644. end
  82645. AppTemplate::Application.configure do
  82646. # Settings specified here will take precedence over those in config/application.rb.
  82647. # Code is not reloaded between requests.
  82648. config.cache_classes = true
  82649. # Full error reports are disabled and caching is turned on.
  82650. config.consider_all_requests_local = false
  82651. config.action_controller.perform_caching = true
  82652. # Disable Rails's static asset server (Apache or nginx will already do this).
  82653. config.serve_static_assets = false
  82654. # Compress JavaScripts and CSS.
  82655. config.assets.compress = true
  82656. # Don't fallback to assets pipeline if a precompiled asset is missed.
  82657. config.assets.compile = false
  82658. # Generate digests for assets URLs.
  82659. config.assets.digest = true
  82660. # Defaults to nil and saved in location specified by config.assets.prefix
  82661. # config.assets.manifest = YOUR_PATH
  82662. # Specifies the header that your server uses for sending files.
  82663. # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
  82664. # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
  82665. # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  82666. # config.force_ssl = true
  82667. # Set to :debug to see everything in the log.
  82668. config.log_level = :info
  82669. # Prepend all log lines with the following tags.
  82670. # config.log_tags = [ :subdomain, :uuid ]
  82671. # Use a different logger for distributed setups.
  82672. # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
  82673. # Use a different cache store in production.
  82674. # config.cache_store = :mem_cache_store
  82675. # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  82676. # config.action_controller.asset_host = "http://assets.example.com"
  82677. # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added).
  82678. # config.assets.precompile += %w( search.js )
  82679. # Disable delivery errors, bad email addresses will be ignored.
  82680. # config.action_mailer.raise_delivery_errors = false
  82681. # Enable threaded mode.
  82682. # config.threadsafe!
  82683. # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  82684. # the I18n.default_locale when a translation can not be found).
  82685. config.i18n.fallbacks = true
  82686. # Send deprecation notices to registered listeners.
  82687. config.active_support.deprecation = :notify
  82688. # Log the query plan for queries taking more than this (works
  82689. # with SQLite, MySQL, and PostgreSQL).
  82690. # config.active_record.auto_explain_threshold_in_seconds = 0.5
  82691. # Disable automatic flushing of the log to improve performance.
  82692. # config.autoflush_log = false
  82693. # Use default logging formatter so that PID and timestamp are not suppressed
  82694. config.log_formatter = ::Logger::Formatter.new
  82695. # Default the production mode queue to an in-memory queue. You will probably
  82696. # want to replace this with an out-of-process queueing solution
  82697. config.queue = Rails::Queueing::Queue
  82698. end
  82699. AppTemplate::Application.configure do
  82700. # Settings specified here will take precedence over those in config/application.rb.
  82701. # The test environment is used exclusively to run your application's
  82702. # test suite. You never need to work with it otherwise. Remember that
  82703. # your test database is "scratch space" for the test suite and is wiped
  82704. # and recreated between test runs. Don't rely on the data there!
  82705. config.cache_classes = true
  82706. # Configure static asset server for tests with Cache-Control for performance.
  82707. config.serve_static_assets = true
  82708. config.static_cache_control = "public, max-age=3600"
  82709. # Show full error reports and disable caching.
  82710. config.consider_all_requests_local = true
  82711. config.action_controller.perform_caching = false
  82712. # Raise exceptions instead of rendering exception templates.
  82713. config.action_dispatch.show_exceptions = false
  82714. # Disable request forgery protection in test environment.
  82715. config.action_controller.allow_forgery_protection = false
  82716. # Tell Action Mailer not to deliver emails to the real world.
  82717. # The :test delivery method accumulates sent emails in the
  82718. # ActionMailer::Base.deliveries array.
  82719. config.action_mailer.delivery_method = :test
  82720. # Raise exception on mass assignment protection for Active Record models.
  82721. config.active_record.mass_assignment_sanitizer = :strict
  82722. # Print deprecation notices to the stderr.
  82723. config.active_support.deprecation = :stderr
  82724. # Use the testing queue
  82725. config.queue = Rails::Queueing::TestQueue
  82726. end
  82727. # Be sure to restart your server when you modify this file.
  82728. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
  82729. # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
  82730. # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
  82731. # Rails.backtrace_cleaner.remove_silencers!
  82732. # Be sure to restart your server when you modify this file.
  82733. # Add new inflection rules using the following format
  82734. # (all these examples are active by default):
  82735. # ActiveSupport::Inflector.inflections do |inflect|
  82736. # inflect.plural /^(ox)$/i, '\1en'
  82737. # inflect.singular /^(ox)en/i, '\1'
  82738. # inflect.irregular 'person', 'people'
  82739. # inflect.uncountable %w( fish sheep )
  82740. # end
  82741. #
  82742. # These inflection rules are supported but not enabled by default:
  82743. # ActiveSupport::Inflector.inflections do |inflect|
  82744. # inflect.acronym 'RESTful'
  82745. # end
  82746. # Be sure to restart your server when you modify this file.
  82747. # Add new mime types for use in respond_to blocks:
  82748. # Mime::Type.register "text/richtext", :rtf
  82749. # Mime::Type.register_alias "text/html", :iphone
  82750. # Be sure to restart your server when you modify this file.
  82751. # Your secret key for verifying the integrity of signed cookies.
  82752. # If you change this key, all old signed cookies will become invalid!
  82753. # Make sure the secret is at least 30 characters and all random,
  82754. # no regular words or you'll be exposed to dictionary attacks.
  82755. # Make sure your secret_token is kept private
  82756. # if you're sharing your code publicly.
  82757. AppTemplate::Application.config.secret_token = '900e93eda3749848cd9837613d41c6c22672a03eb6895e1378d8a381272db1df2984a39ac91ad0bbd3a44925617c3eab3f9bd6b038958c7516686ac35770f578'
  82758. # Be sure to restart your server when you modify this file.
  82759. AppTemplate::Application.config.session_store :cookie_store, key: '_app_template_session'
  82760. # Use the database for sessions instead of the cookie-based default,
  82761. # which shouldn't be used to store highly confidential information
  82762. # (create the session table with "rails generate session_migration")
  82763. # AppTemplate::Application.config.session_store :active_record_store
  82764. # Be sure to restart your server when you modify this file.
  82765. #
  82766. # This file contains settings for ActionController::ParamsWrapper which
  82767. # is enabled by default.
  82768. # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
  82769. ActiveSupport.on_load(:action_controller) do
  82770. wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
  82771. end
  82772. # Disable root element in JSON by default.
  82773. ActiveSupport.on_load(:active_record) do
  82774. self.include_root_in_json = false
  82775. end
  82776. AppTemplate::Application.routes.draw do
  82777. # The priority is based upon order of creation:
  82778. # first created -> highest priority.
  82779. # Sample of regular route:
  82780. # get 'products/:id' => 'catalog#view'
  82781. # Keep in mind you can assign values other than :controller and :action
  82782. # Sample of named route:
  82783. # get 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
  82784. # This route can be invoked with purchase_url(:id => product.id)
  82785. # Sample resource route (maps HTTP verbs to controller actions automatically):
  82786. # resources :products
  82787. # Sample resource route with options:
  82788. # resources :products do
  82789. # member do
  82790. # get 'short'
  82791. # post 'toggle'
  82792. # end
  82793. #
  82794. # collection do
  82795. # get 'sold'
  82796. # end
  82797. # end
  82798. # Sample resource route with sub-resources:
  82799. # resources :products do
  82800. # resources :comments, :sales
  82801. # resource :seller
  82802. # end
  82803. # Sample resource route with more complex sub-resources
  82804. # resources :products do
  82805. # resources :comments
  82806. # resources :sales do
  82807. # get 'recent', :on => :collection
  82808. # end
  82809. # end
  82810. # Sample resource route within a namespace:
  82811. # namespace :admin do
  82812. # # Directs /admin/products/* to Admin::ProductsController
  82813. # # (app/controllers/admin/products_controller.rb)
  82814. # resources :products
  82815. # end
  82816. # You can have the root of your site routed with "root"
  82817. # just remember to delete public/index.html.
  82818. # root :to => 'welcome#index'
  82819. # See how all your routes lay out with "rake routes"
  82820. end# This file should contain all the record creation needed to seed the database with its default values.
  82821. # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
  82822. #
  82823. # Examples:
  82824. #
  82825. # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
  82826. # Mayor.create(name: 'Emanuel', city: cities.first)
  82827. FRAMEWORKS = %w( activesupport activemodel activerecord actionpack actionmailer railties )
  82828. root = File.expand_path('../../', __FILE__)
  82829. version = File.read("#{root}/RAILS_VERSION").strip
  82830. tag = "v#{version}"
  82831. directory "pkg"
  82832. (FRAMEWORKS + ['rails']).each do |framework|
  82833. namespace framework do
  82834. gem = "pkg/#{framework}-#{version}.gem"
  82835. gemspec = "#{framework}.gemspec"
  82836. task :clean do
  82837. rm_f gem
  82838. end
  82839. task :update_version_rb do
  82840. glob = root.dup
  82841. glob << "/#{framework}/lib/*" unless framework == "rails"
  82842. glob << "/version.rb"
  82843. file = Dir[glob].first
  82844. ruby = File.read(file)
  82845. major, minor, tiny, pre = version.split('.')
  82846. pre = pre ? pre.inspect : "nil"
  82847. ruby.gsub!(/^(\s*)MAJOR = .*?$/, "\\1MAJOR = #{major}")
  82848. raise "Could not insert MAJOR in #{file}" unless $1
  82849. ruby.gsub!(/^(\s*)MINOR = .*?$/, "\\1MINOR = #{minor}")
  82850. raise "Could not insert MINOR in #{file}" unless $1
  82851. ruby.gsub!(/^(\s*)TINY = .*?$/, "\\1TINY = #{tiny}")
  82852. raise "Could not insert TINY in #{file}" unless $1
  82853. ruby.gsub!(/^(\s*)PRE = .*?$/, "\\1PRE = #{pre}")
  82854. raise "Could not insert PRE in #{file}" unless $1
  82855. File.open(file, 'w') { |f| f.write ruby }
  82856. end
  82857. task gem => %w(update_version_rb pkg) do
  82858. cmd = ""
  82859. cmd << "cd #{framework} && " unless framework == "rails"
  82860. cmd << "gem build #{gemspec} && mv #{framework}-#{version}.gem #{root}/pkg/"
  82861. sh cmd
  82862. end
  82863. task :build => [:clean, gem]
  82864. task :install => :build do
  82865. sh "gem install #{gem}"
  82866. end
  82867. task :prep_release => [:ensure_clean_state, :build]
  82868. task :push => :build do
  82869. sh "gem push #{gem}"
  82870. end
  82871. end
  82872. end
  82873. namespace :changelog do
  82874. task :release_date do
  82875. FRAMEWORKS.each do |fw|
  82876. require 'date'
  82877. replace = '\1(' + Date.today.strftime('%B %d, %Y') + ')'
  82878. fname = File.join fw, 'CHANGELOG.md'
  82879. contents = File.read(fname).sub(/^([^(]*)\(unreleased\)/, replace)
  82880. File.open(fname, 'wb') { |f| f.write contents }
  82881. end
  82882. end
  82883. task :release_summary do
  82884. FRAMEWORKS.each do |fw|
  82885. puts "## #{fw}"
  82886. fname = File.join fw, 'CHANGELOG.md'
  82887. contents = File.readlines fname
  82888. contents.shift
  82889. changes = []
  82890. changes << contents.shift until contents.first =~ /^\*Rails \d+\.\d+\.\d+/
  82891. puts changes.reject { |change| change.strip.empty? }.join
  82892. puts
  82893. end
  82894. end
  82895. end
  82896. namespace :all do
  82897. task :build => FRAMEWORKS.map { |f| "#{f}:build" } + ['rails:build']
  82898. task :install => FRAMEWORKS.map { |f| "#{f}:install" } + ['rails:install']
  82899. task :push => FRAMEWORKS.map { |f| "#{f}:push" } + ['rails:push']
  82900. task :ensure_clean_state do
  82901. unless `git status -s | grep -v RAILS_VERSION`.strip.empty?
  82902. abort "[ABORTING] `git status` reports a dirty tree. Make sure all changes are committed"
  82903. end
  82904. unless ENV['SKIP_TAG'] || `git tag | grep #{tag}`.strip.empty?
  82905. abort "[ABORTING] `git tag` shows that #{tag} already exists. Has this version already\n"\
  82906. " been released? Git tagging can be skipped by setting SKIP_TAG=1"
  82907. end
  82908. end
  82909. task :commit do
  82910. File.open('pkg/commit_message.txt', 'w') do |f|
  82911. f.puts "# Preparing for #{version} release\n"
  82912. f.puts
  82913. f.puts "# UNCOMMENT THE LINE ABOVE TO APPROVE THIS COMMIT"
  82914. end
  82915. sh "git add . && git commit --verbose --template=pkg/commit_message.txt"
  82916. rm_f "pkg/commit_message.txt"
  82917. end
  82918. task :tag do
  82919. sh "git tag #{tag}"
  82920. sh "git push --tags"
  82921. end
  82922. task :release => %w(ensure_clean_state build commit tag push)
  82923. end
  82924. module Rails
  82925. module VERSION #:nodoc:
  82926. MAJOR = 4
  82927. MINOR = 0
  82928. TINY = 0
  82929. PRE = "beta"
  82930. STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
  82931. end
  82932. end