module ActiveLinkTo # Creates a link tag of the given +name+ using a URL created by the set of # +options+. Please see documentation for link_to, as active_link_to is # basically a wrapper for it. This method accepts an optional +:active+ # parameter that dictates if the given link will have an extra css class # attached that marks it as 'active'. # # === Super Simple Example # Here's a link that will have class attached if it happens to be rendered # on page with path /people or any child of that page like /people/123 # # active_link_to 'People', '/people' # # => People # # This is exactly the same as: # # active_link_to 'People', '/people', :active => { :when => :self } # # => People # # === Options # Here's available options that can be inserted into :active hash # * :when => predefined symbol || Regexp || Array tuple of controller/actions || Boolean - This controls # when link is considered to be 'active' # * :active_class => 'class_name' - When link is 'active', css # class of that link is set to the default 'active'. This parameter # allows it to be changed # * :inactive_class => 'class_name' - Opposite of the :active_class # By default it's blank. However you can change it to whatever you want. # * :disable_link => Boolean - When link is active, sometimes you # want to render span tag instead of the link. This parameter is for that. # # === Examples # Most of the functionality of active_link_helper depends on the current # url. Specifically, request.request_uri value. We covered the basic example # already, so let's try sometihng more fun # # We want to highlight the link that matches immidiate url, and not the children # nodes as well # # # For URL: /people/24 # active_link_to 'People', people_path, :active => { :when => :self_only } # # => People # # # For URL: /people # active_link_to 'People', people_path, :active => { :when => :self_only } # # => People # # If we need to set link to be active based on some regular expression, we can do # that as well. Let's try to activate links urls of which begin with 'peop': # # # For URL: /people/44 # active_link_to 'People', people_path, :active => { :when => /^peop/ } # # => People # # # For URL: /aliens/9 # active_link_to 'People', people_path, :active => { :when => /^peop/ } # # => People # # What if we need to mark link active for all URLs that match a particular controller, # or action, or both? Or any number of those at the same time? Sure, why not: # # # For URL: /people/56/edit # # For matching multiple controllers and actions: # active_link_to 'Person Edit', edit_person_path(@person), :active => { # :when => [['people', 'aliens'], ['show', 'edit']] # } # # # for matching all actions under given controllers: # active_link_to 'Person Edit', edit_person_path(@person), :active => { # :when => [['people', 'aliens'], []] # } # # # for matching all controllers for a particular action # active_link_to 'Person Edit', edit_person_path(@person), :active => { # :when => [[], ['edit']] # } # # => People # # Sometimes it should be easy as setting a true or false: # # active_link_to 'People', people_path, :active => { :when => true } # # => People # # active_link_to 'People', people_path, :active => { :when => false } # # => People # # Lets see what happens when we push some css class modifier parameters # # # For URL: /people/12 # active_link_to 'People', people_path, :active => { :active_link => 'awesome_selected' } # # => People # # active_link_to 'Aliens', aliens_path, :active => { :inactive_link => 'bummer_inactive' } # # => Aliens # # Some wierd people think that link should change to a span if it indicated current page: # # # For URL: /people # active_link_to 'People', people_path, :active => { :disable_link => true } # # => People # def active_link_to(*args, &block) if block_given? name = capture(&block) options = args[0] || {} html_options = args[1] || {} else name = args[0] options = args[1] || {} html_options = args[2] || {} end options = options.clone html_options = html_options.clone url = url_for(options) active_link_options = html_options.delete(:active) || {} css_class = active_class(url, active_link_options) html_options[:class] ||= '' html_options[:class] += " #{css_class}" if !css_class.blank? html_options[:class].blank? ? html_options.delete(:class) : html_options[:class].lstrip! if active_link_options[:disable_link] === true && is_active_link?(url, active_link_options) content_tag(:span, name, html_options) else link_to(name, url, html_options) end end # Returns css class name. Takes the link's URL and :active paramers def active_class(url, options = {}) if is_active_link?(url, options) options[:active_class] || 'active' else options[:inactive_class] || '' end end # Returns true or false. Takes the link's URL and :active parameters def is_active_link?(url, options = {}) case options[:when] when :self, nil !request.fullpath.match(/^#{Regexp.escape(url)}(\/.*|\?.*)?$/).blank? when :self_only !request.fullpath.match(/^#{Regexp.escape(url)}\/?(\?.*)?$/).blank? when Regexp !request.fullpath.match(options[:when]).blank? when Array controllers = [*options[:when][0]] actions = [*options[:when][1]] (controllers.blank? || controllers.member?(params[:controller])) && (actions.blank? || actions.member?(params[:action])) when TrueClass true when FalseClass false end end end ActionView::Base.send :include, ActiveLinkTo