aadlani/nanoc-toolbox

View on GitHub
lib/nanoc/toolbox/helpers/tagging_extra.rb

Summary

Maintainability
A
25 mins
Test Coverage
module Nanoc::Toolbox::Helpers

  # NANOC Helper for added tagging functions
  #
  # This module contains functions for ...
  #
  # @see http://groups.google.com/group/nanoc/browse_thread/thread/caefcab791fd3c4b
  module TaggingExtra
    include Nanoc::Helpers::Blogging

    # Returns all the tags present in a collection of items. The tags are
    # only present once in the returned value. When called whithout
    # parameters, all the site items are considered.
    #
    # @param [Array<Nanoc::Item>] items
    # @return [Array<String>] An array of tags
    def tag_set(items=nil)
      items ||= @items
      items.map { |i| i[:tags] }.flatten.uniq.delete_if{|t| t.nil?}
    end

    # Return true if an item has a specified tag
    #
    # @param [Nanoc::Item] item
    # @param [String] tag
    # @return [Boolean] true if the item contains the specified tag
    def has_tag?(item, tag)
      return false if item[:tags].nil?
      item[:tags].include? tag
    end

    # Finds all the items having a specified tag. By default the method search
    # in all the site items. Alternatively, an item collection can be passed as
    # second (optional) parameter, to restrict the search in the collection.
    #
    # @param [Array<Nanoc::Item>] items
    # @param [String] tag
    # @param [Nanoc::Item] item
    def items_with_tag(tag, items=nil)
      items = sorted_articles if items.nil?
      items.select { |item| has_tag?( item, tag ) }
    end

    # Count the tags in a given collection of items. By default, the method
    # counts tags in all the site items. The result is an hash such as:
    # { tag => count }.
    #
    # @param [Array<Nanoc::Item>] items
    # @return [Hash] Hash indexed by tag name with the occurences as value
    def count_tags(items=nil)
      items ||= @items
      tags = items.map { |i| i[:tags] }.flatten.delete_if{|t| t.nil?}
      tags.inject(Hash.new(0)) {|h,i| h[i] += 1; h }
    end

    # Sort the tags of an item collection (defaults to all site items) in 'n'
    # classes of rank. The rank 0 corresponds to the most frequent tags.
    # The rank 'n-1' to the least frequents. The result is a hash such as:
    # { tag => rank }
    #
    # @param [Integer] n number of rank
    # @param [Array<Nanoc::Item>] items
    def rank_tags(n, items=nil)
      raise ArgumentError, 'the number of ranks should be > 1' if n < 2

      items = @items if items.nil?
      count = count_tags( items )
      min, max = count.values.minmax

      ranker = lambda { |num| n - 1 - (num - min) / (max - min) }

      Hash[count.map {|tag,value| [tag, ranker.call(value) ] }]
    end

    # Returns an Array of links to tags in the item, optionally omits the
    # given tags from the selection
    #
    # @param [Item] item the item from which to extract tags
    # @param [Array<String>] omit_tags tags that should be excluded
    # @param [Item] options the options for the links to be created
    # @option options [String] :tag_pattern ("%%tag%%") The pattern to be replace by the tag name
    # @option options [String] :title ("options[:tag_pattern]") The text that will be in the link
    # @option options [String] :file_extension (".html") The file extension
    # @option options [String] :url_format ("/tags/:tag_pattern:file_extension") The path pattern
    #
    # @return [Array<String>] An array of html links
    #
    # @example
    #   omited = ['strange_tag']
    #   options = { :tag_pattern => "%%TAGNAME%%",
    #               :title       => "articles tagged with %%TAGNAME%%",
    #               :url_format  => "/tags/tag_%%TAGNAME%%.html"}
    #   tag_links_for(item, omited, options) # => ['<a href="/tags/tag_a.html">articles tagged with a</a>', '<a href="/tags/tag_b.html">articles tagged with b</a>']
    def tag_links_for(item, omit_tags=[], options={})
      tags = []
      return tags unless item[:tags]

      options[:tag_pattern]     ||= "%%tag%%"
      options[:title]           ||= options[:tag_pattern]
      options[:file_extension]  ||= ".html"
      options[:url_format]      ||= "/tags/#{options[:tag_pattern]}#{options[:file_extension]}"

      tags = item[:tags] - omit_tags

      tags.map! do |tag|
          title = options[:title].gsub(options[:tag_pattern], tag.downcase)
          url = options[:url_format].gsub(options[:tag_pattern], tag.downcase)
          content_tag('a', title, {:href => url})
      end
    end

    # Creates in-memory tag pages from tags of the passed items or form all items
    #
    # @param [Array<Item>] item the item from which to extract tags
    # @param [Item] options the options for the item to be created
    # @option options [String] :tag_pattern ("%%tag%%") The pattern to be replace by the tag name
    # @option options [String] :title ("options[:tag_pattern]") The text that will be in the link
    # @option options [String] :identifier ("/tags/option[:tag_pattern]") The item identifer
    # @option options [String] :template ("tag") The template/layout to use to render the tag
    def create_tag_pages(items=nil, options={})
      options[:tag_pattern]     ||= "%%tag%%"
      options[:title]           ||= options[:tag_pattern]
      options[:identifier]      ||= "/tags/#{options[:tag_pattern]}/"
      options[:template]        ||= "tag"

      tag_set(items).each do |tagname|
        raw_content = "<%= render('#{options[:template]}', :tag => '#{tagname}') %>"
        attributes  = { :title => options[:title].gsub(options[:tag_pattern], tagname) }
        identifier  = options[:identifier].gsub(options[:tag_pattern], tagname)

        @items << Nanoc::Item.new(raw_content, attributes, identifier, :binary => false)
      end
    end
  end
end