diff options
Diffstat (limited to 'actionpack/lib/action_view/helpers')
7 files changed, 134 insertions, 116 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index ceb56824fa..db4da6f9c8 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -222,6 +222,14 @@ module ActionView # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "Example RSS"}) # # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" /> def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {}) + if !(type == :rss || type == :atom) && tag_options[:type].blank? + message = "You have passed type other than :rss or :atom to auto_discovery_link_tag and haven't supplied " + + "the :type option key. This behavior is deprecated and will be remove in Rails 4.1. You should pass " + + ":type option explicitly if you want to use other types, for example: " + + "auto_discovery_link_tag(:xml, '/feed.xml', :type => 'application/xml')" + ActiveSupport::Deprecation.warn message + end + tag( "link", "rel" => tag_options[:rel] || "alternate", diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index 39518268df..59e1015976 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -13,10 +13,9 @@ module ActionView # kick out old entries. For more on key-based expiration, see: # http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works # - # When using this method, you list the cache dependencies as part of - # the name of the cache, like so: + # When using this method, you list the cache dependency as the name of the cache, like so: # - # <% cache [ "v1", project ] do %> + # <% cache project do %> # <b>All the topics on this project</b> # <%= render project.topics %> # <% end %> @@ -24,15 +23,89 @@ module ActionView # This approach will assume that when a new topic is added, you'll touch # the project. The cache key generated from this call will be something like: # - # views/v1/projects/123-20120806214154 - # ^class ^id ^updated_at + # views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9 + # ^class ^id ^updated_at ^template tree digest # - # If you update the rendering of topics, you just bump the version to v2. - # Otherwise the cache is automatically bumped whenever the project updated_at - # is touched. + # The cache is thus automatically bumped whenever the project updated_at is touched. + # + # If your template cache depends on multiple sources (try to avoid this to keep things simple), + # you can name all these dependencies as part of an array: + # + # <% cache [ project, current_user ] do %> + # <b>All the topics on this project</b> + # <%= render project.topics %> + # <% end %> + # + # This will include both records as part of the cache key and updating either of them will + # expire the cache. + # + # ==== Template digest + # + # The template digest that's added to the cache key is computed by taking an md5 of the + # contents of the entire template file. This ensures that your caches will automatically + # expire when you change the template file. + # + # Note that the md5 is taken of the entire template file, not just what's within the + # cache do/end call. So it's possible that changing something outside of that call will + # still expire the cache. + # + # Additionally, the digestor will automatically look through your template file for + # explicit and implicit dependencies, and include those as part of the digest. + # + # ==== Implicit dependencies + # + # Most template dependencies can be derived from calls to render in the template itself. + # Here are some examples of render calls that Cache Digests knows how to decode: + # + # render partial: "comments/comment", collection: commentable.comments + # render "comments/comments" + # render 'comments/comments' + # render('comments/comments') + # + # render "header" => render("comments/header") + # + # render(@topic) => render("topics/topic") + # render(topics) => render("topics/topic") + # render(message.topics) => render("topics/topic") + # + # It's not possible to derive all render calls like that, though. Here are a few examples of things that can't be derived: + # + # render group_of_attachments + # render @project.documents.where(published: true).order('created_at') + # + # You will have to rewrite those to the explicit form: + # + # render partial: 'attachments/attachment', collection: group_of_attachments + # render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at') + # + # === Explicit dependencies + # + # Some times you'll have template dependencies that can't be derived at all. This is typically + # the case when you have template rendering that happens in helpers. Here's an example: + # + # <%= render_sortable_todolists @project.todolists %> + # + # You'll need to use a special comment format to call those out: + # + # <%# Template Dependency: todolists/todolist %> + # <%= render_sortable_todolists @project.todolists %> + # + # The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so. + # You can only declare one template dependency per line. + # + # === External dependencies + # + # If you use a helper method, for example, inside of a cached block and you then update that helper, + # you'll have to bump the cache as well. It doesn't really matter how you do it, but the md5 of the template file + # must change. One recommendation is to simply be explicit in a comment, like: + # + # <%# Helper Dependency Updated: May 6, 2012 at 6pm %> + # <%= some_helper_method(person) %> + # + # Now all you'll have to do is change that timestamp when the helper method changes. def cache(name = {}, options = nil, &block) if controller.perform_caching - safe_concat(fragment_for(name, options, &block)) + safe_concat(fragment_for(fragment_name_with_digest(name), options, &block)) else yield end @@ -58,6 +131,17 @@ module ActionView controller.write_fragment(name, fragment, options) end end + + def fragment_name_with_digest(name) + if @virtual_path + [ + *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name), + Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context) + ] + else + name + end + end end end end diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index dea2aa69dd..795440cacc 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -140,12 +140,19 @@ module ActionView # Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>. # # time_ago_in_words(3.minutes.from_now) # => 3 minutes + # time_ago_in_words(3.minutes.ago) # => 3 minutes # time_ago_in_words(Time.now - 15.hours) # => about 15 hours # time_ago_in_words(Time.now) # => less than a minute # time_ago_in_words(Time.now, :include_seconds => true) # => less than 5 seconds # # from_time = Time.now - 3.days - 14.minutes - 25.seconds # time_ago_in_words(from_time) # => 3 days + # + # from_time = (3.days + 14.minutes + 25.seconds).ago + # time_ago_in_words(from_time) # => 3 days + # + # Note that you cannot pass a <tt>Numeric</tt> value to <tt>time_ago_in_words</tt>. + # def time_ago_in_words(from_time, include_seconds_or_options = {}) distance_of_time_in_words(from_time, Time.now, include_seconds_or_options) end @@ -956,12 +963,15 @@ module ActionView # build_hidden(:year, 2008) # => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />" def build_hidden(type, value) - (tag(:input, { + select_options = { :type => "hidden", :id => input_id_from_type(type), :name => input_name_from_type(type), :value => value - }.merge(@html_options.slice(:disabled))) + "\n").html_safe + }.merge(@html_options.slice(:disabled)) + select_options.merge!(:disabled => 'disabled') if @options[:disabled] + + tag(:input, select_options) + "\n".html_safe end # Returns the name attribute for the input tag. diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index b79577bcd3..17fe7dc1b4 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -4,12 +4,12 @@ require 'action_view/helpers/tag_helper' require 'action_view/helpers/form_tag_helper' require 'action_view/helpers/active_model_helper' require 'action_view/helpers/tags' +require 'action_view/model_naming' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/string/inflections' -require 'action_controller/model_naming' module ActionView # = Action View Form Helpers @@ -115,7 +115,7 @@ module ActionView include FormTagHelper include UrlHelper - include ActionController::ModelNaming + include ModelNaming # Creates a form that allows the user to create or update the attributes # of a specific model object. @@ -1156,7 +1156,7 @@ module ActionView end class FormBuilder - include ActionController::ModelNaming + include ModelNaming # The methods which wrap a form helper call. class_attribute :field_helpers diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 9b35f076e5..dded9aab7c 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -1,10 +1,8 @@ -require 'action_controller/record_identifier' - module ActionView # = Action View Record Tag Helpers module Helpers module RecordTagHelper - include ActionController::RecordIdentifier + include ActionView::RecordIdentifier # Produces a wrapper DIV element with id and class parameters that # relate to the specified Active Record object. Usage example: diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index aaf0e0344a..9c76c26ace 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -1,5 +1,5 @@ require 'active_support/core_ext/object/try' -require 'action_controller/vendor/html-scanner' +require 'action_view/vendor/html-scanner' module ActionView # = Action View Sanitize Helpers diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index fe3240fdc1..3f65791aa0 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -2,7 +2,6 @@ require '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 @@ -20,115 +19,34 @@ module ActionView extend ActiveSupport::Concern - include ActionDispatch::Routing::UrlFor include TagHelper - # We need to override url_options, _routes_context - # and optimize_routes_generation? to consider the controller. - - def url_options #:nodoc: - return super unless controller.respond_to?(:url_options) - controller.url_options - end - - def _routes_context #:nodoc: - controller - end - protected :_routes_context - - def optimize_routes_generation? #:nodoc: - controller.respond_to?(:optimize_routes_generation?) ? - controller.optimize_routes_generation? : super + module ClassMethods + def _url_for_modules + ActionView::RoutingUrlFor + end end - protected :optimize_routes_generation? - # Returns the URL for the set of +options+ provided. This takes the - # same options as +url_for+ in Action Controller (see the - # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default - # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action" - # instead of the fully qualified URL like "http://example.com/controller/action". - # - # ==== Options - # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path. - # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified). - # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this - # is currently not recommended since it breaks caching. - # * <tt>:host</tt> - Overrides the default (current) host if provided. - # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided. - # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present). - # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present). - # - # ==== Relying on named routes - # - # Passing a record (like an Active Record) 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). - # - # ==== Implicit Controller Namespacing - # - # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one. - # - # ==== 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() - # - # <%= url_for(:action => 'index', :controller => 'users') %> - # # Assuming an "admin" namespace - # # => /admin/users - # - # <%= url_for(:action => 'index', :controller => '/users') %> - # # Specify absolute path with beginning slash - # # => /users - def url_for(options = nil) + # Basic implementation of url_for to allow use helpers without routes existence + def url_for(options = nil) # :nodoc: case options when String options - when nil, Hash - options ||= {} - options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys) - super when :back - controller.request.env["HTTP_REFERER"] || 'javascript:history.back()' + _back_url else - polymorphic_path(options) + raise ArgumentError, "arguments passed to url_for can't be handled. Please require " + + "routes or provide your own implementation" end end + def _back_url # :nodoc: + referrer = controller.respond_to?(:request) && controller.request.env["HTTP_REFERER"] + referrer || 'javascript:history.back()' + end + protected :_back_url + + # 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 |