actionpack-3.2.16/0000755000175000017500000000000012247655372013260 5ustar ondrejondrejactionpack-3.2.16/CHANGELOG.md0000644000175000017500000006504612247655372015104 0ustar ondrejondrej* Deep Munge the parameters for GET and POST Fixes CVE-2013-6417 * Stop using i18n's built in HTML error handling. Fixes: CVE-2013-4491 * Escape the unit value provided to number_to_currency Fixes CVE-2013-6415 * Only use valid mime type symbols as cache keys CVE-2013-6414 ## Rails 3.2.15 (Oct 16, 2013) ## * Fix `ActionDispatch::RemoteIp::GetIp#calculate_ip` to only check for spoofing attacks if both `HTTP_CLIENT_IP` and `HTTP_X_FORWARDED_FOR` are set. Fixes #12410 Backports #10844 *Tamir Duberstein* * Fix the assert_recognizes test method so that it works when there are constraints on the querystring. Issue/Pull Request #9368 Backport #5219 *Brian Hahn* * Fix to render partial by context(#11605). *Kassio Borges* * Fix `ActionDispatch::Assertions::ResponseAssertions#assert_redirected_to` does not show user-supplied message. Issue: when `assert_redirected_to` fails due to the response redirect not matching the expected redirect the user-supplied message (second parameter) is not shown. This message is only shown if the response is not a redirect. *Alexey Chernenkov* ## Rails 3.2.14 (Jul 22, 2013) ## * Merge `:action` from routing scope and assign endpoint if both `:controller` and `:action` are present. The endpoint assignment only occurs if there is no `:to` present in the options hash so should only affect routes using the shorthand syntax (i.e. endpoint is inferred from the the path). Fixes #9856 *Yves Senn*, *Andrew White* * Always escape the result of `link_to_unless` method. Before: link_to_unless(true, 'Showing', 'github.com') # => "Showing" After: link_to_unless(true, 'Showing', 'github.com') # => "<b>Showing</b>" *dtaniwaki* * Use a case insensitive URI Regexp for #asset_path. This fix a problem where the same asset path using different case are generating different URIs. Before: image_tag("HTTP://google.com") # => "\"Google\"" image_tag("http://google.com") # => "\"Google\"" After: image_tag("HTTP://google.com") # => "\"Google\"" image_tag("http://google.com") # => "\"Google\"" *David Celis + Rafael Mendonça França* * Fix explicit names on multiple file fields. If a file field tag has the multiple option, it is turned into an array field (appending `[]`), but if an explicit name is passed to `file_field` the `[]` is not appended. Fixes #9830. *Ryan McGeary* * Fix assets loading performance in 3.2.13. Issue #8756 uses Sprockets for resolving files that already exist on disk, for those files their extensions don't need to be rewritten. Fixes #9803. *Fred Wu* * Fix `ActionController#action_missing` not being called. Fixes #9799. *Janko Luin* * `ActionView::Helpers::NumberHelper#number_to_human` returns the number unaltered when the units hash does not contain the needed key, e.g. when the number provided is less than the largest key provided. Examples: number_to_human(123, units: {}) # => 123 number_to_human(123, units: { thousand: 'k' }) # => 123 Fixes #9269. Backport #9347. *Michael Hoffman* * Include I18n locale fallbacks in view lookup. Fixes GH#3512. *Juan Barreneche* * Fix `ActionDispatch::Request#formats` when the Accept request-header is an empty string. Fix #7774 [Backport #8977, #9541] *Soylent + Maxime Réty* ## Rails 3.2.13 (Mar 18, 2013) ## * Fix incorrectly appended square brackets to a multiple select box if an explicit name has been given and it already ends with "[]". Before: select(:category, [], {}, multiple: true, name: "post[category][]") # => Backport #9616. *Olek Janiszewski* * Determine the controller#action from only the matched path when using the shorthand syntax. Previously the complete path was used, which led to problems with nesting (scopes and namespaces). Fixes #7554. Backport #9361. Example: # this will route to questions#new scope ':locale' do get 'questions/new' end *Yves Senn* * Fix `assert_template` with `render :stream => true`. Fix #1743. Backport #5288. *Sergey Nartimov* * Eagerly populate the http method lookup cache so local project inflections do not interfere with use of underscore method ( and we don't need locks ) *Aditya Sanghi* * `BestStandardsSupport` no longer duplicates `X-UA-Compatible` values on each request to prevent header size from blowing up. *Edward Anderson* * Fixed JSON params parsing regression for non-object JSON content. *Dylan Smith* * Prevent unnecessary asset compilation when using `javascript_include_tag` on files with non-standard extensions. *Noah Silas* * Fixes issue where duplicate assets can be required with sprockets. *Jeremy Jackson* * Bump `rack` dependency to 1.4.3, eliminate `Rack::File` headers deprecation warning. *Sam Ruby + Carlos Antonio da Silva* * Do not append second slash to `root_url` when using `trailing_slash: true` Fix #8700. Backport #8701. Example: # before root_url # => http://test.host// # after root_url # => http://test.host/ *Yves Senn* * Fix a bug in `content_tag_for` that prevents it for work without a block. *Jasl* * Clear url helper methods when routes are reloaded by removing the methods explicitly rather than just clearing the module because it didn't work properly and could be the source of a memory leak. *Andrew White* * Fix a bug in `ActionDispatch::Request#raw_post` that caused `env['rack.input']` to be read but not rewound. *Matt Venables* * More descriptive error messages when calling `render :partial` with an invalid `:layout` argument. Fixes #8376. render :partial => 'partial', :layout => true # results in ActionView::MissingTemplate: Missing partial /true *Yves Senn* * Accept symbols as `#send_data` :disposition value. [Backport #8329] *Elia Schito* * Add i18n scope to `distance_of_time_in_words`. [Backport #7997] *Steve Klabnik* * Fix side effect of `url_for` changing the `:controller` string option. [Backport #6003] Before: controller = '/projects' url_for :controller => controller, :action => 'status' puts controller #=> 'projects' After puts controller #=> '/projects' *Nikita Beloglazov + Andrew White* * Introduce `ActionView::Template::Handlers::ERB.escape_whitelist`. This is a list of mime types where template text is not html escaped by default. It prevents `Jack & Joe` from rendering as `Jack & Joe` for the whitelisted mime types. The default whitelist contains text/plain. Fix #7976 [Backport #8235] *Joost Baaij* * `BestStandardsSupport` middleware now appends it's `X-UA-Compatible` value to app's returned value if any. Fix #8086 [Backport #8093] *Nikita Afanasenko* * prevent double slashes in engine urls when `Rails.application.default_url_options[:trailing_slash] = true` is set Fix #7842 *Yves Senn* * Fix input name when `:multiple => true` and `:index` are set. Before: check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1) #=> After: check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1) #=> Fix #8108 *Daniel Fox, Grant Hutchins & Trace Wax* ## Rails 3.2.12 (Feb 11, 2013) ## * No changes. ## Rails 3.2.11 (Jan 8, 2013) ## * Strip nils from collections on JSON and XML posts. [CVE-2013-0155] ## Rails 3.2.10 (Jan 2, 2013) ## * No changes. ## Rails 3.2.9 (Nov 12, 2012) ## * Clear url helpers when reloading routes. *Santiago Pastorino* * Revert the shorthand routes scoped with `:module` option fix This added a regression since it is changing the URL mapping. This makes the stable release backward compatible. *Rafael Mendonça França* * Revert the `assert_template` fix to not pass with ever string that matches the template name. This added a regression since people were relying on this buggy behavior. This will introduce back #3849 but this stable release will be backward compatible. Fixes #8068. *Rafael Mendonça França* * Revert the rename of internal variable on ActionController::TemplateAssertions to prevent naming collisions. This added a regression related with shoulda-matchers, since it is expecting the [instance variable @layouts](https://github.com/thoughtbot/shoulda-matchers/blob/9e1188eea68c47d9a56ce6280e45027da6187ab1/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb#L74). This will introduce back #7459 but this stable release will be backward compatible. Fixes #8068. *Rafael Mendonça França* * Accept :remote as symbolic option for `link_to` helper. *Riley Lynch* * Warn when the `:locals` option is passed to `assert_template` outside of a view test case Fix #3415 *Yves Senn* * Rename internal variables on ActionController::TemplateAssertions to prevent naming collisions. @partials, @templates and @layouts are now prefixed with an underscore. Fix #7459 *Yves Senn* * `resource` and `resources` don't modify the passed options hash Fix #7777 *Yves Senn* * Precompiled assets include aliases from foo.js to foo/index.js and vice versa. # Precompiles phone-.css and aliases phone/index.css to phone.css. config.assets.precompile = [ 'phone.css' ] # Precompiles phone/index-.css and aliases phone.css to phone/index.css. config.assets.precompile = [ 'phone/index.css' ] # Both of these work with either precompile thanks to their aliases. <%= stylesheet_link_tag 'phone', media: 'all' %> <%= stylesheet_link_tag 'phone/index', media: 'all' %> *Jeremy Kemper* * `assert_template` is no more passing with what ever string that matches with the template name. Before when we have a template `/layout/hello.html.erb`, `assert_template` was passing with any string that matches. This behavior allowed false positive like: assert_template "layout" assert_template "out/hello" Now it only passes with: assert_template "layout/hello" assert_template "hello" Fixes #3849. *Hugolnx* * Handle `ActionDispatch::Http::UploadedFile` like `Rack::Test::UploadedFile`, don't call to_param on it. Since `Rack::Test::UploadedFile` isn't API compatible this is needed to test file uploads that rely on `tempfile` being available. *Tim Vandecasteele* * Respect `config.digest = false` for `asset_path` Previously, the `asset_path` internals only respected the `:digest` option, but ignored the global config setting. This meant that `config.digest = false` could not be used in conjunction with `config.compile = false` this corrects the behavior. *Peter Wagenet* * Fix #7646, the log now displays the correct status code when an exception is raised. *Yves Senn* * Fix handling of date selects when using both disabled and discard options. Fixes #7431. *Vasiliy Ermolovich* * Fix select_tag when option_tags is nil. Fixes #7404. *Sandeep Ravichandran* * `javascript_include_tag :all` will now not include `application.js` if the file does not exists. *Prem Sichanugrist* * Support cookie jar options (e.g., domain :all) for all session stores. Fixes GH#3047, GH#2483. *Ravil Bayramgalin* * Performance Improvement to send_file: Avoid having to pass an open file handle as the response body. Rack::Sendfile will usually intercept the response and just uses the path directly, so no reason to open the file. This performance improvement also resolves an issue with jRuby encodings, and is the reason for the backport, see issue #6844. *Jeremy Kemper & Erich Menge* ## Rails 3.2.8 (Aug 9, 2012) ## * There is an XSS vulnerability in the strip_tags helper in Ruby on Rails, the helper doesn't correctly handle malformed html. As a result an attacker can execute arbitrary javascript through the use of specially crafted malformed html. *Marek from Nethemba (www.nethemba.com) & Santiago Pastorino* * When a "prompt" value is supplied to the `select_tag` helper, the "prompt" value is not escaped. If untrusted data is not escaped, and is supplied as the prompt value, there is a potential for XSS attacks. Vulnerable code will look something like this: select_tag("name", options, :prompt => UNTRUSTED_INPUT) *Santiago Pastorino* * Reverted the deprecation of `:confirm`. *Rafael Mendonça França* * Reverted the deprecation of `:disable_with`. *Rafael Mendonça França* * Reverted the deprecation of `:mouseover` option to `image_tag`. *Rafael Mendonça França* * Reverted the deprecation of `button_to_function` and `link_to_function` helpers. *Rafael Mendonça França* ## Rails 3.2.7 (Jul 26, 2012) ## * Do not convert digest auth strings to symbols. CVE-2012-3424 * Bump Journey requirements to 1.0.4 * Add support for optional root segments containing slashes * Fixed bug creating invalid HTML in select options * Show in log correct wrapped keys * Fix NumberHelper options wrapping to prevent verbatim blocks being rendered instead of line continuations. * ActionController::Metal doesn't have logger method, check it and then delegate * ActionController::Caching depends on RackDelegation and AbstractController::Callbacks ## Rails 3.2.6 (Jun 12, 2012) ## * nil is removed from array parameter values CVE-2012-2694 * Deprecate `:confirm` in favor of `':data => { :confirm => "Text" }'` option for `button_to`, `button_tag`, `image_submit_tag`, `link_to` and `submit_tag` helpers. *Carlos Galdino* * Allow to use mounted_helpers (helpers for accessing mounted engines) in ActionView::TestCase. *Piotr Sarnacki* * Include mounted_helpers (helpers for accessing mounted engines) in ActionDispatch::IntegrationTest by default. *Piotr Sarnacki* ## Rails 3.2.5 (Jun 1, 2012) ## * No changes. ## Rails 3.2.4 (May 31, 2012) ## * Deprecate old APIs for highlight, excerpt and word_wrap *Jeremy Walker* * Deprecate `:disable_with` in favor of `'data-disable-with'` option for `button_to`, `button_tag` and `submit_tag` helpers. *Carlos Galdino + Rafael Mendonça França* * Deprecate `:mouseover` option for `image_tag` helper. *Rafael Mendonça França* * Deprecate `button_to_function` and `link_to_function` helpers. *Rafael Mendonça França* * Don't break Haml with textarea newline fix. GH #393, #4000, #5190, #5191 * Fix options handling on labels. GH #2492, #5614 * Added config.action_view.embed_authenticity_token_in_remote_forms to deal with regression from 16ee611fa * Set rendered_format when doing render :inline. GH #5632 * Fix the redirect when it receive blocks with arity of 1. Closes #5677 * Strip [nil] from parameters hash. Thanks to Ben Murphy for reporting this! CVE-2012-2660 ## Rails 3.2.3 (March 30, 2012) ## * Allow to lazy load `default_form_builder` by passing a `String` instead of a constant. *Piotr Sarnacki* * Fix #5632, render :inline set the proper rendered format. *Santiago Pastorino* * Fix textarea rendering when using plugins like HAML. Such plugins encode the first newline character in the content. This issue was introduced in https://github.com/rails/rails/pull/5191 *James Coleman* * Remove the leading \n added by textarea on assert_select. *Santiago Pastorino* * Add `config.action_view.embed_authenticity_token_in_remote_forms` (defaults to true) which allows to set if authenticity token will be included by default in remote forms. If you change it to false, you can still force authenticity token by passing `:authenticity_token => true` in form options *Piotr Sarnacki* * Do not include the authenticity token in forms where remote: true as ajax forms use the meta-tag value *DHH* * Turn off verbose mode of rack-cache, we still have X-Rack-Cache to check that info. Closes #5245. *Santiago Pastorino* * Fix #5238, rendered_format is not set when template is not rendered. *Piotr Sarnacki* * Upgrade rack-cache to 1.2. *José Valim* * ActionController::SessionManagement is deprecated. *Santiago Pastorino* * Since the router holds references to many parts of the system like engines, controllers and the application itself, inspecting the route set can actually be really slow, therefore we default alias inspect to to_s. *José Valim* * Add a new line after the textarea opening tag. Closes #393 *Rafael Mendonça França* * Always pass a respond block from to responder. We should let the responder to decide what to do with the given overridden response block, and not short circuit it. *sikachu* * Fixes layout rendering regression from 3.2.2. *José Valim* ## Rails 3.2.2 (March 1, 2012) ## * Format lookup for partials is derived from the format in which the template is being rendered. Closes #5025 part 2 *Santiago Pastorino* * Use the right format when a partial is missing. Closes #5025. *Santiago Pastorino* * Default responder will now always use your overridden block in `respond_with` to render your response. *Prem Sichanugrist* * check_box helper with :disabled => true will generate a disabled hidden field to conform with the HTML convention where disabled fields are not submitted with the form. This is a behavior change, previously the hidden tag had a value of the disabled checkbox. *Tadas Tamosauskas* ## Rails 3.2.1 (January 26, 2012) ## * Documentation improvements. * Allow `form.select` to accept ranges (regression). *Jeremy Walker* * `datetime_select` works with -/+ infinity dates. *Joe Van Dyk* ## Rails 3.2.0 (January 20, 2012) ## * Setting config.assets.logger to false turn off Sprockets logger *Guillermo Iguaran* * Add `config.action_dispatch.default_charset` to configure default charset for ActionDispatch::Response. *Carlos Antonio da Silva* * Deprecate setting default charset at controller level, use the new `config.action_dispatch.default_charset` instead. *Carlos Antonio da Silva* * Deprecate ActionController::UnknownAction in favour of AbstractController::ActionNotFound. *Carlos Antonio da Silva* * Deprecate ActionController::DoubleRenderError in favour of AbstractController::DoubleRenderError. *Carlos Antonio da Silva* * Deprecate method_missing handling for not found actions, use action_missing instead. *Carlos Antonio da Silva* * Deprecate ActionController#rescue_action, ActionController#initialize_template_class, and ActionController#assign_shortcuts. These methods were not being used internally anymore and are going to be removed in Rails 4. *Carlos Antonio da Silva* * Add config.assets.logger to configure Sprockets logger *Rafael França* * Use a BodyProxy instead of including a Module that responds to close. Closes #4441 if Active Record is disabled assets are delivered correctly *Santiago Pastorino* * Rails initialization with initialize_on_precompile = false should set assets_dir *Santiago Pastorino* * Add font_path helper method *Santiago Pastorino* * Depends on rack ~> 1.4.0 *Santiago Pastorino* * Add :gzip option to `caches_page`. The default option can be configured globally using `page_cache_compression` *Andrey Sitnik* * The ShowExceptions middleware now accepts a exceptions application that is responsible to render an exception when the application fails. The application is invoked with a copy of the exception in `env["action_dispatch.exception"]` and with the PATH_INFO rewritten to the status code. *José Valim* * Add `button_tag` support to ActionView::Helpers::FormBuilder. This support mimics the default behavior of `submit_tag`. Example: <%= form_for @post do |f| %> <%= f.button %> <% end %> * Date helpers accept a new option, `:use_two_digit_numbers = true`, that renders select boxes for months and days with a leading zero without changing the respective values. For example, this is useful for displaying ISO8601-style dates such as '2011-08-01'. *Lennart Fridén and Kim Persson* * Make ActiveSupport::Benchmarkable a default module for ActionController::Base, so the #benchmark method is once again available in the controller context like it used to be *DHH* * Deprecated implied layout lookup in controllers whose parent had a explicit layout set: class ApplicationController layout "application" end class PostsController < ApplicationController end In the example above, Posts controller will no longer automatically look up for a posts layout. If you need this functionality you could either remove `layout "application"` from ApplicationController or explicitly set it to nil in PostsController. *José Valim* * Rails will now use your default layout (such as "layouts/application") when you specify a layout with `:only` and `:except` condition, and those conditions fail. *Prem Sichanugrist* For example, consider this snippet: class CarsController layout 'single_car', :only => :show end Rails will use 'layouts/single_car' when a request comes in `:show` action, and use 'layouts/application' (or 'layouts/cars', if exists) when a request comes in for any other actions. * form_for with +:as+ option uses "#{action}_#{as}" as css class and id: Before: form_for(@user, :as => 'client') # => "
..." Now: form_for(@user, :as => 'client') # => "..." *Vasiliy Ermolovich* * Allow rescue responses to be configured through a railtie as in `config.action_dispatch.rescue_responses`. Please look at ActiveRecord::Railtie for an example *José Valim* * Allow fresh_when/stale? to take a record instead of an options hash *DHH* * Assets should use the request protocol by default or default to relative if no request is available *Jonathan del Strother* * Log "Filter chain halted as CALLBACKNAME rendered or redirected" every time a before callback halts *José Valim* * You can provide a namespace for your form to ensure uniqueness of id attributes on form elements. The namespace attribute will be prefixed with underscore on the generate HTML id. *Vasiliy Ermolovich* Example: <%= form_for(@offer, :namespace => 'namespace') do |f| %> <%= f.label :version, 'Version' %>: <%= f.text_field :version %> <% end %> * Refactor ActionDispatch::ShowExceptions. The controller is responsible for choosing to show exceptions when `consider_all_requests_local` is false. It's possible to override `show_detailed_exceptions?` in controllers to specify which requests should provide debugging information on errors. The default value is now false, meaning local requests in production will no longer show the detailed exceptions page unless `show_detailed_exceptions?` is overridden and set to `request.local?`. * Responders now return 204 No Content for API requests without a response body (as in the new scaffold) *José Valim* * Added ActionDispatch::RequestId middleware that'll make a unique X-Request-Id header available to the response and enables the ActionDispatch::Request#uuid method. This makes it easy to trace requests from end-to-end in the stack and to identify individual requests in mixed logs like Syslog *DHH* * Limit the number of options for select_year to 1000. Pass the :max_years_allowed option to set your own limit. *Libo Cannici* * Passing formats or handlers to render :template and friends is deprecated. For example: *Nick Sutterer & José Valim* render :template => "foo.html.erb" Instead, you can provide :handlers and :formats directly as option: render :template => "foo", :formats => [:html, :js], :handlers => :erb * Changed log level of warning for missing CSRF token from :debug to :warn. *Mike Dillon* * content_tag_for and div_for can now take the collection of records. It will also yield the record as the first argument if you set a receiving argument in your block *Prem Sichanugrist* So instead of having to do this: @items.each do |item| content_tag_for(:li, item) do Title: <%= item.title %> end end You can now do this: content_tag_for(:li, @items) do |item| Title: <%= item.title %> end * send_file now guess the mime type *Esad Hajdarevic* * Mime type entries for PDF, ZIP and other formats were added *Esad Hajdarevic* * Generate hidden input before select with :multiple option set to true. This is useful when you rely on the fact that when no options is set, the state of select will be sent to rails application. Without hidden field nothing is sent according to HTML spec *Bogdan Gusiev* * Refactor ActionController::TestCase cookies *Andrew White* Assigning cookies for test cases should now use cookies[], e.g: cookies[:email] = 'user@example.com' get :index assert_equal 'user@example.com', cookies[:email] To clear the cookies, use clear, e.g: cookies.clear get :index assert_nil cookies[:email] We now no longer write out HTTP_COOKIE and the cookie jar is persistent between requests so if you need to manipulate the environment for your test you need to do it before the cookie jar is created. * ActionController::ParamsWrapper on ActiveRecord models now only wrap attr_accessible attributes if they were set, if not, only the attributes returned by the class method attribute_names will be wrapped. This fixes the wrapping of nested attributes by adding them to attr_accessible. Please check [3-1-stable](https://github.com/rails/rails/blob/3-1-stable/actionpack/CHANGELOG.md) for previous changes. actionpack-3.2.16/README.rdoc0000644000175000017500000002427012247655372015073 0ustar ondrejondrej= Action Pack -- From request to response Action Pack is a framework for handling and responding to web requests. It provides mechanisms for *routing* (mapping request URLs to actions), defining *controllers* that implement actions, and generating responses by rendering *views*, which are templates of various formats. In short, Action Pack provides the view and controller layers in the MVC paradigm. It consists of several modules: * Action Dispatch, which parses information about the web request, handles routing as defined by the user, and does advanced processing related to HTTP such as MIME-type negotiation, decoding parameters in POST/PUT bodies, handling HTTP caching logic, cookies and sessions. * Action Controller, which provides a base controller class that can be subclassed to implement filters and actions to handle requests. The result of an action is typically content generated from views. * Action View, which handles view template lookup and rendering, and provides view helpers that assist when building HTML forms, Atom feeds and more. Template formats that Action View handles are ERB (embedded Ruby, typically used to inline short Ruby snippets inside HTML), and XML Builder. With the Ruby on Rails framework, users only directly interface with the Action Controller module. Necessary Action Dispatch functionality is activated by default and Action View rendering is implicitly triggered by Action Controller. However, these modules are designed to function on their own and can be used outside of Rails. A short rundown of some of the major features: * Actions grouped in controller as methods instead of separate command objects and can therefore share helper methods class CustomersController < ActionController::Base def show @customer = find_customer end def update @customer = find_customer if @customer.update_attributes(params[:customer]) redirect_to :action => "show" else render :action => "edit" end end private def find_customer Customer.find params[:id] end end {Learn more}[link:classes/ActionController/Base.html] * ERB templates (static content mixed with dynamic output from ruby) <% @posts.each do |post| %> Title: <%= post.title %> <% end %> All post titles: <%= @posts.collect{ |p| p.title }.join(", ") %> <% unless @person.is_client? %> Not for clients to see... <% end %> {Learn more}[link:classes/ActionView.html] * "Builder" templates (great for XML content, like RSS) xml.rss("version" => "2.0") do xml.channel do xml.title(@feed_title) xml.link(@url) xml.description "Basecamp: Recent items" xml.language "en-us" xml.ttl "40" @recent_items.each do |item| xml.item do xml.title(item_title(item)) xml.description(item_description(item)) xml.pubDate(item_pubDate(item)) xml.guid(@recent_items.url(item)) xml.link(@recent_items.url(item)) end end end end {Learn more}[link:classes/ActionView/Base.html] * Filters for pre- and post-processing of the response class WeblogController < ActionController::Base # filters as methods before_filter :authenticate, :cache, :audit # filter as a proc after_filter { |c| c.response.body = Gzip::compress(c.response.body) } # class filter after_filter LocalizeFilter def index # Before this action is run, the user will be authenticated, the cache # will be examined to see if a valid copy of the results already # exists, and the action will be logged for auditing. # After this action has run, the output will first be localized then # compressed to minimize bandwidth usage end private def authenticate # Implement the filter with full access to both request and response end end {Learn more}[link:classes/ActionController/Filters/ClassMethods.html] * Helpers for forms, dates, action links, and text <%= text_field_tag "post", "title", "size" => 30 %> <%= link_to "New post", :controller => "post", :action => "new" %> <%= truncate(post.title, :length => 25) %> {Learn more}[link:classes/ActionView/Helpers.html] * Layout sharing for template reuse class WeblogController < ActionController::Base layout "weblog_layout" def hello_world end end Layout file (called weblog_layout): <%= yield %> Template for hello_world action:

Hello world

Result of running hello_world action:

Hello world

{Learn more}[link:classes/ActionController/Layout/ClassMethods.html] * Routing makes pretty URLs incredibly easy match 'clients/:client_name/:project_name/:controller/:action' Accessing "/clients/37signals/basecamp/project/index" calls ProjectController#index with { "client_name" => "37signals", "project_name" => "basecamp" } in `params` From that action, you can write the redirect in a number of ways: redirect_to(:action => "edit") => /clients/37signals/basecamp/project/edit redirect_to(:client_name => "nextangle", :project_name => "rails") => /clients/nextangle/rails/project/index {Learn more}[link:classes/ActionDispatch/Routing.html] * Easy testing of both controller and rendered template through ActionController::TestCase class LoginControllerTest < ActionController::TestCase def test_failing_authenticate process :authenticate, :user_name => "nop", :password => "" assert flash.has_key?(:alert) assert_redirected_to :action => "index" end end {Learn more}[link:classes/ActionController/TestCase.html] * Automated benchmarking and integrated logging Started GET "/weblog" for 127.0.0.1 at Fri May 28 00:41:55 Processing by WeblogController#index as HTML Rendered weblog/index.html.erb within layouts/application (25.7ms) Completed 200 OK in 29.3ms If Active Record is used as the model, you'll have the database debugging as well: Started POST "/posts" for 127.0.0.1 at Sat Jun 19 14:04:23 Processing by PostsController#create as HTML Parameters: {"post"=>{"title"=>"this is good"}} SQL (0.6ms) INSERT INTO posts (title) VALUES('this is good') Redirected to http://example.com/posts/5 Completed 302 Found in 221ms (Views: 215ms | ActiveRecord: 0.6ms) You specify a logger through a class method, such as: ActionController::Base.logger = Logger.new("Application Log") ActionController::Base.logger = Log4r::Logger.new("Application Log") * Caching at three levels of granularity (page, action, fragment) class WeblogController < ActionController::Base caches_page :show caches_action :account def show # the output of the method will be cached as # ActionController::Base.page_cache_directory + "/weblog/show.html" # and the web server will pick it up without even hitting Rails end def account # the output of the method will be cached in the fragment store # but Rails is hit to retrieve it, so filters are run end def update List.update(params[:list][:id], params[:list]) expire_page :action => "show", :id => params[:list][:id] expire_action :action => "account" redirect_to :action => "show", :id => params[:list][:id] end end {Learn more}[link:classes/ActionController/Caching.html] * Powerful debugging mechanism for local requests All exceptions raised on actions performed on the request of a local user will be presented with a tailored debugging screen that includes exception message, stack trace, request parameters, session contents, and the half-finished response. {Learn more}[link:classes/ActionController/Rescue.html] == Simple example (from outside of Rails) This example will implement a simple weblog system using inline templates and an Active Record model. So let's build that WeblogController with just a few methods: require 'action_controller' require 'post' class WeblogController < ActionController::Base layout "weblog/layout" def index @posts = Post.all end def show @post = Post.find(params[:id]) end def new @post = Post.new end def create @post = Post.create(params[:post]) redirect_to :action => "show", :id => @post.id end end WeblogController::Base.view_paths = [ File.dirname(__FILE__) ] WeblogController.process_cgi if $0 == __FILE__ The last two lines are responsible for telling ActionController where the template files are located and actually running the controller on a new request from the web-server (e.g., Apache). And the templates look like this: weblog/layout.html.erb: <%= yield %> weblog/index.html.erb: <% @posts.each do |post| %>

<%= link_to(post.title, :action => "show", :id => post.id) %>

<% end %> weblog/show.html.erb:

<%= @post.title %>
<%= @post.content %>

weblog/new.html.erb: <%= form "post" %> This simple setup will list all the posts in the system on the index page, which is called by accessing /weblog/. It uses the form builder for the Active Record model to make the new screen, which in turn hands everything over to the create action (that's the default target for the form builder when given a new model). After creating the post, it'll redirect to the show page using an URL such as /weblog/5 (where 5 is the id of the post). == Download and installation The latest version of Action Pack can be installed with RubyGems: % [sudo] gem install actionpack Source code can be downloaded as part of the Rails project on GitHub * https://github.com/rails/rails/tree/3-2-stable/actionpack == License Action Pack is released under the MIT license. == Support API documentation is at * http://api.rubyonrails.org Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: * https://github.com/rails/rails/issues actionpack-3.2.16/checksums.yaml.gz0000444000175000017500000000041012247655372016541 0ustar ondrejondrej#ReP9R0 | ntt@d*&ǁffvy{}󕗙*+*,j(CfCCHݏ:6YT[Ժ㑪Hrܳdl=v20XN:uP;e4ẘq/uX@.8I1C@}u9Kf;74v2_ԛD 7:X\d1|s);X >a*Uo\4eactionpack-3.2.16/lib/0000755000175000017500000000000012247655372014026 5ustar ondrejondrejactionpack-3.2.16/lib/action_view/0000755000175000017500000000000012247655372016335 5ustar ondrejondrejactionpack-3.2.16/lib/action_view/path_set.rb0000644000175000017500000000311012247655372020464 0ustar ondrejondrejmodule ActionView #:nodoc: # = Action View PathSet class PathSet #:nodoc: include Enumerable attr_reader :paths def initialize(paths = []) @paths = typecast paths end def initialize_copy(other) @paths = other.paths.dup self end def [](i) paths[i] end def to_ary paths.dup end def include?(item) paths.include? item end def pop paths.pop end def size paths.size end def each(&block) paths.each(&block) end def compact PathSet.new paths.compact end def +(array) PathSet.new(paths + array) end %w(<< concat push insert unshift).each do |method| class_eval <<-METHOD, __FILE__, __LINE__ + 1 def #{method}(*args) paths.#{method}(*typecast(args)) end METHOD end def find(*args) find_all(*args).first || raise(MissingTemplate.new(self, *args)) end def find_all(path, prefixes = [], *args) prefixes = [prefixes] if String === prefixes prefixes.each do |prefix| paths.each do |resolver| templates = resolver.find_all(path, prefix, *args) return templates unless templates.empty? end end [] end def exists?(path, prefixes, *args) find_all(path, prefixes, *args).any? end private def typecast(paths) paths.map do |path| case path when Pathname, String OptimizedFileSystemResolver.new path.to_s else path end end end end end actionpack-3.2.16/lib/action_view/log_subscriber.rb0000644000175000017500000000162712247655372021674 0ustar ondrejondrejmodule ActionView # = Action View Log Subscriber # # Provides functionality so that Rails can output logs from Action View. class LogSubscriber < ActiveSupport::LogSubscriber def render_template(event) message = " Rendered #{from_rails_root(event.payload[:identifier])}" message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout] message << (" (%.1fms)" % event.duration) info(message) end alias :render_partial :render_template alias :render_collection :render_template # TODO: Ideally, ActionView should have its own logger so it does not depend on AC.logger def logger ActionController::Base.logger if defined?(ActionController::Base) end protected def from_rails_root(string) string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "") end end end ActionView::LogSubscriber.attach_to :action_view actionpack-3.2.16/lib/action_view/lookup_context.rb0000644000175000017500000001740112247655372021742 0ustar ondrejondrejrequire 'active_support/core_ext/array/wrap' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/module/remove_method' module ActionView # = Action View Lookup Context # # LookupContext is the object responsible to hold all information required to lookup # templates, i.e. view paths and details. The LookupContext is also responsible to # generate a key, given to view paths, used in the resolver cache lookup. Since # this key is generated just once during the request, it speeds up all cache accesses. class LookupContext #:nodoc: attr_accessor :prefixes, :rendered_format mattr_accessor :fallbacks @@fallbacks = FallbackFileSystemResolver.instances mattr_accessor :registered_details self.registered_details = [] def self.register_detail(name, options = {}, &block) self.registered_details << name initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" } Accessors.send :define_method, :"default_#{name}", &block Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1 def #{name} @details[:#{name}] end def #{name}=(value) value = value.present? ? Array.wrap(value) : default_#{name} _set_detail(:#{name}, value) if value != @details[:#{name}] end remove_possible_method :initialize_details def initialize_details(details) #{initialize.join("\n")} end METHOD end # Holds accessors for the registered details. module Accessors #:nodoc: end register_detail(:locale) do locales = [I18n.locale] locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks locales << I18n.default_locale locales.uniq! locales end register_detail(:formats) { Mime::SET.symbols } register_detail(:handlers){ Template::Handlers.extensions } class DetailsKey #:nodoc: alias :eql? :equal? alias :object_hash :hash attr_reader :hash @details_keys = Hash.new def self.get(details) if details[:formats] details = details.dup syms = Set.new Mime::SET.symbols details[:formats] = details[:formats].select { |v| syms.include? v } end @details_keys[details] ||= new end def self.clear @details_keys.clear end def initialize @hash = object_hash end end # Add caching behavior on top of Details. module DetailsCache attr_accessor :cache # Calculate the details key. Remove the handlers from calculation to improve performance # since the user cannot modify it explicitly. def details_key #:nodoc: @details_key ||= DetailsKey.get(@details) if @cache end # Temporary skip passing the details_key forward. def disable_cache old_value, @cache = @cache, false yield ensure @cache = old_value end protected def _set_detail(key, value) @details = @details.dup if @details_key @details_key = nil @details[key] = value end end # Helpers related to template lookup using the lookup context information. module ViewPaths attr_reader :view_paths # Whenever setting view paths, makes a copy so we can manipulate then in # instance objects as we wish. def view_paths=(paths) @view_paths = ActionView::PathSet.new(Array.wrap(paths)) end def find(name, prefixes = [], partial = false, keys = [], options = {}) @view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options)) end alias :find_template :find def find_all(name, prefixes = [], partial = false, keys = [], options = {}) @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options)) end def exists?(name, prefixes = [], partial = false, keys = [], options = {}) @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options)) end alias :template_exists? :exists? # Add fallbacks to the view paths. Useful in cases you are rendering a :file. def with_fallbacks added_resolvers = 0 self.class.fallbacks.each do |resolver| next if view_paths.include?(resolver) view_paths.push(resolver) added_resolvers += 1 end yield ensure added_resolvers.times { view_paths.pop } end protected def args_for_lookup(name, prefixes, partial, keys, details_options) #:nodoc: name, prefixes = normalize_name(name, prefixes) details, details_key = detail_args_for(details_options) [name, prefixes, partial || false, details, details_key, keys] end # Compute details hash and key according to user options (e.g. passed from #render). def detail_args_for(options) return @details, details_key if options.empty? # most common path. user_details = @details.merge(options) [user_details, DetailsKey.get(user_details)] end # Support legacy foo.erb names even though we now ignore .erb # as well as incorrectly putting part of the path in the template # name instead of the prefix. def normalize_name(name, prefixes) #:nodoc: name = name.to_s.sub(handlers_regexp) do |match| ActiveSupport::Deprecation.warn "Passing a template handler in the template name is deprecated. " \ "You can simply remove the handler name or pass render :handlers => [:#{match[1..-1]}] instead.", caller "" end prefixes = nil if prefixes.blank? parts = name.split('/') name = parts.pop return name, prefixes || [""] if parts.empty? parts = parts.join('/') prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts] return name, prefixes end def handlers_regexp #:nodoc: @@handlers_regexp ||= /\.(?:#{default_handlers.join('|')})$/ end end include Accessors include DetailsCache include ViewPaths def initialize(view_paths, details = {}, prefixes = []) @details, @details_key = {}, nil @skip_default_locale = false @cache = true @prefixes = prefixes @rendered_format = nil self.view_paths = view_paths initialize_details(details) end # Override formats= to expand ["*/*"] values and automatically # add :html as fallback to :js. def formats=(values) if values values.concat(default_formats) if values.delete "*/*" values << :html if values == [:js] end super(values) end # Do not use the default locale on template lookup. def skip_default_locale! @skip_default_locale = true self.locale = nil end # Override locale to return a symbol instead of array. def locale @details[:locale].first end # Overload locale= to also set the I18n.locale. If the current I18n.config object responds # to original_config, it means that it's has a copy of the original I18n configuration and it's # acting as proxy, which we need to skip. def locale=(value) if value config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config config.locale = value end super(@skip_default_locale ? I18n.locale : default_locale) end # A method which only uses the first format in the formats array for layout lookup. def with_layout_format if formats.size == 1 yield else old_formats = formats _set_detail(:formats, formats[0,1]) begin yield ensure _set_detail(:formats, old_formats) end end end end end actionpack-3.2.16/lib/action_view/helpers.rb0000644000175000017500000000276212247655372020333 0ustar ondrejondrejrequire 'active_support/benchmarkable' module ActionView #:nodoc: module Helpers #:nodoc: extend ActiveSupport::Autoload autoload :ActiveModelHelper autoload :AssetTagHelper autoload :AtomFeedHelper autoload :CacheHelper autoload :CaptureHelper autoload :ControllerHelper autoload :CsrfHelper autoload :DateHelper autoload :DebugHelper autoload :FormHelper autoload :FormOptionsHelper autoload :FormTagHelper autoload :JavaScriptHelper, "action_view/helpers/javascript_helper" autoload :NumberHelper autoload :OutputSafetyHelper autoload :RecordTagHelper autoload :RenderingHelper autoload :SanitizeHelper autoload :TagHelper autoload :TextHelper autoload :TranslationHelper autoload :UrlHelper extend ActiveSupport::Concern included do extend SanitizeHelper::ClassMethods end include ActiveSupport::Benchmarkable include ActiveModelHelper include AssetTagHelper include AtomFeedHelper include CacheHelper include CaptureHelper include ControllerHelper include CsrfHelper include DateHelper include DebugHelper include FormHelper include FormOptionsHelper include FormTagHelper include JavaScriptHelper include NumberHelper include OutputSafetyHelper include RecordTagHelper include RenderingHelper include SanitizeHelper include TagHelper include TextHelper include TranslationHelper include UrlHelper end end actionpack-3.2.16/lib/action_view/helpers/0000755000175000017500000000000012247655372017777 5ustar ondrejondrejactionpack-3.2.16/lib/action_view/helpers/active_model_helper.rb0000644000175000017500000000236512247655372024324 0ustar ondrejondrejrequire 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/object/blank' module ActionView # = Active Model Helpers module Helpers module ActiveModelHelper end module ActiveModelInstanceTag def object @active_model_object ||= begin object = super object.respond_to?(:to_model) ? object.to_model : object end end %w(content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| module_eval "def #{meth}(*) error_wrapping(super) end", __FILE__, __LINE__ end def tag(type, options, *) tag_generate_errors?(options) ? error_wrapping(super) : super end def error_wrapping(html_tag) if object_has_errors? Base.field_error_proc.call(html_tag, self) else html_tag end end def error_message object.errors[@method_name] end private def object_has_errors? object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && error_message.any? end def tag_generate_errors?(options) options['type'] != 'hidden' end end end end actionpack-3.2.16/lib/action_view/helpers/url_helper.rb0000644000175000017500000007552312247655372022501 0ustar ondrejondrejrequire 'action_view/helpers/javascript_helper' require 'active_support/core_ext/array/access' require 'active_support/core_ext/hash/keys' require 'active_support/core_ext/string/output_safety' require 'action_dispatch' module ActionView # = Action View URL Helpers module Helpers #:nodoc: # Provides a set of methods for making links and getting URLs that # depend on the routing subsystem (see ActionDispatch::Routing). # This allows you to use the same format for links in views # and controllers. module UrlHelper # This helper may be included in any class that includes the # URL helpers of a routes (routes.url_helpers). Some methods # provided here will only work in the context of a request # (link_to_unless_current, for instance), which must be provided # as a method called #request on the context. extend ActiveSupport::Concern include ActionDispatch::Routing::UrlFor include TagHelper def _routes_context controller end # Need to map default url options to controller one. # def default_url_options(*args) #:nodoc: # controller.send(:default_url_options, *args) # end # def url_options return super unless controller.respond_to?(:url_options) controller.url_options end # Returns the URL for the set of +options+ provided. This takes the # same options as +url_for+ in Action Controller (see the # documentation for ActionController::Base#url_for). Note that by default # :only_path is true so you'll get the relative "/controller/action" # instead of the fully qualified URL like "http://example.com/controller/action". # # ==== Options # * :anchor - Specifies the anchor name to be appended to the path. # * :only_path - If true, returns the relative URL (omitting the protocol, host name, and port) (true by default unless :host is specified). # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2005/". Note that this # is currently not recommended since it breaks caching. # * :host - Overrides the default (current) host if provided. # * :protocol - Overrides the default (current) protocol if provided. # * :user - Inline HTTP authentication (only plucked out if :password is also present). # * :password - Inline HTTP authentication (only plucked out if :user is also present). # # ==== Relying on named routes # # Passing a record (like an Active Record or Active Resource) instead of a Hash as the options parameter will # trigger the named route for that record. The lookup will happen on the name of the class. So passing a # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route). # # ==== Examples # <%= url_for(:action => 'index') %> # # => /blog/ # # <%= url_for(:action => 'find', :controller => 'books') %> # # => /books/find # # <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %> # # => https://www.example.com/members/login/ # # <%= url_for(:action => 'play', :anchor => 'player') %> # # => /messages/play/#player # # <%= url_for(:action => 'jump', :anchor => 'tax&ship') %> # # => /testing/jump/#tax&ship # # <%= url_for(Workshop.new) %> # # relies on Workshop answering a persisted? call (and in this case returning false) # # => /workshops # # <%= url_for(@workshop) %> # # calls @workshop.to_param which by default returns the id # # => /workshops/5 # # # to_param can be re-defined in a model to provide different URL names: # # => /workshops/1-workshop-name # # <%= url_for("http://www.example.com") %> # # => http://www.example.com # # <%= url_for(:back) %> # # if request.env["HTTP_REFERER"] is set to "http://www.example.com" # # => http://www.example.com # # <%= url_for(:back) %> # # if request.env["HTTP_REFERER"] is not set or is blank # # => javascript:history.back() def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(:only_path => options[:host].nil?) super when :back controller.request.env["HTTP_REFERER"] || 'javascript:history.back()' else polymorphic_path(options) end end # Creates a link tag of the given +name+ using a URL created by the set of +options+. # See the valid options in the documentation for +url_for+. It's also possible to # pass a String instead of an options hash, which generates a link tag that uses the # value of the String as the href for the link. Using a :back Symbol instead # of an options hash will generate a link to the referrer (a JavaScript back link # will be used in place of a referrer if none exists). If +nil+ is passed as the name # the value of the link itself will become the name. # # ==== Signatures # # link_to(body, url, html_options = {}) # # url is a String; you can use URL helpers like # # posts_path # # link_to(body, url_options = {}, html_options = {}) # # url_options, except :confirm or :method, # # is passed to url_for # # link_to(options = {}, html_options = {}) do # # name # end # # link_to(url, html_options = {}) do # # name # end # # ==== Options # * :confirm => 'question?' - This will allow the unobtrusive JavaScript # driver to prompt with the question specified. If the user accepts, the link is # processed normally, otherwise no action is taken. # * :method => symbol of HTTP verb - This modifier will dynamically # create an HTML form and immediately submit the form for processing using # the HTTP verb specified. Useful for having links perform a POST operation # in dangerous actions like deleting a record (which search bots can follow # while spidering your site). Supported verbs are :post, :delete and :put. # Note that if the user has JavaScript disabled, the request will fall back # to using GET. If :href => '#' is used and the user has JavaScript # disabled clicking the link will have no effect. If you are relying on the # POST behavior, you should check for it in your controller's action by using # the request object's methods for post?, delete? or put?. # * :remote => true - This will allow the unobtrusive JavaScript # driver to make an Ajax request to the URL in question instead of following # the link. The drivers each provide mechanisms for listening for the # completion of the Ajax request and performing JavaScript operations once # they're complete # # ==== Examples # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base # your application on resources and use # # link_to "Profile", profile_path(@profile) # # => Profile # # or the even pithier # # link_to "Profile", @profile # # => Profile # # in place of the older more verbose, non-resource-oriented # # link_to "Profile", :controller => "profiles", :action => "show", :id => @profile # # => Profile # # Similarly, # # link_to "Profiles", profiles_path # # => Profiles # # is better than # # link_to "Profiles", :controller => "profiles" # # => Profiles # # You can use a block as well if your link target is hard to fit into the name parameter. ERB example: # # <%= link_to(@profile) do %> # <%= @profile.name %> -- Check it out! # <% end %> # # => # David -- Check it out! # # # Classes and ids for CSS are easy to produce: # # link_to "Articles", articles_path, :id => "news", :class => "article" # # => Articles # # Be careful when using the older argument style, as an extra literal hash is needed: # # link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article" # # => Articles # # Leaving the hash off gives the wrong link: # # link_to "WRONG!", :controller => "articles", :id => "news", :class => "article" # # => WRONG! # # +link_to+ can also produce links with anchors or query strings: # # link_to "Comment wall", profile_path(@profile, :anchor => "wall") # # => Comment wall # # link_to "Ruby on Rails search", :controller => "searches", :query => "ruby on rails" # # => Ruby on Rails search # # link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux") # # => Nonsense search # # The two options specific to +link_to+ (:confirm and :method) are used as follows: # # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?" # # => Visit Other Site # # link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?") # # => Destroy def link_to(*args, &block) if block_given? options = args.first || {} html_options = args.second link_to(capture(&block), options, html_options) else name = args[0] options = args[1] || {} html_options = args[2] html_options = convert_options_to_data_attributes(options, html_options) url = url_for(options) href = html_options['href'] tag_options = tag_options(html_options) href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href "#{ERB::Util.html_escape(name || url)}".html_safe end end # Generates a form containing a single button that submits to the URL created # by the set of +options+. This is the safest method to ensure links that # cause changes to your data are not triggered by search bots or accelerators. # If the HTML button does not work with your layout, you can also consider # using the +link_to+ method with the :method modifier as described in # the +link_to+ documentation. # # By default, the generated form element has a class name of button_to # to allow styling of the form itself and its children. This can be changed # using the :form_class modifier within +html_options+. You can control # the form submission and input element behavior using +html_options+. # This method accepts the :method and :confirm modifiers # described in the +link_to+ documentation. If no :method modifier # is given, it will default to performing a POST operation. You can also # disable the button by passing :disabled => true in +html_options+. # If you are using RESTful routes, you can pass the :method # to change the HTTP verb used to submit the form. # # ==== Options # The +options+ hash accepts the same options as +url_for+. # # There are a few special +html_options+: # * :method - Symbol of HTTP verb. Supported verbs are :post, :get, # :delete and :put. By default it will be :post. # * :disabled - If set to true, it will generate a disabled button. # * :confirm - This will use the unobtrusive JavaScript driver to # prompt with the question specified. If the user accepts, the link is # processed normally, otherwise no action is taken. # * :remote - If set to true, will allow the Unobtrusive JavaScript drivers to control the # submit behavior. By default this behavior is an ajax submit. # * :form - This hash will be form attributes # * :form_class - This controls the class of the form within which the submit button will # be placed # # ==== Examples # <%= button_to "New", :action => "new" %> # # => " # #
# #
" # # # <%= button_to "New", :action => "new", :form_class => "new-thing" %> # # => "
# #
# #
" # # # <%= button_to "Create", :action => "create", :remote => true, :form => { "data-type" => "json" } %> # # => "
# #
# #
" # # # <%= button_to "Delete Image", { :action => "delete", :id => @image.id }, # :confirm => "Are you sure?", :method => :delete %> # # => "
# #
# # # # # #
# #
" # # # <%= button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?', # :method => "delete", :remote => true, :disable_with => 'loading...') %> # # => "
# #
# # # # # #
# #
" # # def button_to(name, options = {}, html_options = {}) html_options = html_options.stringify_keys convert_boolean_attributes!(html_options, %w( disabled )) method_tag = '' if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s) method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s) end form_method = method.to_s == 'get' ? 'get' : 'post' form_options = html_options.delete('form') || {} form_options[:class] ||= html_options.delete('form_class') || 'button_to' remote = html_options.delete('remote') request_token_tag = '' if form_method == 'post' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end url = options.is_a?(String) ? options : self.url_for(options) name ||= url html_options = convert_options_to_data_attributes(options, html_options) html_options.merge!("type" => "submit", "value" => name) form_options.merge!(:method => form_method, :action => url) form_options.merge!("data-remote" => "true") if remote "#{tag(:form, form_options, true)}
#{method_tag}#{tag("input", html_options)}#{request_token_tag}
".html_safe end # Creates a link tag of the given +name+ using a URL created by the set of # +options+ unless the current request URI is the same as the links, in # which case only the name is returned (or the given block is yielded, if # one exists). You can give +link_to_unless_current+ a block which will # specialize the default behavior (e.g., show a "Start Here" link rather # than the link's text). # # ==== Examples # Let's say you have a navigation menu... # # # # If in the "about" action, it will render... # # # # ...but if in the "index" action, it will render: # # # # The implicit block given to +link_to_unless_current+ is evaluated if the current # action is the action given. So, if we had a comments page and wanted to render a # "Go Back" link instead of a link to the comments page, we could do something like this... # # <%= # link_to_unless_current("Comment", { :controller => "comments", :action => "new" }) do # link_to("Go back", { :controller => "posts", :action => "index" }) # end # %> def link_to_unless_current(name, options = {}, html_options = {}, &block) link_to_unless current_page?(options), name, options, html_options, &block end # Creates a link tag of the given +name+ using a URL created by the set of # +options+ unless +condition+ is true, in which case only the name is # returned. To specialize the default behavior (i.e., show a login link rather # than just the plaintext link text), you can pass a block that # accepts the name or the full argument list for +link_to_unless+. # # ==== Examples # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %> # # If the user is logged in... # # => Reply # # <%= # link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name| # link_to(name, { :controller => "accounts", :action => "signup" }) # end # %> # # If the user is logged in... # # => Reply # # If not... # # => Reply def link_to_unless(condition, name, options = {}, html_options = {}, &block) if condition if block_given? block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block) else ERB::Util.html_escape(name) end else link_to(name, options, html_options) end end # Creates a link tag of the given +name+ using a URL created by the set of # +options+ if +condition+ is true, otherwise only the name is # returned. To specialize the default behavior, you can pass a block that # accepts the name or the full argument list for +link_to_unless+ (see the examples # in +link_to_unless+). # # ==== Examples # <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %> # # If the user isn't logged in... # # => Login # # <%= # link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do # link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user }) # end # %> # # If the user isn't logged in... # # => Login # # If they are logged in... # # => my_username def link_to_if(condition, name, options = {}, html_options = {}, &block) link_to_unless !condition, name, options, html_options, &block end # Creates a mailto link tag to the specified +email_address+, which is # also used as the name of the link unless +name+ is specified. Additional # HTML attributes for the link can be passed in +html_options+. # # +mail_to+ has several methods for hindering email harvesters and customizing # the email itself by passing special keys to +html_options+. # # ==== Options # * :encode - This key will accept the strings "javascript" or "hex". # Passing "javascript" will dynamically create and encode the mailto link then # eval it into the DOM of the page. This method will not show the link on # the page if the user has JavaScript disabled. Passing "hex" will hex # encode the +email_address+ before outputting the mailto link. # * :replace_at - When the link +name+ isn't provided, the # +email_address+ is used for the link label. You can use this option to # obfuscate the +email_address+ by substituting the @ sign with the string # given as the value. # * :replace_dot - When the link +name+ isn't provided, the # +email_address+ is used for the link label. You can use this option to # obfuscate the +email_address+ by substituting the . in the email with the # string given as the value. # * :subject - Preset the subject line of the email. # * :body - Preset the body of the email. # * :cc - Carbon Copy additional recipients on the email. # * :bcc - Blind Carbon Copy additional recipients on the email. # # ==== Examples # mail_to "me@domain.com" # # => me@domain.com # # mail_to "me@domain.com", "My email", :encode => "javascript" # # => # # mail_to "me@domain.com", "My email", :encode => "hex" # # => My email # # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email" # # => # # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com", # :subject => "This is an example email" # # => My email def mail_to(email_address, name = nil, html_options = {}) email_address = ERB::Util.html_escape(email_address) html_options = html_options.stringify_keys encode = html_options.delete("encode").to_s extras = %w{ cc bcc body subject }.map { |item| option = html_options.delete(item) || next "#{item}=#{Rack::Utils.escape(option).gsub("+", "%20")}" }.compact extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&')) email_address_obfuscated = email_address.to_str email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at") email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot") case encode when "javascript" string = '' html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)) html = escape_javascript(html.to_str) "document.write('#{html}');".each_byte do |c| string << sprintf("%%%x", c) end "".html_safe when "hex" email_address_encoded = email_address_obfuscated.unpack('C*').map {|c| sprintf("&#%d;", c) }.join string = 'mailto:'.unpack('C*').map { |c| sprintf("&#%d;", c) }.join + email_address.unpack('C*').map { |c| char = c.chr char =~ /\w/ ? sprintf("%%%x", c) : char }.join content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe) else content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe) end end # True if the current request URI was generated by the given +options+. # # ==== Examples # Let's say we're in the /shop/checkout?order=desc action. # # current_page?(:action => 'process') # # => false # # current_page?(:controller => 'shop', :action => 'checkout') # # => true # # current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc') # # => false # # current_page?(:action => 'checkout') # # => true # # current_page?(:controller => 'library', :action => 'checkout') # # => false # # Let's say we're in the /shop/checkout?order=desc&page=1 action. # # current_page?(:action => 'process') # # => false # # current_page?(:controller => 'shop', :action => 'checkout') # # => true # # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '1') # # => true # # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '2') # # => false # # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc') # # => false # # current_page?(:action => 'checkout') # # => true # # current_page?(:controller => 'library', :action => 'checkout') # # => false # # Let's say we're in the /products action with method POST in case of invalid product. # # current_page?(:controller => 'product', :action => 'index') # # => false # def current_page?(options) unless request raise "You cannot use helpers that need to determine the current " \ "page unless your view context provides a Request object " \ "in a #request method" end return false unless request.get? url_string = url_for(options) # We ignore any extra parameters in the request_uri if the # submitted url doesn't have any either. This lets the function # work with things like ?order=asc if url_string.index("?") request_uri = request.fullpath else request_uri = request.path end if url_string =~ /^\w+:\/\// url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}" else url_string == request_uri end end private def convert_options_to_data_attributes(options, html_options) if html_options html_options = html_options.stringify_keys html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options) disable_with = html_options.delete("disable_with") confirm = html_options.delete('confirm') method = html_options.delete('method') html_options["data-disable-with"] = disable_with if disable_with html_options["data-confirm"] = confirm if confirm add_method_to_attributes!(html_options, method) if method html_options else link_to_remote_options?(options) ? {'data-remote' => 'true'} : {} end end def link_to_remote_options?(options) if options.is_a?(Hash) options.delete('remote') || options.delete(:remote) end end def add_method_to_attributes!(html_options, method) if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/ html_options["rel"] = "#{html_options["rel"]} nofollow".strip end html_options["data-method"] = method end def options_for_javascript(options) if options.empty? '{}' else "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}" end end def array_or_string_for_javascript(option) if option.kind_of?(Array) "['#{option.join('\',\'')}']" elsif !option.nil? "'#{option}'" end end # Processes the +html_options+ hash, converting the boolean # attributes from true/false form into the form required by # HTML/XHTML. (An attribute is considered to be boolean if # its name is listed in the given +bool_attrs+ array.) # # More specifically, for each boolean attribute in +html_options+ # given as: # # "attr" => bool_value # # if the associated +bool_value+ evaluates to true, it is # replaced with the attribute's name; otherwise the attribute is # removed from the +html_options+ hash. (See the XHTML 1.0 spec, # section 4.5 "Attribute Minimization" for more: # http://www.w3.org/TR/xhtml1/#h-4.5) # # Returns the updated +html_options+ hash, which is also modified # in place. # # Example: # # convert_boolean_attributes!( html_options, # %w( checked disabled readonly ) ) def convert_boolean_attributes!(html_options, bool_attrs) bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) } html_options end end end end actionpack-3.2.16/lib/action_view/helpers/controller_helper.rb0000644000175000017500000000152112247655372024045 0ustar ondrejondrejrequire 'active_support/core_ext/module/attr_internal' module ActionView module Helpers # This module keeps all methods and behavior in ActionView # that simply delegates to the controller. module ControllerHelper #:nodoc: attr_internal :controller, :request delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers, :flash, :action_name, :controller_name, :controller_path, :to => :controller def assign_controller(controller) if @_controller = controller @_request = controller.request if controller.respond_to?(:request) @_config = controller.config.inheritable_copy if controller.respond_to?(:config) end end def logger controller.logger if controller.respond_to?(:logger) end end end end actionpack-3.2.16/lib/action_view/helpers/text_helper.rb0000644000175000017500000004225012247655372022652 0ustar ondrejondrejrequire 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/filters' module ActionView # = Action View Text Helpers module Helpers #:nodoc: # The TextHelper module provides a set of methods for filtering, formatting # and transforming strings, which can reduce the amount of inline Ruby code in # your views. These helper methods extend Action View making them callable # within your template files. # # ==== Sanitization # # Most text helpers by default sanitize the given content, but do not escape it. # This means HTML tags will appear in the page but all malicious code will be removed. # Let's look at some examples using the +simple_format+ method: # # simple_format('Example') # # => "

Example

" # # simple_format('Example') # # => "

Example

" # # If you want to escape all content, you should invoke the +h+ method before # calling the text helper. # # simple_format h('Example') # # => "

<a href=\"http://example.com/\">Example</a>

" module TextHelper extend ActiveSupport::Concern include SanitizeHelper include TagHelper # The preferred method of outputting text in your views is to use the # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods # do not operate as expected in an eRuby code block. If you absolutely must # output text within a non-output code block (i.e., <% %>), you can use the concat method. # # ==== Examples # <% # concat "hello" # # is the equivalent of <%= "hello" %> # # if logged_in # concat "Logged in!" # else # concat link_to('login', :action => login) # end # # will either display "Logged in!" or a login link # %> def concat(string) output_buffer << string end def safe_concat(string) output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string) end # Truncates a given +text+ after a given :length if +text+ is longer than :length # (defaults to 30). The last characters will be replaced with the :omission (defaults to "...") # for a total length not exceeding :length. # # Pass a :separator to truncate +text+ at a natural break. # # The result is not marked as HTML-safe, so will be subject to the default escaping when # used in views, unless wrapped by raw(). Care should be taken if +text+ contains HTML tags # or entities, because truncation may produce invalid HTML (such as unbalanced or incomplete tags). # # ==== Examples # # truncate("Once upon a time in a world far far away") # # => "Once upon a time in a world..." # # truncate("Once upon a time in a world far far away", :length => 17) # # => "Once upon a ti..." # # truncate("Once upon a time in a world far far away", :length => 17, :separator => ' ') # # => "Once upon a..." # # truncate("And they found that many people were sleeping better.", :length => 25, :omission => '... (continued)') # # => "And they f... (continued)" # # truncate("

Once upon a time in a world far far away

") # # => "

Once upon a time in a wo..." def truncate(text, options = {}) options.reverse_merge!(:length => 30) text.truncate(options.delete(:length), options) if text end # Highlights one or more +phrases+ everywhere in +text+ by inserting it into # a :highlighter string. The highlighter can be specialized by passing :highlighter # as a single-quoted string with \1 where the phrase is to be inserted (defaults to # '\1') # # ==== Examples # highlight('You searched for: rails', 'rails') # # => You searched for: rails # # highlight('You searched for: ruby, rails, dhh', 'actionpack') # # => You searched for: ruby, rails, dhh # # highlight('You searched for: rails', ['for', 'rails'], :highlighter => '\1') # # => You searched for: rails # # highlight('You searched for: rails', 'rails', :highlighter => '\1') # # => You searched for: rails # # You can still use highlight with the old API that accepts the # +highlighter+ as its optional third parameter: # highlight('You searched for: rails', 'rails', '\1') # => You searched for: rails def highlight(text, phrases, *args) options = args.extract_options! unless args.empty? ActiveSupport::Deprecation.warn "Calling highlight with a highlighter as an argument is deprecated. " \ "Please call with :highlighter => '#{args[0]}' instead.", caller options[:highlighter] = args[0] || '\1' end options.reverse_merge!(:highlighter => '\1') text = sanitize(text) unless options[:sanitize] == false if text.blank? || phrases.blank? text else match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') text.gsub(/(#{match})(?![^<]*?>)/i, options[:highlighter]) end.html_safe end # Extracts an excerpt from +text+ that matches the first instance of +phrase+. # The :radius option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters # defined in :radius (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+, # then the :omission option (which defaults to "...") will be prepended/appended accordingly. The resulting string # will be stripped in any case. If the +phrase+ isn't found, nil is returned. # # ==== Examples # excerpt('This is an example', 'an', :radius => 5) # # => ...s is an exam... # # excerpt('This is an example', 'is', :radius => 5) # # => This is a... # # excerpt('This is an example', 'is') # # => This is an example # # excerpt('This next thing is an example', 'ex', :radius => 2) # # => ...next... # # excerpt('This is also an example', 'an', :radius => 8, :omission => ' ') # # => is also an example # # You can still use excerpt with the old API that accepts the # +radius+ as its optional third and the +ellipsis+ as its # optional forth parameter: # excerpt('This is an example', 'an', 5) # => ...s is an exam... # excerpt('This is also an example', 'an', 8, ' ') # => is also an example def excerpt(text, phrase, *args) return unless text && phrase options = args.extract_options! unless args.empty? ActiveSupport::Deprecation.warn "Calling excerpt with radius and omission as arguments is deprecated. " \ "Please call with :radius => #{args[0]}#{", :omission => '#{args[1]}'" if args[1]} instead.", caller options[:radius] = args[0] || 100 options[:omission] = args[1] || "..." end options.reverse_merge!(:radius => 100, :omission => "...") phrase = Regexp.escape(phrase) return unless found_pos = text.mb_chars =~ /(#{phrase})/i start_pos = [ found_pos - options[:radius], 0 ].max end_pos = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min prefix = start_pos > 0 ? options[:omission] : "" postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : "" prefix + text.mb_chars[start_pos..end_pos].strip + postfix end # Attempts to pluralize the +singular+ word unless +count+ is 1. If # +plural+ is supplied, it will use that when count is > 1, otherwise # it will use the Inflector to determine the plural form # # ==== Examples # pluralize(1, 'person') # # => 1 person # # pluralize(2, 'person') # # => 2 people # # pluralize(3, 'person', 'users') # # => 3 users # # pluralize(0, 'person') # # => 0 people def pluralize(count, singular, plural = nil) "#{count || 0} " + ((count == 1 || count =~ /^1(\.0+)?$/) ? singular : (plural || singular.pluralize)) end # Wraps the +text+ into lines no longer than +line_width+ width. This method # breaks on the first whitespace character that does not exceed +line_width+ # (which is 80 by default). # # ==== Examples # # word_wrap('Once upon a time') # # => Once upon a time # # 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...') # # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\n a successor to the throne turned out to be more trouble than anyone could have\n imagined... # # word_wrap('Once upon a time', :line_width => 8) # # => Once upon\na time # # word_wrap('Once upon a time', :line_width => 1) # # => Once\nupon\na\ntime # # You can still use word_wrap with the old API that accepts the # +line_width+ as its optional second parameter: # word_wrap('Once upon a time', 8) # => Once upon\na time def word_wrap(text, *args) options = args.extract_options! unless args.blank? ActiveSupport::Deprecation.warn "Calling word_wrap with line_width as an argument is deprecated. " \ "Please call with :line_width => #{args[0]} instead.", caller options[:line_width] = args[0] || 80 end options.reverse_merge!(:line_width => 80) text.split("\n").collect do |line| line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line end * "\n" end # Returns +text+ transformed into HTML using simple formatting rules. # Two or more consecutive newlines(\n\n) are considered as a # paragraph and wrapped in

tags. One newline (\n) is # considered as a linebreak and a
tag is appended. This # method does not remove the newlines from the +text+. # # You can pass any HTML attributes into html_options. These # will be added to all created paragraphs. # # ==== Options # * :sanitize - If +false+, does not sanitize +text+. # # ==== Examples # my_text = "Here is some basic text...\n...with a line break." # # simple_format(my_text) # # => "

Here is some basic text...\n
...with a line break.

" # # more_text = "We want to put a paragraph...\n\n...right there." # # simple_format(more_text) # # => "

We want to put a paragraph...

\n\n

...right there.

" # # simple_format("Look ma! A class!", :class => 'description') # # => "

Look ma! A class!

" # # simple_format("I'm allowed! It's true.", {}, :sanitize => false) # # => "

I'm allowed! It's true.

" def simple_format(text, html_options={}, options={}) text = '' if text.nil? text = text.dup start_tag = tag('p', html_options, true) text = sanitize(text) unless options[:sanitize] == false text = text.to_str text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n text.gsub!(/\n\n+/, "

\n\n#{start_tag}") # 2+ newline -> paragraph text.gsub!(/([^\n]\n)(?=[^\n])/, '\1
') # 1 newline -> br text.insert 0, start_tag text.html_safe.safe_concat("

") end # Creates a Cycle object whose _to_s_ method cycles through elements of an # array every time it is called. This can be used for example, to alternate # classes for table rows. You can use named cycles to allow nesting in loops. # Passing a Hash as the last parameter with a :name key will create a # named cycle. The default name for a cycle without a +:name+ key is # "default". You can manually reset a cycle by calling reset_cycle # and passing the name of the cycle. The current cycle string can be obtained # anytime using the current_cycle method. # # ==== Examples # # Alternate CSS classes for even and odd numbers... # @items = [1,2,3,4] # # <% @items.each do |item| %> # "> # # # <% end %> #
item
# # # # Cycle CSS classes for rows, and text colors for values within each row # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'}, # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'}, # {:first => 'June', :middle => 'Dae', :last => 'Jones'}] # <% @items.each do |item| %> # "row_class") -%>"> # # <% item.values.each do |value| %> # <%# Create a named cycle "colors" %> # "colors") -%>"> # <%= value %> # # <% end %> # <% reset_cycle("colors") %> # # # <% end %> def cycle(first_value, *values) if (values.last.instance_of? Hash) params = values.pop name = params[:name] else name = "default" end values.unshift(first_value) cycle = get_cycle(name) unless cycle && cycle.values == values cycle = set_cycle(name, Cycle.new(*values)) end cycle.to_s end # Returns the current cycle string after a cycle has been started. Useful # for complex table highlighting or any other design need which requires # the current cycle string in more than one place. # # ==== Example # # Alternate background colors # @items = [1,2,3,4] # <% @items.each do |item| %> #
"> # <%= item %> #
# <% end %> def current_cycle(name = "default") cycle = get_cycle(name) cycle.current_value if cycle end # Resets a cycle so that it starts from the first element the next time # it is called. Pass in +name+ to reset a named cycle. # # ==== Example # # Alternate CSS classes for even and odd numbers... # @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]] # # <% @items.each do |item| %> # "> # <% item.each do |value| %> # "colors") -%>"> # <%= value %> # # <% end %> # # <% reset_cycle("colors") %> # # <% end %> #
def reset_cycle(name = "default") cycle = get_cycle(name) cycle.reset if cycle end class Cycle #:nodoc: attr_reader :values def initialize(first_value, *values) @values = values.unshift(first_value) reset end def reset @index = 0 end def current_value @values[previous_index].to_s end def to_s value = @values[@index].to_s @index = next_index return value end private def next_index step_index(1) end def previous_index step_index(-1) end def step_index(n) (@index + n) % @values.size end end private # The cycle helpers need to store the cycles in a place that is # guaranteed to be reset every time a page is rendered, so it # uses an instance variable of ActionView::Base. def get_cycle(name) @_cycles = Hash.new unless defined?(@_cycles) return @_cycles[name] end def set_cycle(name, cycle_object) @_cycles = Hash.new unless defined?(@_cycles) @_cycles[name] = cycle_object end end end end actionpack-3.2.16/lib/action_view/helpers/javascript_helper.rb0000644000175000017500000001053712247655372024037 0ustar ondrejondrejrequire 'action_view/helpers/tag_helper' require 'active_support/core_ext/string/encoding' module ActionView module Helpers module JavaScriptHelper JS_ESCAPE_MAP = { '\\' => '\\\\', ' '<\/', "\r\n" => '\n', "\n" => '\n', "\r" => '\n', '"' => '\\"', "'" => "\\'" } if "ruby".encoding_aware? JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
' else JS_ESCAPE_MAP["\342\200\250"] = '
' end # Escapes carriage returns and single and double quotes for JavaScript segments. # # Also available through the alias j(). This is particularly helpful in JavaScript responses, like: # # $('some_element').replaceWith('<%=j render 'some/element_template' %>'); def escape_javascript(javascript) if javascript result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] } javascript.html_safe? ? result.html_safe : result else '' end end alias_method :j, :escape_javascript # Returns a JavaScript tag with the +content+ inside. Example: # javascript_tag "alert('All is good')" # # Returns: # # # +html_options+ may be a hash of attributes for the \ # # Instead of passing the content as an argument, you can also use a block # in which case, you pass your +html_options+ as the first parameter. # <%= javascript_tag :defer => 'defer' do -%> # alert('All is good') # <% end -%> def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block) content = if block_given? html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash) capture(&block) else content_or_options_with_block end content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS)) end def javascript_cdata_section(content) #:nodoc: "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe end # Returns a button whose +onclick+ handler triggers the passed JavaScript. # # The helper receives a name, JavaScript code, and an optional hash of HTML options. The # name is used as button label and the JavaScript code goes into its +onclick+ attribute. # If +html_options+ has an :onclick, that one is put before +function+. # # button_to_function "Greeting", "alert('Hello world!')", :class => "ok" # # => # def button_to_function(name, function=nil, html_options={}) onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) end # Returns a link whose +onclick+ handler triggers the passed JavaScript. # # The helper receives a name, JavaScript code, and an optional hash of HTML options. The # name is used as the link text and the JavaScript code goes into the +onclick+ attribute. # If +html_options+ has an :onclick, that one is put before +function+. Once all # the JavaScript is set, the helper appends "; return false;". # # The +href+ attribute of the tag is set to "#" unless +html_options+ has one. # # link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link" # # => Greeting # def link_to_function(name, function, html_options={}) onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" href = html_options[:href] || '#' content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick)) end end end end actionpack-3.2.16/lib/action_view/helpers/form_tag_helper.rb0000644000175000017500000010027612247655372023467 0ustar ondrejondrejrequire 'cgi' require 'action_view/helpers/tag_helper' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/module/attribute_accessors' module ActionView # = Action View Form Tag Helpers module Helpers # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like # FormHelper does. Instead, you provide the names and values manually. # # NOTE: The HTML options disabled, readonly, and multiple can all be treated as booleans. So specifying # :disabled => true will give disabled="disabled". module FormTagHelper extend ActiveSupport::Concern include UrlHelper include TextHelper mattr_accessor :embed_authenticity_token_in_remote_forms self.embed_authenticity_token_in_remote_forms = true # Starts a form tag that points the action to an url configured with url_for_options just like # ActionController::Base#url_for. The method for the form defaults to POST. # # ==== Options # * :multipart - If set to true, the enctype is set to "multipart/form-data". # * :method - The method to use when submitting the form, usually either "get" or "post". # If "put", "delete", or another verb is used, a hidden input with name _method # is added to simulate the verb over post. # * :authenticity_token - Authenticity token to use in the form. Use only if you need to # pass custom authenticity token string, or to not add authenticity_token field at all # (by passing false). Remote forms may omit the embedded authenticity token # by setting config.action_view.embed_authenticity_token_in_remote_forms = false. # This is helpful when you're fragment-caching the form. Remote forms get the # authenticity from the meta tag, so embedding is unnecessary unless you # support browsers without JavaScript. # * A list of parameters to feed to the URL the form will be posted to. # * :remote - If set to true, will allow the Unobtrusive JavaScript drivers to control the # submit behavior. By default this behavior is an ajax submit. # # ==== Examples # form_tag('/posts') # # =>
# # form_tag('/posts/1', :method => :put) # # => ... ... # # form_tag('/upload', :multipart => true) # # => # # <%= form_tag('/posts') do -%> #
<%= submit_tag 'Save' %>
# <% end -%> # # =>
# # <%= form_tag('/posts', :remote => true) %> # # =>
# # form_tag('http://far.away.com/form', :authenticity_token => false) # # form without authenticity token # # form_tag('http://far.away.com/form', :authenticity_token => "cf50faa3fe97702ca1ae") # # form with custom authenticity token # def form_tag(url_for_options = {}, options = {}, &block) html_options = html_options_for_form(url_for_options, options) if block_given? form_tag_in_block(html_options, &block) else form_tag_html(html_options) end end # Creates a dropdown selection box, or if the :multiple option is set to true, a multiple # choice selection box. # # Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or # associated records. option_tags is a string containing the option tags for the select box. # # ==== Options # * :multiple - If set to true the selection will allow multiple choices. # * :disabled - If set to true, the user will not be able to use this input. # * :include_blank - If set to true, an empty option will be create # * :prompt - Create a prompt option with blank value and the text asking user to select something # * Any other key creates standard HTML attributes for the tag. # # ==== Examples # select_tag "people", options_from_collection_for_select(@people, "id", "name") # # # # select_tag "people", "".html_safe # # => # # select_tag "count", "".html_safe # # => # # select_tag "colors", "".html_safe, :multiple => true # # => # # select_tag "locations", "".html_safe # # => # # select_tag "access", "".html_safe, :multiple => true, :class => 'form_input' # # => # # select_tag "people", options_from_collection_for_select(@people, "id", "name"), :include_blank => true # # => # # select_tag "people", options_from_collection_for_select(@people, "id", "name"), :prompt => "Select something" # # => # # select_tag "destination", "".html_safe, :disabled => true # # => def select_tag(name, option_tags = nil, options = {}) option_tags ||= "" html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name if options.delete(:include_blank) option_tags = content_tag(:option, '', :value => '').safe_concat(option_tags) end if prompt = options.delete(:prompt) option_tags = content_tag(:option, prompt, :value => '').safe_concat(option_tags) end content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end # Creates a standard text field; use these text fields to input smaller chunks of text like a username # or a search query. # # ==== Options # * :disabled - If set to true, the user will not be able to use this input. # * :size - The number of visible characters that will fit in the input. # * :maxlength - The maximum number of characters that the browser will allow the user to enter. # * :placeholder - The text contained in the field by default which is removed when the field receives focus. # * Any other key creates standard HTML attributes for the tag. # # ==== Examples # text_field_tag 'name' # # => # # text_field_tag 'query', 'Enter your search query here' # # => # # text_field_tag 'search', nil, :placeholder => 'Enter search term...' # # => # # text_field_tag 'request', nil, :class => 'special_input' # # => # # text_field_tag 'address', '', :size => 75 # # => # # text_field_tag 'zip', nil, :maxlength => 5 # # => # # text_field_tag 'payment_amount', '$0.00', :disabled => true # # => # # text_field_tag 'ip', '0.0.0.0', :maxlength => 15, :size => 20, :class => "ip-input" # # => def text_field_tag(name, value = nil, options = {}) tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys) end # Creates a label element. Accepts a block. # # ==== Options # * Creates standard HTML attributes for the tag. # # ==== Examples # label_tag 'name' # # => # # label_tag 'name', 'Your name' # # => # # label_tag 'name', nil, :class => 'small_label' # # => def label_tag(name = nil, content_or_options = nil, options = nil, &block) if block_given? && content_or_options.is_a?(Hash) options = content_or_options = content_or_options.stringify_keys else options ||= {} options = options.stringify_keys end options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for") content_tag :label, content_or_options || name.to_s.humanize, options, &block end # Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or # data that should be hidden from the user. # # ==== Options # * Creates standard HTML attributes for the tag. # # ==== Examples # hidden_field_tag 'tags_list' # # => # # hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@' # # => # # hidden_field_tag 'collected_input', '', :onchange => "alert('Input collected!')" # # => def hidden_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "hidden")) end # Creates a file upload field. If you are using file uploads then you will also need # to set the multipart option for the form tag: # # <%= form_tag '/upload', :multipart => true do %> # <%= file_field_tag "file" %> # <%= submit_tag %> # <% end %> # # The specified URL will then be passed a File object containing the selected file, or if the field # was left blank, a StringIO object. # # ==== Options # * Creates standard HTML attributes for the tag. # * :disabled - If set to true, the user will not be able to use this input. # # ==== Examples # file_field_tag 'attachment' # # => # # file_field_tag 'avatar', :class => 'profile_input' # # => # # file_field_tag 'picture', :disabled => true # # => # # file_field_tag 'resume', :value => '~/resume.doc' # # => # # file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg' # # => # # file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html' # # => def file_field_tag(name, options = {}) text_field_tag(name, nil, options.update("type" => "file")) end # Creates a password field, a masked text field that will hide the users input behind a mask character. # # ==== Options # * :disabled - If set to true, the user will not be able to use this input. # * :size - The number of visible characters that will fit in the input. # * :maxlength - The maximum number of characters that the browser will allow the user to enter. # * Any other key creates standard HTML attributes for the tag. # # ==== Examples # password_field_tag 'pass' # # => # # password_field_tag 'secret', 'Your secret here' # # => # # password_field_tag 'masked', nil, :class => 'masked_input_field' # # => # # password_field_tag 'token', '', :size => 15 # # => # # password_field_tag 'key', nil, :maxlength => 16 # # => # # password_field_tag 'confirm_pass', nil, :disabled => true # # => # # password_field_tag 'pin', '1234', :maxlength => 4, :size => 6, :class => "pin_input" # # => def password_field_tag(name = "password", value = nil, options = {}) text_field_tag(name, value, options.update("type" => "password")) end # Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions. # # ==== Options # * :size - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10"). # * :rows - Specify the number of rows in the textarea # * :cols - Specify the number of columns in the textarea # * :disabled - If set to true, the user will not be able to use this input. # * :escape - By default, the contents of the text input are HTML escaped. # If you need unescaped contents, set this to false. # * Any other key creates standard HTML attributes for the tag. # # ==== Examples # text_area_tag 'post' # # => # # text_area_tag 'bio', @user.bio # # => # # text_area_tag 'body', nil, :rows => 10, :cols => 25 # # => # # text_area_tag 'body', nil, :size => "25x10" # # => # # text_area_tag 'description', "Description goes here.", :disabled => true # # => # # text_area_tag 'comment', nil, :class => 'comment_input' # # => def text_area_tag(name, content = nil, options = {}) options = options.stringify_keys if size = options.delete("size") options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) end escape = options.key?("escape") ? options.delete("escape") : true content = ERB::Util.html_escape(content) if escape content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options) end # Creates a check box form input tag. # # ==== Options # * :disabled - If set to true, the user will not be able to use this input. # * Any other key creates standard HTML options for the tag. # # ==== Examples # check_box_tag 'accept' # # => # # check_box_tag 'rock', 'rock music' # # => # # check_box_tag 'receive_email', 'yes', true # # => # # check_box_tag 'tos', 'yes', false, :class => 'accept_tos' # # => # # check_box_tag 'eula', 'accepted', false, :disabled => true # # => def check_box_tag(name, value = "1", checked = false, options = {}) html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys) html_options["checked"] = "checked" if checked tag :input, html_options end # Creates a radio button; use groups of radio buttons named the same to allow users to # select from a group of options. # # ==== Options # * :disabled - If set to true, the user will not be able to use this input. # * Any other key creates standard HTML options for the tag. # # ==== Examples # radio_button_tag 'gender', 'male' # # => # # radio_button_tag 'receive_updates', 'no', true # # => # # radio_button_tag 'time_slot', "3:00 p.m.", false, :disabled => true # # => # # radio_button_tag 'color', "green", true, :class => "color_input" # # => def radio_button_tag(name, value, checked = false, options = {}) html_options = { "type" => "radio", "name" => name, "id" => "#{sanitize_to_id(name)}_#{sanitize_to_id(value)}", "value" => value }.update(options.stringify_keys) html_options["checked"] = "checked" if checked tag :input, html_options end # Creates a submit button with the text value as the caption. # # ==== Options # * :confirm => 'question?' - If present the unobtrusive JavaScript # drivers will provide a prompt with the question specified. If the user accepts, # the form is processed normally, otherwise no action is taken. # * :disabled - If true, the user will not be able to use this input. # * :disable_with - Value of this parameter will be used as the value for a # disabled version of the submit button when the form is submitted. This feature is # provided by the unobtrusive JavaScript driver. # * Any other key creates standard HTML options for the tag. # # ==== Examples # submit_tag # # => # # submit_tag "Edit this article" # # => # # submit_tag "Save edits", :disabled => true # # => # # submit_tag "Complete sale", :disable_with => "Please wait..." # # => # # submit_tag nil, :class => "form_submit" # # => # # submit_tag "Edit", :disable_with => "Editing...", :class => "edit_button" # # => # # submit_tag "Save", :confirm => "Are you sure?" # # => # def submit_tag(value = "Save changes", options = {}) options = options.stringify_keys if disable_with = options.delete("disable_with") options["data-disable-with"] = disable_with end if confirm = options.delete("confirm") options["data-confirm"] = confirm end tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options) end # Creates a button element that defines a submit button, # resetbutton or a generic button which can be used in # JavaScript, for example. You can use the button tag as a regular # submit tag but it isn't supported in legacy browsers. However, # the button tag allows richer labels such as images and emphasis, # so this helper will also accept a block. # # ==== Options # * :confirm => 'question?' - If present, the # unobtrusive JavaScript drivers will provide a prompt with # the question specified. If the user accepts, the form is # processed normally, otherwise no action is taken. # * :disabled - If true, the user will not be able to # use this input. # * :disable_with - Value of this parameter will be # used as the value for a disabled version of the submit # button when the form is submitted. This feature is provided # by the unobtrusive JavaScript driver. # * Any other key creates standard HTML options for the tag. # # ==== Examples # button_tag # # => # # button_tag(:type => 'button') do # content_tag(:strong, 'Ask me!') # end # # => # # button_tag "Checkout", :disable_with => "Please wait..." # # => # def button_tag(content_or_options = nil, options = nil, &block) options = content_or_options if block_given? && content_or_options.is_a?(Hash) options ||= {} options = options.stringify_keys if disable_with = options.delete("disable_with") options["data-disable-with"] = disable_with end if confirm = options.delete("confirm") options["data-confirm"] = confirm end options.reverse_merge! 'name' => 'button', 'type' => 'submit' content_tag :button, content_or_options || 'Button', options, &block end # Displays an image which when clicked will submit the form. # # source is passed to AssetTagHelper#path_to_image # # ==== Options # * :confirm => 'question?' - This will add a JavaScript confirm # prompt with the question specified. If the user accepts, the form is # processed normally, otherwise no action is taken. # * :disabled - If set to true, the user will not be able to use this input. # * Any other key creates standard HTML options for the tag. # # ==== Examples # image_submit_tag("login.png") # # => # # image_submit_tag("purchase.png", :disabled => true) # # => # # image_submit_tag("search.png", :class => 'search_button') # # => # # image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button") # # => def image_submit_tag(source, options = {}) options = options.stringify_keys if confirm = options.delete("confirm") options["data-confirm"] = confirm end tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options) end # Creates a field set for grouping HTML form elements. # # legend will become the fieldset's title (optional as per W3C). # options accept the same values as tag. # # ==== Examples # <%= field_set_tag do %> #

<%= text_field_tag 'name' %>

# <% end %> # # =>

# # <%= field_set_tag 'Your details' do %> #

<%= text_field_tag 'name' %>

# <% end %> # # =>
Your details

# # <%= field_set_tag nil, :class => 'format' do %> #

<%= text_field_tag 'name' %>

# <% end %> # # =>

def field_set_tag(legend = nil, options = nil, &block) content = capture(&block) output = tag(:fieldset, options, true) output.safe_concat(content_tag(:legend, legend)) unless legend.blank? output.concat(content) output.safe_concat("") end # Creates a text field of type "search". # # ==== Options # * Accepts the same options as text_field_tag. def search_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "search")) end # Creates a text field of type "tel". # # ==== Options # * Accepts the same options as text_field_tag. def telephone_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "tel")) end alias phone_field_tag telephone_field_tag # Creates a text field of type "url". # # ==== Options # * Accepts the same options as text_field_tag. def url_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "url")) end # Creates a text field of type "email". # # ==== Options # * Accepts the same options as text_field_tag. def email_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "email")) end # Creates a number field. # # ==== Options # * :min - The minimum acceptable value. # * :max - The maximum acceptable value. # * :in - A range specifying the :min and # :max values. # * :step - The acceptable value granularity. # * Otherwise accepts the same options as text_field_tag. # # ==== Examples # number_field_tag 'quantity', nil, :in => 1...10 # # => def number_field_tag(name, value = nil, options = {}) options = options.stringify_keys options["type"] ||= "number" if range = options.delete("in") || options.delete("within") options.update("min" => range.min, "max" => range.max) end text_field_tag(name, value, options) end # Creates a range form element. # # ==== Options # * Accepts the same options as number_field_tag. def range_field_tag(name, value = nil, options = {}) number_field_tag(name, value, options.stringify_keys.update("type" => "range")) end # Creates the hidden UTF8 enforcer tag. Override this method in a helper # to customize the tag. def utf8_enforcer_tag tag(:input, :type => "hidden", :name => "utf8", :value => "✓".html_safe) end private def html_options_for_form(url_for_options, options) options.stringify_keys.tap do |html_options| html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart") # The following URL is unescaped, this is just a hash of options, and it is the # responsibility of the caller to escape all the values. html_options["action"] = url_for(url_for_options) html_options["accept-charset"] = "UTF-8" html_options["data-remote"] = true if html_options.delete("remote") if html_options["data-remote"] && !embed_authenticity_token_in_remote_forms && html_options["authenticity_token"].blank? # The authenticity token is taken from the meta tag in this case html_options["authenticity_token"] = false elsif html_options["authenticity_token"] == true # Include the default authenticity_token, which is only generated when its set to nil, # but we needed the true value to override the default of no authenticity_token on data-remote. html_options["authenticity_token"] = nil end end end def extra_tags_for_form(html_options) authenticity_token = html_options.delete("authenticity_token") method = html_options.delete("method").to_s method_tag = case method when /^get$/i # must be case-insensitive, but can't use downcase as might be nil html_options["method"] = "get" '' when /^post$/i, "", nil html_options["method"] = "post" token_tag(authenticity_token) else html_options["method"] = "post" tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token) end tags = utf8_enforcer_tag << method_tag content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline') end def form_tag_html(html_options) extra_tags = extra_tags_for_form(html_options) (tag(:form, html_options, true) + extra_tags).html_safe end def form_tag_in_block(html_options, &block) content = capture(&block) output = ActiveSupport::SafeBuffer.new output.safe_concat(form_tag_html(html_options)) output << content output.safe_concat("
") end def token_tag(token) if token == false || !protect_against_forgery? '' else token ||= form_authenticity_token tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token) end end # see http://www.w3.org/TR/html4/types.html#type-name def sanitize_to_id(name) name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_") end end end end actionpack-3.2.16/lib/action_view/helpers/tag_helper.rb0000644000175000017500000001520612247655372022442 0ustar ondrejondrejrequire 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' require 'set' module ActionView # = Action View Tag Helpers module Helpers #:nodoc: # Provides methods to generate HTML tags programmatically when you can't use # a Builder. By default, they output XHTML compliant tags. module TagHelper extend ActiveSupport::Concern include CaptureHelper BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer autoplay controls loop selected hidden scoped async defer reversed ismap seemless muted required autofocus novalidate formnovalidate open pubdate).to_set BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym }) PRE_CONTENT_STRINGS = { :textarea => "\n" } # Returns an empty HTML tag of type +name+ which by default is XHTML # compliant. Set +open+ to true to create an open tag compatible # with HTML 4.0 and below. Add HTML attributes by passing an attributes # hash to +options+. Set +escape+ to false to disable attribute value # escaping. # # ==== Options # You can use symbols or strings for the attribute names. # # Use +true+ with boolean attributes that can render with no value, like # +disabled+ and +readonly+. # # HTML5 data-* attributes can be set with a single +data+ key # pointing to a hash of sub-attributes. # # To play nicely with JavaScript conventions sub-attributes are dasherized. # For example, a key +user_id+ would render as data-user-id and # thus accessed as dataset.userId. # # Values are encoded to JSON, with the exception of strings and symbols. # This may come in handy when using jQuery's HTML5-aware .data() # from 1.4.3. # # ==== Examples # tag("br") # # =>
# # tag("br", nil, true) # # =>
# # tag("input", :type => 'text', :disabled => true) # # => # # tag("img", :src => "open & shut.png") # # => # # tag("img", {:src => "open & shut.png"}, false, false) # # => # # tag("div", :data => {:name => 'Stephen', :city_state => %w(Chicago IL)}) # # =>
def tag(name, options = nil, open = false, escape = true) "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe end # Returns an HTML block tag of type +name+ surrounding the +content+. Add # HTML attributes by passing an attributes hash to +options+. # Instead of passing the content as an argument, you can also use a block # in which case, you pass your +options+ as the second parameter. # Set escape to false to disable attribute value escaping. # # ==== Options # The +options+ hash is used with attributes with no value like (disabled and # readonly), which you can give a value of true in the +options+ hash. You can use # symbols or strings for the attribute names. # # ==== Examples # content_tag(:p, "Hello world!") # # =>

Hello world!

# content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong") # # =>

Hello world!

# content_tag("select", options, :multiple => true) # # => # # <%= content_tag :div, :class => "strong" do -%> # Hello world! # <% end -%> # # =>
Hello world!
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block) if block_given? options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash) content_tag_string(name, capture(&block), options, escape) else content_tag_string(name, content_or_options_with_block, options, escape) end end # Returns a CDATA section with the given +content+. CDATA sections # are used to escape blocks of text containing characters which would # otherwise be recognized as markup. CDATA sections begin with the string # and end with (and may not contain) the string ]]>. # # ==== Examples # cdata_section("") # # => ]]> # # cdata_section(File.read("hello_world.txt")) # # => def cdata_section(content) "".html_safe end # Returns an escaped version of +html+ without affecting existing escaped entities. # # ==== Examples # escape_once("1 < 2 & 3") # # => "1 < 2 & 3" # # escape_once("<< Accept & Checkout") # # => "<< Accept & Checkout" def escape_once(html) ActiveSupport::Multibyte.clean(html.to_s).gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } end private def content_tag_string(name, content, options, escape = true) tag_options = tag_options(options, escape) if options "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{escape ? ERB::Util.h(content) : content}".html_safe end def tag_options(options, escape = true) unless options.blank? attrs = [] options.each_pair do |key, value| if key.to_s == 'data' && value.is_a?(Hash) value.each do |k, v| unless v.is_a?(String) || v.is_a?(Symbol) || v.is_a?(BigDecimal) v = v.to_json end v = ERB::Util.html_escape(v) if escape attrs << %(data-#{k.to_s.dasherize}="#{v}") end elsif BOOLEAN_ATTRIBUTES.include?(key) attrs << %(#{key}="#{key}") if value elsif !value.nil? final_value = value.is_a?(Array) ? value.join(" ") : value final_value = ERB::Util.html_escape(final_value) if escape attrs << %(#{key}="#{final_value}") end end " #{attrs.sort * ' '}".html_safe unless attrs.empty? end end end end end actionpack-3.2.16/lib/action_view/helpers/form_options_helper.rb0000644000175000017500000010225612247655372024407 0ustar ondrejondrejrequire 'cgi' require 'erb' require 'action_view/helpers/form_helper' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' module ActionView # = Action View Form Option Helpers module Helpers # Provides a number of methods for turning different kinds of containers into a set of option tags. # == Options # The collection_select, select and time_zone_select methods take an options parameter, a hash: # # * :include_blank - 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. # # For example, # # select("post", "category", Post::CATEGORIES, {:include_blank => true}) # # could become: # # # # Another common case is a select tag for an belongs_to-associated object. # # Example with @post.person_id => 2: # # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:include_blank => 'None'}) # # could become: # # # # * :prompt - 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. # # Example: # # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:prompt => 'Select Person'}) # # could become: # # # # Like the other form helpers, +select+ can accept an :index option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this # option to be in the +html_options+ parameter. # # Example: # # select("album[]", "genre", %w[rap rock country], {}, { :index => nil }) # # becomes: # # # # * :disabled - can be a single value or an array of values that will be disabled options in the final output. # # Example: # # select("post", "category", Post::CATEGORIES, {:disabled => 'restricted'}) # # could become: # # # # When used with the collection_select helper, :disabled can also be a Proc that identifies those options that should be disabled. # # Example: # # collection_select(:post, :category_id, Category.all, :id, :name, {:disabled => lambda{|category| category.archived? }}) # # If the categories "2008 stuff" and "Christmas" return true when the method archived? is called, this would return: # # module FormOptionsHelper # ERB::Util can mask some helpers like textilize. Make sure to include them. include TextHelper # Create a select tag and a series of contained option tags for the provided object and method. # The option currently held by the object will be selected, provided that the object is available. # # There are two possible formats for the choices parameter, corresponding to other helpers' output: # * A flat collection: see options_for_select # * A nested collection: see grouped_options_for_select # # Example with @post.person_id => 1: # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { :include_blank => true }) # # could become: # # # # This can be used to provide a default set of options in the standard way: before rendering the create form, a # new model instance is assigned the default options and bound to @model_name. Usually this model is not saved # to the database. Instead, a second model object is created when the create request is received. # This allows the user to submit a form page more than once with the expected results of creating multiple records. # In addition, this allows a single partial to be used to generate form inputs for both edit and create forms. # # By default, post.person_id is the selected option. Specify :selected => value to use a different selection # or :selected => nil to leave all options unselected. Similarly, you can specify values to be disabled in the option # tags by specifying the :disabled option. This can either be a single value or an array of values to be disabled. # # ==== Gotcha # # The HTML specification says when +multiple+ parameter passed to select and all options got deselected # web browsers do not send any value to server. Unfortunately this introduces a gotcha: # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So, # any mass-assignment idiom like # # @user.update_attributes(params[:user]) # # wouldn't update roles. # # To prevent this the helper generates an auxiliary hidden field before # every multiple select. The hidden field has the same name as multiple select and blank value. # # This way, the client either sends only the hidden field (representing # the deselected multiple select box), or both fields. Since the HTML specification # says key/value pairs have to be sent in the same order they appear in the # form, and parameters extraction gets the last occurrence of any repeated # key in the query string, that works for ordinary forms. # def select(object, method, choices, options = {}, html_options = {}) InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options) end # Returns # # # # # def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {}) InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options) end # Returns # # # # # # # # # # def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}) InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) end # Return select and option tags for the given object and method, using # #time_zone_options_for_select to generate the list of option tags. # # In addition to the :include_blank option documented above, # this method also supports a :model option, which defaults # to ActiveSupport::TimeZone. This may be used by users to specify a # different time zone model object. (See +time_zone_options_for_select+ # for more information.) # # You can also supply an array of ActiveSupport::TimeZone objects # as +priority_zones+, so that they will be listed above the rest of the # (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience # for obtaining a list of the US time zones, or a Regexp to select the zones # of your choice) # # Finally, this method supports a :default option, which selects # a default ActiveSupport::TimeZone if the object's time zone is +nil+. # # Examples: # time_zone_select( "user", "time_zone", nil, :include_blank => true) # # time_zone_select( "user", "time_zone", nil, :default => "Pacific Time (US & Canada)" ) # # time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, :default => "Pacific Time (US & Canada)") # # time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ]) # # time_zone_select( "user", 'time_zone', /Australia/) # # time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, :model => ActiveSupport::TimeZone) def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {}) InstanceTag.new(object, method, self, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options) end # Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container # where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and # the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+ # may also be an array of values to be selected when using a multiple select. # # Examples (call, result): # options_for_select([["Dollar", "$"], ["Kroner", "DKK"]]) # \n # # options_for_select([ "VISA", "MasterCard" ], "MasterCard") # \n # # options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40") # \n # # options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"]) # \n\n # # You can optionally provide html attributes as the last element of the array. # # Examples: # options_for_select([ "Denmark", ["USA", {:class => 'bold'}], "Sweden" ], ["USA", "Sweden"]) # \n\n # # options_for_select([["Dollar", "$", {:class => "bold"}], ["Kroner", "DKK", {:onclick => "alert('HI');"}]]) # \n # # If you wish to specify disabled option tags, set +selected+ to be a hash, with :disabled being either a value # or array of values to be disabled. In this case, you can use :selected to specify selected option tags. # # Examples: # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :disabled => "Super Platinum") # \n\n\n # # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :disabled => ["Advanced", "Super Platinum"]) # \n\n\n # # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :selected => "Free", :disabled => "Super Platinum") # \n\n\n # # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. def options_for_select(container, selected = nil) return container if String === container selected, disabled = extract_selected_and_disabled(selected).map do | r | Array.wrap(r).map { |item| item.to_s } end container.map do |element| html_attributes = option_html_attributes(element) text, value = option_text_and_value(element).map { |item| item.to_s } selected_attribute = ' selected="selected"' if option_value_selected?(value, selected) disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled) %() end.join("\n").html_safe end # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning # the result of a call to the +value_method+ as the option value and the +text_method+ as the option text. # Example: # options_from_collection_for_select(@people, 'id', 'name') # This will output the same HTML as if you did this: # # # This is more often than not used inside a #select_tag like this example: # select_tag 'person', options_from_collection_for_select(@people, 'id', 'name') # # If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+ # will be selected option tag(s). # # If +selected+ is specified as a Proc, those members of the collection that return true for the anonymous # function are the selected values. # # +selected+ can also be a hash, specifying both :selected and/or :disabled values as required. # # Be sure to specify the same class as the +value_method+ when specifying selected or disabled options. # Failure to do this will produce undesired results. Example: # options_from_collection_for_select(@people, 'id', 'name', '1') # Will not select a person with the id of 1 because 1 (an Integer) is not the same as '1' (a string) # options_from_collection_for_select(@people, 'id', 'name', 1) # should produce the desired results. def options_from_collection_for_select(collection, value_method, text_method, selected = nil) options = collection.map do |element| [element.send(text_method), element.send(value_method)] end selected, disabled = extract_selected_and_disabled(selected) select_deselect = {} select_deselect[:selected] = extract_values_from_collection(collection, value_method, selected) select_deselect[:disabled] = extract_values_from_collection(collection, value_method, disabled) options_for_select(options, select_deselect) end # Returns a string of tags, like options_from_collection_for_select, but # groups them by tags based on the object relationships of the arguments. # # Parameters: # * +collection+ - An array of objects representing the tags. # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an # array of child objects representing the tags. # * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a # string to be used as the +label+ attribute for its tag. # * +option_key_method+ - The name of a method which, when called on a child object of a member of # +collection+, returns a value to be used as the +value+ attribute for its tag. # * +option_value_method+ - The name of a method which, when called on a child object of a member of # +collection+, returns a value to be used as the contents of its tag. # * +selected_key+ - A value equal to the +value+ attribute for one of the tags, # which will have the +selected+ attribute set. Corresponds to the return value of one of the calls # to +option_key_method+. If +nil+, no selection is made. Can also be a hash if disabled values are # to be specified. # # Example object structure for use with this method: # class Continent < ActiveRecord::Base # has_many :countries # # attribs: id, name # end # class Country < ActiveRecord::Base # belongs_to :continent # # attribs: id, name, continent_id # end # # Sample usage: # option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3) # # Possible output: # # # # ... # # # # # # ... # # # Note: Only the and tags are returned, so you still have to # wrap the output in an appropriate tag. def grouped_options_for_select(grouped_options, selected_key = nil, prompt = nil) body = '' body << content_tag(:option, prompt, { :value => "" }, true) if prompt grouped_options = grouped_options.sort if grouped_options.is_a?(Hash) grouped_options.each do |group| body << content_tag(:optgroup, options_for_select(group[1], selected_key), :label => group[0]) end body.html_safe end # Returns a string of option tags for pretty much any time zone in the # world. Supply a ActiveSupport::TimeZone name as +selected+ to have it # marked as the selected option tag. You can also supply an array of # ActiveSupport::TimeZone objects as +priority_zones+, so that they will # be listed above the rest of the (long) list. (You can use # ActiveSupport::TimeZone.us_zones as a convenience for obtaining a list # of the US time zones, or a Regexp to select the zones of your choice) # # The +selected+ parameter must be either +nil+, or a string that names # a ActiveSupport::TimeZone. # # By default, +model+ is the ActiveSupport::TimeZone constant (which can # be obtained in Active Record as a value object). The only requirement # is that the +model+ parameter be an object that responds to +all+, and # returns an array of objects that represent time zones. # # NOTE: Only the option tags are returned, you have to wrap this call in # a regular HTML select tag. def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone) zone_options = "" zones = model.all convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } } if priority_zones if priority_zones.is_a?(Regexp) priority_zones = model.all.find_all {|z| z =~ priority_zones} end zone_options += options_for_select(convert_zones[priority_zones], selected) zone_options += "\n" zones = zones.reject { |z| priority_zones.include?( z ) } end zone_options += options_for_select(convert_zones[zones], selected) zone_options.html_safe end private def option_html_attributes(element) return "" unless Array === element html_attributes = [] element.select { |e| Hash === e }.reduce({}, :merge).each do |k, v| html_attributes << " #{k}=\"#{ERB::Util.html_escape(v.to_s)}\"" end html_attributes.join end def option_text_and_value(option) # Options are [text, value] pairs or strings used for both. case when Array === option option = option.reject { |e| Hash === e } [option.first, option.last] when !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last) [option.first, option.last] else [option, option] end end def option_value_selected?(value, selected) if selected.respond_to?(:include?) && !selected.is_a?(String) selected.include? value else value == selected end end def extract_selected_and_disabled(selected) if selected.is_a?(Proc) [ selected, nil ] else selected = Array.wrap(selected) options = selected.extract_options!.symbolize_keys [ options.include?(:selected) ? options[:selected] : selected, options[:disabled] ] end end def extract_values_from_collection(collection, value_method, selected) if selected.is_a?(Proc) collection.map do |element| element.send(value_method) if selected.call(element) end.compact else selected end end end class InstanceTag #:nodoc: include FormOptionsHelper def to_select_tag(choices, options, html_options) selected_value = options.has_key?(:selected) ? options[:selected] : value(object) choices = choices.to_a if choices.is_a?(Range) # Grouped choices look like this: # # [nil, []] # { nil => [] } # if !choices.empty? && choices.first.respond_to?(:last) && Array === choices.first.last option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]) else option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]) end select_content_tag(option_tags, options, html_options) end def to_collection_select_tag(collection, value_method, text_method, options, html_options) selected_value = options.has_key?(:selected) ? options[:selected] : value(object) select_content_tag( options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options ) end def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) select_content_tag( option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, html_options ) end def to_time_zone_select_tag(priority_zones, options, html_options) select_content_tag( time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options ) end private def add_options(option_tags, options, value = nil) if options[:include_blank] option_tags = content_tag_string('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags end if value.blank? && options[:prompt] prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select') option_tags = content_tag_string('option', prompt, :value => '') + "\n" + option_tags end option_tags end def select_content_tag(option_tags, options, html_options) html_options = html_options.stringify_keys add_default_name_and_id(html_options) select = content_tag("select", add_options(option_tags, options, value(object)), html_options) if html_options["multiple"] tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select else select end end end class FormBuilder def select(method, choices, options = {}, html_options = {}) @template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options)) end def collection_select(method, collection, value_method, text_method, options = {}, html_options = {}) @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)) end def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}) @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)) end def time_zone_select(method, priority_zones = nil, options = {}, html_options = {}) @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options)) end end end end actionpack-3.2.16/lib/action_view/helpers/translation_helper.rb0000644000175000017500000000717112247655372024227 0ustar ondrejondrejrequire 'action_view/helpers/tag_helper' require 'i18n/exceptions' module ActionView # = Action View Translation Helpers module Helpers module TranslationHelper # Delegates to I18n#translate but also performs three additional functions. # # First, it will ensure that any thrown +MissingTranslation+ messages will be turned # into inline spans that: # # * have a "translation-missing" class set, # * contain the missing key as a title attribute and # * a titleized version of the last key segment as a text. # # E.g. the value returned for a missing translation key :"blog.post.title" will be # Title. # This way your views will display rather reasonable strings but it will still # be easy to spot missing translations. # # Second, it'll scope the key by the current partial if the key starts # with a period. So if you call translate(".foo") from the # people/index.html.erb template, you'll actually be calling # I18n.translate("people.index.foo"). This makes it less repetitive # to translate many keys within the same partials and gives you a simple framework # for scoping them consistently. If you don't prepend the key with a period, # nothing is converted. # # Third, it'll mark the translation as safe HTML if the key has the suffix # "_html" or the last element of the key is the word "html". For example, # calling translate("footer_html") or translate("footer.html") will return # a safe HTML string that won't be escaped by other HTML helper methods. This # naming convention helps to identify translations that include HTML tags so that # you know what kind of output to expect when you call translate in a template. def translate(key, options = {}) # If the user has specified rescue_format then pass it all through, otherwise use # raise and do the work ourselves options[:raise] = true unless options.key?(:raise) || options.key?(:rescue_format) if html_safe_translation_key?(key) html_safe_options = options.dup options.except(*I18n::RESERVED_KEYS).each do |name, value| unless name == :count && value.is_a?(Numeric) html_safe_options[name] = ERB::Util.html_escape(value.to_s) end end translation = I18n.translate(scope_key_by_partial(key), html_safe_options) translation.respond_to?(:html_safe) ? translation.html_safe : translation else I18n.translate(scope_key_by_partial(key), options) end rescue I18n::MissingTranslationData => e keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope]) content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}") end alias :t :translate # Delegates to I18n.localize with no additional functionality. def localize(*args) I18n.localize(*args) end alias :l :localize private def scope_key_by_partial(key) if key.to_s.first == "." if @virtual_path @virtual_path.gsub(%r{/_?}, ".") + key.to_s else raise "Cannot use t(#{key.inspect}) shortcut because path is not available" end else key end end def html_safe_translation_key?(key) key.to_s =~ /(\b|_|\.)html$/ end end end end actionpack-3.2.16/lib/action_view/helpers/capture_helper.rb0000644000175000017500000001652112247655372023333 0ustar ondrejondrejrequire 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' module ActionView # = Action View Capture Helper module Helpers # CaptureHelper exposes methods to let you extract generated markup which # can be used in other parts of a template or layout file. # # It provides a method to capture blocks into variables through capture and # a way to capture a block of markup for use in a layout through content_for. module CaptureHelper # The capture method allows you to extract part of a template into a # variable. You can then use this variable anywhere in your templates or layout. # # ==== Examples # The capture method can be used in ERB templates... # # <% @greeting = capture do %> # Welcome to my shiny new web page! The date and time is # <%= Time.now %> # <% end %> # # ...and Builder (RXML) templates. # # @timestamp = capture do # "The current timestamp is #{Time.now}." # end # # You can then use that variable anywhere else. For example: # # # <%= @greeting %> # # <%= @greeting %> # # def capture(*args) value = nil buffer = with_output_buffer { value = yield(*args) } if string = buffer.presence || value and string.is_a?(String) ERB::Util.html_escape string end end # Calling content_for stores a block of markup in an identifier for later use. # You can make subsequent calls to the stored content in other templates, helper modules # or the layout by passing the identifier as an argument to content_for. # # Note: yield can still be used to retrieve the stored content, but calling # yield doesn't work in helper modules, while content_for does. # # ==== Examples # # <% content_for :not_authorized do %> # alert('You are not authorized to do that!') # <% end %> # # You can then use content_for :not_authorized anywhere in your templates. # # <%= content_for :not_authorized if current_user.nil? %> # # This is equivalent to: # # <%= yield :not_authorized if current_user.nil? %> # # content_for, however, can also be used in helper modules. # # module StorageHelper # def stored_content # content_for(:storage) || "Your storage is empty" # end # end # # This helper works just like normal helpers. # # <%= stored_content %> # # You can use the yield syntax alongside an existing call to yield in a layout. For example: # # <%# This is the layout %> # # # My Website # <%= yield :script %> # # # <%= yield %> # # # # And now, we'll create a view that has a content_for call that # creates the script identifier. # # <%# This is our view %> # Please login! # # <% content_for :script do %> # # <% end %> # # Then, in another view, you could to do something like this: # # <%= link_to 'Logout', :action => 'logout', :remote => true %> # # <% content_for :script do %> # <%= javascript_include_tag :defaults %> # <% end %> # # That will place +script+ tags for your default set of JavaScript files on the page; # this technique is useful if you'll only be using these scripts in a few views. # # Note that content_for concatenates the blocks it is given for a particular # identifier in order. For example: # # <% content_for :navigation do %> #
  • <%= link_to 'Home', :action => 'index' %>
  • # <% end %> # # <%# Add some other content, or use a different template: %> # # <% content_for :navigation do %> #
  • <%= link_to 'Login', :action => 'login' %>
  • # <% end %> # # Then, in another template or layout, this code would render both links in order: # #
      <%= content_for :navigation %>
    # # Lastly, simple content can be passed as a parameter: # # <% content_for :script, javascript_include_tag(:defaults) %> # # WARNING: content_for is ignored in caches. So you shouldn't use it # for elements that will be fragment cached. def content_for(name, content = nil, &block) if content || block_given? content = capture(&block) if block_given? @view_flow.append(name, content) if content nil else @view_flow.get(name) end end # The same as +content_for+ but when used with streaming flushes # straight back to the layout. In other words, if you want to # concatenate several times to the same buffer when rendering a given # template, you should use +content_for+, if not, use +provide+ to tell # the layout to stop looking for more contents. def provide(name, content = nil, &block) content = capture(&block) if block_given? result = @view_flow.append!(name, content) if content result unless content end # content_for? simply checks whether any content has been captured yet using content_for # Useful to render parts of your layout differently based on what is in your views. # # ==== Examples # # Perhaps you will use different css in you layout if no content_for :right_column # # <%# This is the layout %> # # # My Website # <%= yield :script %> # # # <%= yield %> # <%= yield :right_col %> # # def content_for?(name) @view_flow.get(name).present? end # Use an alternate output buffer for the duration of the block. # Defaults to a new empty string. def with_output_buffer(buf = nil) #:nodoc: unless buf buf = ActionView::OutputBuffer.new buf.force_encoding(output_buffer.encoding) if output_buffer.respond_to?(:encoding) && buf.respond_to?(:force_encoding) end self.output_buffer, old_buffer = buf, output_buffer yield output_buffer ensure self.output_buffer = old_buffer end # Add the output buffer to the response body and start a new one. def flush_output_buffer #:nodoc: if output_buffer && !output_buffer.empty? response.body_parts << output_buffer self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0] nil end end end end end actionpack-3.2.16/lib/action_view/helpers/form_helper.rb0000644000175000017500000017217112247655372022637 0ustar ondrejondrejrequire 'cgi' require 'action_view/helpers/date_helper' require 'action_view/helpers/tag_helper' require 'action_view/helpers/form_tag_helper' require 'action_view/helpers/active_model_helper' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/module/method_names' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/string/inflections' module ActionView # = Action View Form Helpers module Helpers # Form helpers are designed to make working with resources much easier # compared to using vanilla HTML. # # Forms for models are created with +form_for+. That method yields a form # builder that knows the model the form is about. The form builder is thus # able to generate default values for input fields that correspond to model # attributes, and also convenient names, IDs, endpoints, etc. # # Conventions in the generated field names allow controllers to receive form # data nicely structured in +params+ with no effort on your side. # # For example, to create a new person you typically set up a new instance of # +Person+ in the PeopleController#new action, @person, and # pass it to +form_for+: # # <%= form_for @person do |f| %> # <%= f.label :first_name %>: # <%= f.text_field :first_name %>
    # # <%= f.label :last_name %>: # <%= f.text_field :last_name %>
    # # <%= f.submit %> # <% end %> # # The HTML generated for this would be (modulus formatting): # #
    #
    # #
    # : #
    # # : #
    # # #
    # # As you see, the HTML reflects knowledge about the resource in several spots, # like the path the form should be submitted to, or the names of the input fields. # # In particular, thanks to the conventions followed in the generated field names, the # controller gets a nested hash params[:person] with the person attributes # set in the form. That hash is ready to be passed to Person.create: # # if @person = Person.create(params[:person]) # # success # else # # error handling # end # # Interestingly, the exact same view code in the previous example can be used to edit # a person. If @person is an existing record with name "John Smith" and ID 256, # the code above as is would yield instead: # #
    #
    # # #
    # : #
    # # : #
    # # #
    # # Note that the endpoint, default values, and submit button label are tailored for @person. # That works that way because the involved helpers know whether the resource is a new record or not, # and generate HTML accordingly. # # The controller would receive the form data again in params[:person], ready to be # passed to Person#update_attributes: # # if @person.update_attributes(params[:person]) # # success # else # # error handling # end # # That's how you typically work with resources. module FormHelper extend ActiveSupport::Concern include FormTagHelper include UrlHelper # Converts the given object to an ActiveModel compliant one. def convert_to_model(object) object.respond_to?(:to_model) ? object.to_model : object end # Creates a form and a scope around a specific model object that is used # as a base for questioning about values for the fields. # # Rails provides succinct resource-oriented form generation with +form_for+ # like this: # # <%= form_for @offer do |f| %> # <%= f.label :version, 'Version' %>: # <%= f.text_field :version %>
    # <%= f.label :author, 'Author' %>: # <%= f.text_field :author %>
    # <%= f.submit %> # <% end %> # # There, +form_for+ is able to generate the rest of RESTful form # parameters based on introspection on the record, but to understand what # it does we need to dig first into the alternative generic usage it is # based upon. # # === Generic form_for # # The generic way to call +form_for+ yields a form builder around a # model: # # <%= form_for :person do |f| %> # First name: <%= f.text_field :first_name %>
    # Last name : <%= f.text_field :last_name %>
    # Biography : <%= f.text_area :biography %>
    # Admin? : <%= f.check_box :admin %>
    # <%= f.submit %> # <% end %> # # There, the argument is a symbol or string with the name of the # object the form is about. # # The form builder acts as a regular form helper that somehow carries the # model. Thus, the idea is that # # <%= f.text_field :first_name %> # # gets expanded to # # <%= text_field :person, :first_name %> # # The rightmost argument to +form_for+ is an # optional hash of options: # # * :url - The URL the form is submitted to. It takes the same # fields you pass to +url_for+ or +link_to+. In particular you may pass # here a named route directly as well. Defaults to the current action. # * :namespace - A namespace for your form to ensure uniqueness of # id attributes on form elements. The namespace attribute will be prefixed # with underscore on the generated HTML id. # * :html - Optional HTML attributes for the form tag. # # Also note that +form_for+ doesn't create an exclusive scope. It's still # possible to use both the stand-alone FormHelper methods and methods # from FormTagHelper. For example: # # <%= form_for @person do |f| %> # First name: <%= f.text_field :first_name %> # Last name : <%= f.text_field :last_name %> # Biography : <%= text_area :person, :biography %> # Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %> # <%= f.submit %> # <% end %> # # This also works for the methods in FormOptionHelper and DateHelper that # are designed to work with an object as base, like # FormOptionHelper#collection_select and DateHelper#datetime_select. # # === Resource-oriented style # # As we said above, in addition to manually configuring the +form_for+ # call, you can rely on automated resource identification, which will use # the conventions and named routes of that approach. This is the # preferred way to use +form_for+ nowadays. # # For example, if @post is an existing record you want to edit # # <%= form_for @post do |f| %> # ... # <% end %> # # is equivalent to something like: # # <%= form_for @post, :as => :post, :url => post_path(@post), :method => :put, :html => { :class => "edit_post", :id => "edit_post_45" } do |f| %> # ... # <% end %> # # And for new records # # <%= form_for(Post.new) do |f| %> # ... # <% end %> # # is equivalent to something like: # # <%= form_for @post, :as => :post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %> # ... # <% end %> # # You can also overwrite the individual conventions, like this: # # <%= form_for(@post, :url => super_posts_path) do |f| %> # ... # <% end %> # # You can also set the answer format, like this: # # <%= form_for(@post, :format => :json) do |f| %> # ... # <% end %> # # If you have an object that needs to be represented as a different # parameter, like a Person that acts as a Client: # # <%= form_for(@person, :as => :client) do |f| %> # ... # <% end %> # # For namespaced routes, like +admin_post_url+: # # <%= form_for([:admin, @post]) do |f| %> # ... # <% end %> # # If your resource has associations defined, for example, you want to add comments # to the document given that the routes are set correctly: # # <%= form_for([@document, @comment]) do |f| %> # ... # <% end %> # # Where @document = Document.find(params[:id]) and # @comment = Comment.new. # # === Setting the method # # You can force the form to use the full array of HTTP verbs by setting # # :method => (:get|:post|:put|:delete) # # in the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the # form will be set to POST and a hidden input called _method will carry the intended verb for the server # to interpret. # # === Unobtrusive JavaScript # # Specifying: # # :remote => true # # in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its # behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular # POST arrangement, but ultimately the behavior is the choice of the JavaScript driver implementor. # Even though it's using JavaScript to serialize the form elements, the form submission will work just like # a regular submission as viewed by the receiving side (all elements available in params). # # Example: # # <%= form_for(@post, :remote => true) do |f| %> # ... # <% end %> # # The HTML generated for this would be: # #
    #
    # #
    # ... #
    # # === Removing hidden model id's # # The form_for method automatically includes the model id as a hidden field in the form. # This is used to maintain the correlation between the form data and its associated model. # Some ORM systems do not use IDs on nested models so in this case you want to be able # to disable the hidden id. # # In the following example the Post model has many Comments stored within it in a NoSQL database, # thus there is no primary key for comments. # # Example: # # <%= form_for(@post) do |f| %> # <% f.fields_for(:comments, :include_id => false) do |cf| %> # ... # <% end %> # <% end %> # # === Customized form builders # # You can also build forms using a customized FormBuilder class. Subclass # FormBuilder and override or define some more helpers, then use your # custom builder. For example, let's say you made a helper to # automatically add labels to form inputs. # # <%= form_for @person, :url => { :action => "create" }, :builder => LabellingFormBuilder do |f| %> # <%= f.text_field :first_name %> # <%= f.text_field :last_name %> # <%= f.text_area :biography %> # <%= f.check_box :admin %> # <%= f.submit %> # <% end %> # # In this case, if you use this: # # <%= render f %> # # The rendered template is people/_labelling_form and the local # variable referencing the form builder is called # labelling_form. # # The custom FormBuilder class is automatically merged with the options # of a nested fields_for call, unless it's explicitly set. # # In many cases you will want to wrap the above in another helper, so you # could do something like the following: # # def labelled_form_for(record_or_name_or_array, *args, &block) # options = args.extract_options! # form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &block) # end # # If you don't need to attach a form to a model instance, then check out # FormTagHelper#form_tag. # # === Form to external resources # # When you build forms to external resources sometimes you need to set an authenticity token or just render a form # without it, for example when you submit data to a payment gateway number and types of fields could be limited. # # To set an authenticity token you need to pass an :authenticity_token parameter # # <%= form_for @invoice, :url => external_url, :authenticity_token => 'external_token' do |f| # ... # <% end %> # # If you don't want to an authenticity token field be rendered at all just pass false: # # <%= form_for @invoice, :url => external_url, :authenticity_token => false do |f| # ... # <% end %> def form_for(record, options = {}, &block) raise ArgumentError, "Missing block" unless block_given? options[:html] ||= {} case record when String, Symbol object_name = record object = nil else object = record.is_a?(Array) ? record.last : record object_name = options[:as] || ActiveModel::Naming.param_key(object) apply_form_for_options!(record, options) end options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote) options[:html][:method] = options.delete(:method) if options.has_key?(:method) options[:html][:authenticity_token] = options.delete(:authenticity_token) builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &block) output = capture(builder, &block) default_options = builder.multipart? ? { :multipart => true } : {} form_tag(options.delete(:url) || {}, default_options.merge!(options.delete(:html))) { output } end def apply_form_for_options!(object_or_array, options) #:nodoc: object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array object = convert_to_model(object) as = options[:as] action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post] options[:html].reverse_merge!( :class => as ? "#{action}_#{as}" : dom_class(object, action), :id => as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence, :method => method ) options[:url] ||= polymorphic_path(object_or_array, :format => options.delete(:format)) end private :apply_form_for_options! # Creates a scope around a specific model object like form_for, but # doesn't create the form tags themselves. This makes fields_for suitable # for specifying additional model objects in the same form. # # === Generic Examples # # <%= form_for @person do |person_form| %> # First name: <%= person_form.text_field :first_name %> # Last name : <%= person_form.text_field :last_name %> # # <%= fields_for @person.permission do |permission_fields| %> # Admin? : <%= permission_fields.check_box :admin %> # <% end %> # # <%= f.submit %> # <% end %> # # ...or if you have an object that needs to be represented as a different # parameter, like a Client that acts as a Person: # # <%= fields_for :person, @client do |permission_fields| %> # Admin?: <%= permission_fields.check_box :admin %> # <% end %> # # ...or if you don't have an object, just a name of the parameter: # # <%= fields_for :person do |permission_fields| %> # Admin?: <%= permission_fields.check_box :admin %> # <% end %> # # Note: This also works for the methods in FormOptionHelper and # DateHelper that are designed to work with an object as base, like # FormOptionHelper#collection_select and DateHelper#datetime_select. # # === Nested Attributes Examples # # When the object belonging to the current scope has a nested attribute # writer for a certain attribute, fields_for will yield a new scope # for that attribute. This allows you to create forms that set or change # the attributes of a parent object and its associations in one go. # # Nested attribute writers are normal setter methods named after an # association. The most common way of defining these writers is either # with +accepts_nested_attributes_for+ in a model definition or by # defining a method with the proper name. For example: the attribute # writer for the association :address is called # address_attributes=. # # Whether a one-to-one or one-to-many style form builder will be yielded # depends on whether the normal reader method returns a _single_ object # or an _array_ of objects. # # ==== One-to-one # # Consider a Person class which returns a _single_ Address from the # address reader method and responds to the # address_attributes= writer method: # # class Person # def address # @address # end # # def address_attributes=(attributes) # # Process the attributes hash # end # end # # This model can now be used with a nested fields_for, like so: # # <%= form_for @person do |person_form| %> # ... # <%= person_form.fields_for :address do |address_fields| %> # Street : <%= address_fields.text_field :street %> # Zip code: <%= address_fields.text_field :zip_code %> # <% end %> # ... # <% end %> # # When address is already an association on a Person you can use # +accepts_nested_attributes_for+ to define the writer method for you: # # class Person < ActiveRecord::Base # has_one :address # accepts_nested_attributes_for :address # end # # If you want to destroy the associated model through the form, you have # to enable it first using the :allow_destroy option for # +accepts_nested_attributes_for+: # # class Person < ActiveRecord::Base # has_one :address # accepts_nested_attributes_for :address, :allow_destroy => true # end # # Now, when you use a form element with the _destroy parameter, # with a value that evaluates to +true+, you will destroy the associated # model (eg. 1, '1', true, or 'true'): # # <%= form_for @person do |person_form| %> # ... # <%= person_form.fields_for :address do |address_fields| %> # ... # Delete: <%= address_fields.check_box :_destroy %> # <% end %> # ... # <% end %> # # ==== One-to-many # # Consider a Person class which returns an _array_ of Project instances # from the projects reader method and responds to the # projects_attributes= writer method: # # class Person # def projects # [@project1, @project2] # end # # def projects_attributes=(attributes) # # Process the attributes hash # end # end # # Note that the projects_attributes= writer method is in fact # required for fields_for to correctly identify :projects as a # collection, and the correct indices to be set in the form markup. # # When projects is already an association on Person you can use # +accepts_nested_attributes_for+ to define the writer method for you: # # class Person < ActiveRecord::Base # has_many :projects # accepts_nested_attributes_for :projects # end # # This model can now be used with a nested fields_for. The block given to # the nested fields_for call will be repeated for each instance in the # collection: # # <%= form_for @person do |person_form| %> # ... # <%= person_form.fields_for :projects do |project_fields| %> # <% if project_fields.object.active? %> # Name: <%= project_fields.text_field :name %> # <% end %> # <% end %> # ... # <% end %> # # It's also possible to specify the instance to be used: # # <%= form_for @person do |person_form| %> # ... # <% @person.projects.each do |project| %> # <% if project.active? %> # <%= person_form.fields_for :projects, project do |project_fields| %> # Name: <%= project_fields.text_field :name %> # <% end %> # <% end %> # <% end %> # ... # <% end %> # # Or a collection to be used: # # <%= form_for @person do |person_form| %> # ... # <%= person_form.fields_for :projects, @active_projects do |project_fields| %> # Name: <%= project_fields.text_field :name %> # <% end %> # ... # <% end %> # # When projects is already an association on Person you can use # +accepts_nested_attributes_for+ to define the writer method for you: # # class Person < ActiveRecord::Base # has_many :projects # accepts_nested_attributes_for :projects # end # # If you want to destroy any of the associated models through the # form, you have to enable it first using the :allow_destroy # option for +accepts_nested_attributes_for+: # # class Person < ActiveRecord::Base # has_many :projects # accepts_nested_attributes_for :projects, :allow_destroy => true # end # # This will allow you to specify which models to destroy in the # attributes hash by adding a form element for the _destroy # parameter with a value that evaluates to +true+ # (eg. 1, '1', true, or 'true'): # # <%= form_for @person do |person_form| %> # ... # <%= person_form.fields_for :projects do |project_fields| %> # Delete: <%= project_fields.check_box :_destroy %> # <% end %> # ... # <% end %> def fields_for(record_name, record_object = nil, options = {}, &block) builder = instantiate_builder(record_name, record_object, options, &block) output = capture(builder, &block) output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id? output end # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation # is found in the current I18n locale (through helpers.label..) or you specify it explicitly. # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged # onto the HTML as an HTML element attribute as in the example shown, except for the :value option, which is designed to # target labels for radio_button tags (where the value is used in the ID of the input tag). # # ==== Examples # label(:post, :title) # # => # # You can localize your labels based on model and attribute names. # For example you can define the following in your locale (e.g. en.yml) # # helpers: # label: # post: # body: "Write your entire text here" # # Which then will result in # # label(:post, :body) # # => # # Localization can also be based purely on the translation of the attribute-name # (if you are using ActiveRecord): # # activerecord: # attributes: # post: # cost: "Total cost" # # label(:post, :cost) # # => # # label(:post, :title, "A short title") # # => # # label(:post, :title, "A short title", :class => "title_label") # # => # # label(:post, :privacy, "Public Post", :value => "public") # # => # # label(:post, :terms) do # 'Accept Terms.'.html_safe # end def label(object_name, method, content_or_options = nil, options = nil, &block) options ||= {} content_is_options = content_or_options.is_a?(Hash) if content_is_options || block_given? options.merge!(content_or_options) if content_is_options text = nil else text = content_or_options end InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block) end # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example # shown. # # ==== Examples # text_field(:post, :title, :size => 20) # # => # # text_field(:post, :title, :class => "create_input") # # => # # text_field(:session, :user, :onchange => "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }") # # => # # text_field(:snippet, :code, :size => 20, :class => 'code_input') # # => # def text_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("text", options) end # Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example # shown. # # ==== Examples # password_field(:login, :pass, :size => 20) # # => # # password_field(:account, :secret, :class => "form_input", :value => @account.secret) # # => # # password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }") # # => # # password_field(:account, :pin, :size => 20, :class => 'form_input') # # => # def password_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("password", { :value => nil }.merge!(options)) end # Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example # shown. # # ==== Examples # hidden_field(:signup, :pass_confirm) # # => # # hidden_field(:post, :tag_list) # # => # # hidden_field(:user, :token) # # => def hidden_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("hidden", options) end # Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example # shown. # # Using this method inside a +form_for+ block will set the enclosing form's encoding to multipart/form-data. # # ==== Examples # file_field(:user, :avatar) # # => # # file_field(:post, :attached, :accept => 'text/html') # # => # # file_field(:attachment, :file, :class => 'file_input') # # => # def file_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("file", options.update({:size => nil})) end # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+) # on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a # hash with +options+. # # ==== Examples # text_area(:post, :body, :cols => 20, :rows => 40) # # => # # text_area(:comment, :text, :size => "20x30") # # => # # text_area(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input') # # => # # text_area(:entry, :body, :size => "20x20", :disabled => 'disabled') # # => def text_area(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options) end # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object. # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked. # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1 # while the default +unchecked_value+ is set to 0 which is convenient for boolean values. # # ==== Gotcha # # The HTML specification says unchecked check boxes are not successful, and # thus web browsers do not send them. Unfortunately this introduces a gotcha: # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid # invoice the user unchecks its check box, no +paid+ parameter is sent. So, # any mass-assignment idiom like # # @invoice.update_attributes(params[:invoice]) # # wouldn't update the flag. # # To prevent this the helper generates an auxiliary hidden field before # the very check box. The hidden field has the same name and its # attributes mimic an unchecked check box. # # This way, the client either sends only the hidden field (representing # the check box is unchecked), or both fields. Since the HTML specification # says key/value pairs have to be sent in the same order they appear in the # form, and parameters extraction gets the last occurrence of any repeated # key in the query string, that works for ordinary forms. # # Unfortunately that workaround does not work when the check box goes # within an array-like parameter, as in # # <%= fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %> # <%= form.check_box :paid %> # ... # <% end %> # # because parameter name repetition is precisely what Rails seeks to distinguish # the elements of the array. For each item with a checked check box you # get an extra ghost item with only that attribute, assigned to "0". # # In that case it is preferable to either use +check_box_tag+ or to use # hashes instead of arrays. # # ==== Examples # # Let's say that @post.validated? is 1: # check_box("post", "validated") # # => # # # # # Let's say that @puppy.gooddog is "no": # check_box("puppy", "gooddog", {}, "yes", "no") # # => # # # # check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no") # # => # # # def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0") InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value) end # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the # radio button will be checked. # # To force the radio button to be checked pass :checked => true in the # +options+ hash. You may pass HTML options there as well. # # ==== Examples # # Let's say that @post.category returns "rails": # radio_button("post", "category", "rails") # radio_button("post", "category", "java") # # => # # # # radio_button("user", "receive_newsletter", "yes") # radio_button("user", "receive_newsletter", "no") # # => # # def radio_button(object_name, method, tag_value, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options) end # Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object_name+). Inputs of type "search" may be styled differently by # some browsers. # # ==== Examples # # search_field(:user, :name) # # => # search_field(:user, :name, :autosave => false) # # => # search_field(:user, :name, :results => 3) # # => # # Assume request.host returns "www.example.com" # search_field(:user, :name, :autosave => true) # # => # search_field(:user, :name, :onsearch => true) # # => # search_field(:user, :name, :autosave => false, :onsearch => true) # # => # search_field(:user, :name, :autosave => true, :onsearch => true) # # => # def search_field(object_name, method, options = {}) options = options.stringify_keys if options["autosave"] if options["autosave"] == true options["autosave"] = request.host.split(".").reverse.join(".") end options["results"] ||= 10 end if options["onsearch"] options["incremental"] = true unless options.has_key?("incremental") end InstanceTag.new(object_name, method, self, options.delete("object")).to_input_field_tag("search", options) end # Returns a text_field of type "tel". # # telephone_field("user", "phone") # # => # def telephone_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("tel", options) end alias phone_field telephone_field # Returns a text_field of type "url". # # url_field("user", "homepage") # # => # def url_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("url", options) end # Returns a text_field of type "email". # # email_field("user", "address") # # => # def email_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("email", options) end # Returns an input tag of type "number". # # ==== Options # * Accepts same options as number_field_tag def number_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("number", options) end # Returns an input tag of type "range". # # ==== Options # * Accepts same options as range_field_tag def range_field(object_name, method, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("range", options) end private def instantiate_builder(record_name, record_object, options, &block) case record_name when String, Symbol object = record_object object_name = record_name else object = record_name object_name = ActiveModel::Naming.param_key(object) end builder = options[:builder] || default_form_builder builder.new(object_name, object, self, options, block) end def default_form_builder builder = ActionView::Base.default_form_builder builder.respond_to?(:constantize) ? builder.constantize : builder end end class InstanceTag include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper attr_reader :object, :method_name, :object_name DEFAULT_FIELD_OPTIONS = { "size" => 30 } DEFAULT_RADIO_OPTIONS = { } DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 } def initialize(object_name, method_name, template_object, object = nil) @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup @template_object = template_object @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]") @object = retrieve_object(object) @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match end def to_label_tag(text = nil, options = {}, &block) options = options.stringify_keys tag_value = options.delete("value") name_and_id = options.dup if name_and_id["for"] name_and_id["id"] = name_and_id["for"] else name_and_id.delete("id") end add_default_name_and_id_for_value(tag_value, name_and_id) options.delete("index") options.delete("namespace") options["for"] ||= name_and_id["id"] if block_given? @template_object.label_tag(name_and_id["id"], options, &block) else content = if text.blank? object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1') method_and_value = tag_value.present? ? "#{method_name}.#{tag_value}" : method_name if object.respond_to?(:to_model) key = object.class.model_name.i18n_key i18n_default = ["#{key}.#{method_and_value}".to_sym, ""] end i18n_default ||= "" I18n.t("#{object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence else text.to_s end content ||= if object && object.class.respond_to?(:human_attribute_name) object.class.human_attribute_name(method_name) end content ||= method_name.humanize label_tag(name_and_id["id"], content, options) end end def to_input_field_tag(field_type, options = {}) options = options.stringify_keys options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size") options = DEFAULT_FIELD_OPTIONS.merge(options) if field_type == "hidden" options.delete("size") end options["type"] ||= field_type options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file" options["value"] &&= ERB::Util.html_escape(options["value"]) add_default_name_and_id(options) tag("input", options) end def to_number_field_tag(field_type, options = {}) options = options.stringify_keys options['size'] ||= nil if range = options.delete("in") || options.delete("within") options.update("min" => range.min, "max" => range.max) end to_input_field_tag(field_type, options) end def to_radio_button_tag(tag_value, options = {}) options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys) options["type"] = "radio" options["value"] = tag_value if options.has_key?("checked") cv = options.delete "checked" checked = cv == true || cv == "checked" else checked = self.class.radio_button_checked?(value(object), tag_value) end options["checked"] = "checked" if checked add_default_name_and_id_for_value(tag_value, options) tag("input", options) end def to_text_area_tag(options = {}) options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys) add_default_name_and_id(options) if size = options.delete("size") options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) end content_tag("textarea", options.delete('value') || value_before_type_cast(object), options) end def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0") options = options.stringify_keys options["type"] = "checkbox" options["value"] = checked_value if options.has_key?("checked") cv = options.delete "checked" checked = cv == true || cv == "checked" else checked = self.class.check_box_checked?(value(object), checked_value) end options["checked"] = "checked" if checked if options["multiple"] add_default_name_and_id_for_value(checked_value, options) options.delete("multiple") else add_default_name_and_id(options) end hidden = unchecked_value ? tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value, "disabled" => options["disabled"]) : "" checkbox = tag("input", options) (hidden + checkbox).html_safe end def to_boolean_select_tag(options = {}) options = options.stringify_keys add_default_name_and_id(options) value = value(object) tag_text = "" end def to_content_tag(tag_name, options = {}) content_tag(tag_name, value(object), options) end def retrieve_object(object) if object object elsif @template_object.instance_variable_defined?("@#{@object_name}") @template_object.instance_variable_get("@#{@object_name}") end rescue NameError # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil. nil end def retrieve_autoindex(pre_match) object = self.object || @template_object.instance_variable_get("@#{pre_match}") if object && object.respond_to?(:to_param) object.to_param else raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}" end end def value(object) self.class.value(object, @method_name) end def value_before_type_cast(object) self.class.value_before_type_cast(object, @method_name) end class << self def value(object, method_name) object.send method_name if object end def value_before_type_cast(object, method_name) unless object.nil? object.respond_to?(method_name + "_before_type_cast") ? object.send(method_name + "_before_type_cast") : object.send(method_name) end end def check_box_checked?(value, checked_value) case value when TrueClass, FalseClass value when NilClass false when Integer value != 0 when String value == checked_value when Array value.include?(checked_value) else value.to_i != 0 end end def radio_button_checked?(value, checked_value) value.to_s == checked_value.to_s end end private def add_default_name_and_id_for_value(tag_value, options) unless tag_value.nil? pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase specified_id = options["id"] add_default_name_and_id(options) options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present? else add_default_name_and_id(options) end end def add_default_name_and_id(options) if options.has_key?("index") options["name"] ||= tag_name_with_index(options["index"], options["multiple"]) options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) } options.delete("index") elsif defined?(@auto_index) options["name"] ||= tag_name_with_index(@auto_index, options["multiple"]) options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) } else options["name"] ||= tag_name(options["multiple"]) options["id"] = options.fetch("id"){ tag_id } end options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence end def tag_name(multiple = false) "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}" end def tag_name_with_index(index, multiple = false) "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}" end def tag_id "#{sanitized_object_name}_#{sanitized_method_name}" end def tag_id_with_index(index) "#{sanitized_object_name}_#{index}_#{sanitized_method_name}" end def sanitized_object_name @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "") end def sanitized_method_name @sanitized_method_name ||= @method_name.sub(/\?$/,"") end end class FormBuilder # The methods which wrap a form helper call. class_attribute :field_helpers self.field_helpers = FormHelper.instance_method_names - %w(form_for convert_to_model) attr_accessor :object_name, :object, :options attr_reader :multipart, :parent_builder alias :multipart? :multipart def multipart=(multipart) @multipart = multipart parent_builder.multipart = multipart if parent_builder end def self._to_partial_path @_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, '') end def to_partial_path self.class._to_partial_path end def to_model self end def initialize(object_name, object, template, options, proc) @nested_child_index = {} @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc @parent_builder = options[:parent_builder] @default_options = @options ? @options.slice(:index, :namespace) : {} if @object_name.to_s.match(/\[\]$/) if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param) @auto_index = object.to_param else raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}" end end @multipart = nil end (field_helpers - %w(label check_box radio_button fields_for hidden_field file_field)).each do |selector| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{selector}(method, options = {}) # def text_field(method, options = {}) @template.send( # @template.send( #{selector.inspect}, # "text_field", @object_name, # @object_name, method, # method, objectify_options(options)) # objectify_options(options)) end # end RUBY_EVAL end def fields_for(record_name, record_object = nil, fields_options = {}, &block) fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options? fields_options[:builder] ||= options[:builder] fields_options[:parent_builder] = self fields_options[:namespace] = fields_options[:parent_builder].options[:namespace] case record_name when String, Symbol if nested_attributes_association?(record_name) return fields_for_with_nested_attributes(record_name, record_object, fields_options, block) end else record_object = record_name.is_a?(Array) ? record_name.last : record_name record_name = ActiveModel::Naming.param_key(record_object) end index = if options.has_key?(:index) "[#{options[:index]}]" elsif defined?(@auto_index) self.object_name = @object_name.to_s.sub(/\[\]$/,"") "[#{@auto_index}]" end record_name = "#{object_name}#{index}[#{record_name}]" @template.fields_for(record_name, record_object, fields_options, &block) end def label(method, text = nil, options = {}, &block) @template.label(@object_name, method, text, objectify_options(options), &block) end def check_box(method, options = {}, checked_value = "1", unchecked_value = "0") @template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value) end def radio_button(method, tag_value, options = {}) @template.radio_button(@object_name, method, tag_value, objectify_options(options)) end def hidden_field(method, options = {}) @emitted_hidden_id = true if method == :id @template.hidden_field(@object_name, method, objectify_options(options)) end def file_field(method, options = {}) self.multipart = true @template.file_field(@object_name, method, objectify_options(options)) end # Add the submit button for the given form. When no value is given, it checks # if the object is a new resource or not to create the proper label: # # <%= form_for @post do |f| %> # <%= f.submit %> # <% end %> # # In the example above, if @post is a new record, it will use "Create Post" as # submit button label, otherwise, it uses "Update Post". # # Those labels can be customized using I18n, under the helpers.submit key and accept # the %{model} as translation interpolation: # # en: # helpers: # submit: # create: "Create a %{model}" # update: "Confirm changes to %{model}" # # It also searches for a key specific for the given object: # # en: # helpers: # submit: # post: # create: "Add %{model}" # def submit(value=nil, options={}) value, options = nil, value if value.is_a?(Hash) value ||= submit_default_value @template.submit_tag(value, options) end # Add the submit button for the given form. When no value is given, it checks # if the object is a new resource or not to create the proper label: # # <%= form_for @post do |f| %> # <%= f.button %> # <% end %> # # In the example above, if @post is a new record, it will use "Create Post" as # submit button label, otherwise, it uses "Update Post". # # Those labels can be customized using I18n, under the helpers.submit key and accept # the %{model} as translation interpolation: # # en: # helpers: # button: # create: "Create a %{model}" # update: "Confirm changes to %{model}" # # It also searches for a key specific for the given object: # # en: # helpers: # button: # post: # create: "Add %{model}" # def button(value=nil, options={}) value, options = nil, value if value.is_a?(Hash) value ||= submit_default_value @template.button_tag(value, options) end def emitted_hidden_id? @emitted_hidden_id ||= nil end private def objectify_options(options) @default_options.merge(options.merge(:object => @object)) end def submit_default_value object = convert_to_model(@object) key = object ? (object.persisted? ? :update : :create) : :submit model = if object.class.respond_to?(:model_name) object.class.model_name.human else @object_name.to_s.humanize end defaults = [] defaults << :"helpers.submit.#{object_name}.#{key}" defaults << :"helpers.submit.#{key}" defaults << "#{key.to_s.humanize} #{model}" I18n.t(defaults.shift, :model => model, :default => defaults) end def nested_attributes_association?(association_name) @object.respond_to?("#{association_name}_attributes=") end def fields_for_with_nested_attributes(association_name, association, options, block) name = "#{object_name}[#{association_name}_attributes]" association = convert_to_model(association) if association.respond_to?(:persisted?) association = [association] if @object.send(association_name).is_a?(Array) elsif !association.respond_to?(:to_ary) association = @object.send(association_name) end if association.respond_to?(:to_ary) explicit_child_index = options[:child_index] output = ActiveSupport::SafeBuffer.new association.each do |child| output << fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block) end output elsif association fields_for_nested_model(name, association, options, block) end end def fields_for_nested_model(name, object, options, block) object = convert_to_model(object) parent_include_id = self.options.fetch(:include_id, true) include_id = options.fetch(:include_id, parent_include_id) options[:hidden_field_id] = object.persisted? && include_id @template.fields_for(name, object, options, &block) end def nested_child_index(name) @nested_child_index[name] ||= -1 @nested_child_index[name] += 1 end def convert_to_model(object) object.respond_to?(:to_model) ? object.to_model : object end end end ActiveSupport.on_load(:action_view) do class ActionView::Base cattr_accessor :default_form_builder @@default_form_builder = ::ActionView::Helpers::FormBuilder end end end actionpack-3.2.16/lib/action_view/helpers/asset_tag_helper.rb0000644000175000017500000005645112247655372023650 0ustar ondrejondrejrequire 'action_view/helpers/asset_tag_helpers/javascript_tag_helpers' require 'action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers' require 'action_view/helpers/asset_tag_helpers/asset_paths' require 'action_view/helpers/tag_helper' module ActionView # = Action View Asset Tag Helpers module Helpers #:nodoc: # This module provides methods for generating HTML that links views to assets such # as images, javascripts, stylesheets, and feeds. These methods do not verify # the assets exist before linking to them: # # image_tag("rails.png") # # => Rails # stylesheet_link_tag("application") # # => # # === Using asset hosts # # By default, Rails links to these assets on the current host in the public # folder, but you can direct Rails to link to assets from a dedicated asset # server by setting ActionController::Base.asset_host in the application # configuration, typically in config/environments/production.rb. # For example, you'd define assets.example.com to be your asset # host this way: # # ActionController::Base.asset_host = "assets.example.com" # # Helpers take that into account: # # image_tag("rails.png") # # => Rails # stylesheet_link_tag("application") # # => # # Browsers typically open at most two simultaneous connections to a single # host, which means your assets often have to wait for other assets to finish # downloading. You can alleviate this by using a %d wildcard in the # +asset_host+. For example, "assets%d.example.com". If that wildcard is # present Rails distributes asset requests among the corresponding four hosts # "assets0.example.com", ..., "assets3.example.com". With this trick browsers # will open eight simultaneous connections rather than two. # # image_tag("rails.png") # # => Rails # stylesheet_link_tag("application") # # => # # To do this, you can either setup four actual hosts, or you can use wildcard # DNS to CNAME the wildcard to a single asset host. You can read more about # setting up your DNS CNAME records from your ISP. # # Note: This is purely a browser performance optimization and is not meant # for server load balancing. See http://www.die.net/musings/page_load_time/ # for background. # # Alternatively, you can exert more control over the asset host by setting # +asset_host+ to a proc like this: # # ActionController::Base.asset_host = Proc.new { |source| # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com" # } # image_tag("rails.png") # # => Rails # stylesheet_link_tag("application") # # => # # The example above generates "http://assets1.example.com" and # "http://assets2.example.com". This option is useful for example if # you need fewer/more than four hosts, custom host names, etc. # # As you see the proc takes a +source+ parameter. That's a string with the # absolute path of the asset with any extensions and timestamps in place, # for example "/images/rails.png?1230601161". # # ActionController::Base.asset_host = Proc.new { |source| # if source.starts_with?('/images') # "http://images.example.com" # else # "http://assets.example.com" # end # } # image_tag("rails.png") # # => Rails # stylesheet_link_tag("application") # # => # # Alternatively you may ask for a second parameter +request+. That one is # particularly useful for serving assets from an SSL-protected page. The # example proc below disables asset hosting for HTTPS connections, while # still sending assets for plain HTTP requests from asset hosts. If you don't # have SSL certificates for each of the asset hosts this technique allows you # to avoid warnings in the client about mixed media. # # ActionController::Base.asset_host = Proc.new { |source, request| # if request.ssl? # "#{request.protocol}#{request.host_with_port}" # else # "#{request.protocol}assets.example.com" # end # } # # You can also implement a custom asset host object that responds to +call+ # and takes either one or two parameters just like the proc. # # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new( # "http://asset%d.example.com", "https://asset1.example.com" # ) # # === Customizing the asset path # # By default, Rails appends asset's timestamps to all asset paths. This allows # you to set a cache-expiration date for the asset far into the future, but # still be able to instantly invalidate it by simply updating the file (and # hence updating the timestamp, which then updates the URL as the timestamp # is part of that, which in turn busts the cache). # # It's the responsibility of the web server you use to set the far-future # expiration date on cache assets that you need to take advantage of this # feature. Here's an example for Apache: # # # Asset Expiration # ExpiresActive On # # ExpiresDefault "access plus 1 year" # # # Also note that in order for this to work, all your application servers must # return the same timestamps. This means that they must have their clocks # synchronized. If one of them drifts out of sync, you'll see different # timestamps at random and the cache won't work. In that case the browser # will request the same assets over and over again even thought they didn't # change. You can use something like Live HTTP Headers for Firefox to verify # that the cache is indeed working. # # This strategy works well enough for most server setups and requires the # least configuration, but if you deploy several application servers at # different times - say to handle a temporary spike in load - then the # asset time stamps will be out of sync. In a setup like this you may want # to set the way that asset paths are generated yourself. # # Altering the asset paths that Rails generates can be done in two ways. # The easiest is to define the RAILS_ASSET_ID environment variable. The # contents of this variable will always be used in preference to # calculated timestamps. A more complex but flexible way is to set # ActionController::Base.config.asset_path to a proc # that takes the unmodified asset path and returns the path needed for # your asset caching to work. Typically you'd do something like this in # config/environments/production.rb: # # # Normally you'd calculate RELEASE_NUMBER at startup. # RELEASE_NUMBER = 12345 # config.action_controller.asset_path = proc { |asset_path| # "/release-#{RELEASE_NUMBER}#{asset_path}" # } # # This example would cause the following behavior on all servers no # matter when they were deployed: # # image_tag("rails.png") # # => Rails # stylesheet_link_tag("application") # # => # # Changing the asset_path does require that your web servers have # knowledge of the asset template paths that you rewrite to so it's not # suitable for out-of-the-box use. To use the example given above you # could use something like this in your Apache VirtualHost configuration: # # # # Some browsers still send conditional-GET requests if there's a # # Last-Modified header or an ETag header even if they haven't # # reached the expiry date sent in the Expires header. # Header unset Last-Modified # Header unset ETag # FileETag None # # # Assets requested using a cache-busting filename should be served # # only once and then cached for a really long time. The HTTP/1.1 # # spec frowns on hugely-long expiration times though and suggests # # that assets which never expire be served with an expiration date # # 1 year from access. # ExpiresActive On # ExpiresDefault "access plus 1 year" # # # # We use cached-busting location names with the far-future expires # # headers to ensure that if a file does change it can force a new # # request. The actual asset filenames are still the same though so we # # need to rewrite the location from the cache-busting location to the # # real asset location so that we can serve it. # RewriteEngine On # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L] module AssetTagHelper include TagHelper include JavascriptTagHelpers include StylesheetTagHelpers # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be :rss (default) or # :atom. Control the link options in url_for format using the # +url_options+. You can modify the LINK tag itself in +tag_options+. # # ==== Options # * :rel - Specify the relation of this link, defaults to "alternate" # * :type - Override the auto-generated mime type # * :title - Specify the title of the link, defaults to the +type+ # # ==== Examples # auto_discovery_link_tag # => # # auto_discovery_link_tag(:atom) # => # # auto_discovery_link_tag(:rss, {:action => "feed"}) # => # # auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "My RSS"}) # => # # auto_discovery_link_tag(:rss, {:controller => "news", :action => "feed"}) # => # # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "Example RSS"}) # => # def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {}) tag( "link", "rel" => tag_options[:rel] || "alternate", "type" => tag_options[:type] || Mime::Type.lookup_by_extension(type.to_s).to_s, "title" => tag_options[:title] || type.to_s.upcase, "href" => url_options.is_a?(Hash) ? url_for(url_options.merge(:only_path => false)) : url_options ) end # <%= favicon_link_tag %> # # generates # # # # You may specify a different file in the first argument: # # <%= favicon_link_tag '/myicon.ico' %> # # That's passed to +path_to_image+ as is, so it gives # # # # The helper accepts an additional options hash where you can override "rel" and "type". # # For example, Mobile Safari looks for a different LINK tag, pointing to an image that # will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad. # The following call would generate such a tag: # # <%= favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png' %> # def favicon_link_tag(source='/favicon.ico', options={}) tag('link', { :rel => 'shortcut icon', :type => 'image/vnd.microsoft.icon', :href => path_to_image(source) }.merge(options.symbolize_keys)) end # Computes the path to an image asset in the public images directory. # Full paths from the document root will be passed through. # Used internally by +image_tag+ to build the image path: # # image_path("edit") # => "/images/edit" # image_path("edit.png") # => "/images/edit.png" # image_path("icons/edit.png") # => "/images/icons/edit.png" # image_path("/icons/edit.png") # => "/icons/edit.png" # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png" # # If you have images as application resources this method may conflict with their named routes. # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and # plugin authors are encouraged to do so. def image_path(source) source.present? ? asset_paths.compute_public_path(source, 'images') : "" end alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route # Computes the path to a video asset in the public videos directory. # Full paths from the document root will be passed through. # Used internally by +video_tag+ to build the video path. # # ==== Examples # video_path("hd") # => /videos/hd # video_path("hd.avi") # => /videos/hd.avi # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi # video_path("/trailers/hd.avi") # => /trailers/hd.avi # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi def video_path(source) asset_paths.compute_public_path(source, 'videos') end alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route # Computes the path to an audio asset in the public audios directory. # Full paths from the document root will be passed through. # Used internally by +audio_tag+ to build the audio path. # # ==== Examples # audio_path("horse") # => /audios/horse # audio_path("horse.wav") # => /audios/horse.wav # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav # audio_path("/sounds/horse.wav") # => /sounds/horse.wav # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav def audio_path(source) asset_paths.compute_public_path(source, 'audios') end alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route # Computes the path to a font asset in the public fonts directory. # Full paths from the document root will be passed through. # # ==== Examples # font_path("font") # => /fonts/font # font_path("font.ttf") # => /fonts/font.ttf # font_path("dir/font.ttf") # => /fonts/dir/font.ttf # font_path("/dir/font.ttf") # => /dir/font.ttf # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf def font_path(source) asset_paths.compute_public_path(source, 'fonts') end alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route # Returns an html image tag for the +source+. The +source+ can be a full # path or a file that exists in your public images directory. # # ==== Options # You can add HTML attributes using the +options+. The +options+ supports # three additional keys for convenience and conformance: # # * :alt - If no alt text is given, the file name part of the # +source+ is used (capitalized and without the extension) # * :size - Supplied as "{Width}x{Height}", so "30x45" becomes # width="30" and height="45". :size will be ignored if the # value is not in the correct format. # * :mouseover - Set an alternate image to be used when the onmouseover # event is fired, and sets the original image to be replaced onmouseout. # This can be used to implement an easy image toggle that fires on onmouseover. # # ==== Examples # image_tag("icon") # => # Icon # image_tag("icon.png") # => # Icon # image_tag("icon.png", :size => "16x10", :alt => "Edit Entry") # => # Edit Entry # image_tag("/icons/icon.gif", :size => "16x16") # => # Icon # image_tag("/icons/icon.gif", :height => '32', :width => '32') # => # Icon # image_tag("/icons/icon.gif", :class => "menu_icon") # => # Icon # image_tag("mouse.png", :mouseover => "/images/mouse_over.png") # => # Mouse # image_tag("mouse.png", :mouseover => image_path("mouse_over.png")) # => # Mouse def image_tag(source, options = {}) options.symbolize_keys! src = options[:src] = path_to_image(source) unless src =~ /^(?:cid|data):/ || src.blank? options[:alt] = options.fetch(:alt){ image_alt(src) } end if size = options.delete(:size) options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$} end if mouseover = options.delete(:mouseover) options[:onmouseover] = "this.src='#{path_to_image(mouseover)}'" options[:onmouseout] = "this.src='#{src}'" end tag("img", options) end def image_alt(src) File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize end # Returns an html video tag for the +sources+. If +sources+ is a string, # a single video tag will be returned. If +sources+ is an array, a video # tag with nested source tags for each source will be returned. The # +sources+ can be full paths or files that exists in your public videos # directory. # # ==== Options # You can add HTML attributes using the +options+. The +options+ supports # two additional keys for convenience and conformance: # # * :poster - Set an image (like a screenshot) to be shown # before the video loads. The path is calculated like the +src+ of +image_tag+. # * :size - Supplied as "{Width}x{Height}", so "30x45" becomes # width="30" and height="45". :size will be ignored if the # value is not in the correct format. # # ==== Examples # video_tag("trailer") # => #