refinery/refinerycms

View on GitHub
pages/lib/refinery/pages/finder.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Refinery
  module Pages
    class Finder
      def self.by_path(path)
        FinderByPath.new(path).find
      end

      def self.by_path_or_id(path, id)
        FinderByPathOrId.new(path, id).find
      end

      def self.by_title(title)
        FinderByTitle.new(title).find
      end

      def self.by_slug(slug, conditions = {})
        FinderBySlug.new(slug, conditions).find
      end

      def self.with_mobility(conditions = {})
        Finder.new(conditions).find
      end

      def initialize(conditions)
        @conditions = conditions
      end

      def find
        with_mobility
      end

      def with_mobility
        mobility_conditions = {:locale => ::Mobility.locale.to_s}.merge(conditions)
        translations_conditions = translations_conditions(mobility_conditions)

        # A join implies readonly which we don't really want.
        Page.i18n.where(mobility_conditions).
             joins(:translations).
             where(translations_conditions).
             readonly(false)
      end

      private
      attr_accessor :conditions

      def translated_attributes
        Page.translated_attribute_names.map(&:to_s) | %w(locale)
      end

      def translations_conditions(original_conditions)
        translations_conditions = {}
        original_conditions.keys.each do |key|
          if translated_attributes.include? key.to_s
            translations_conditions["#{Page::Translation.table_name}.#{key}"] = original_conditions.delete(key)
          end
        end
        translations_conditions
      end

    end

    class FinderByTitle < Finder
      def initialize(title)
        @title = title
        @conditions = default_conditions
      end

      def default_conditions
        { :title => title }
      end

      private
      attr_accessor :title
    end

    class FinderBySlug < Finder
      def initialize(slug, conditions)
        @slug = slug
        @conditions = default_conditions.merge(conditions)
      end

      def default_conditions
        {
          :locale => Refinery::I18n.frontend_locales.map(&:to_s),
          :slug => slug
        }
      end

      private
      attr_accessor :slug
    end

    class FinderByPath
      def initialize(path)
        @path = path
      end

      def find
        if slugs_scoped_by_parent?
          FinderByScopedPath.new(path).find
        else
          FinderByUnscopedPath.new(path).find
        end
      end

      private
      attr_accessor :path

      def slugs_scoped_by_parent?
        ::Refinery::Pages.scope_slug_by_parent
      end

      def by_slug(slug_path, conditions = {})
        Finder.by_slug(slug_path, conditions)
      end
    end

    class FinderByScopedPath < FinderByPath
      def find
        # With slugs scoped to the parent page we need to find a page by its full path.
        # For example with about/example we would need to find 'about' and then its child
        # called 'example' otherwise it may clash with another page called /example.
        page = parent_page
        while page && path_segments.any? do
          page = next_page(page)
        end
        page
      end

      private

      def path_segments
        @path_segments ||= path.split('/').select(&:present?)
      end

      def parent_page
        parent_page_segment = path_segments.shift
        if parent_page_segment.friendly_id?
          by_slug(parent_page_segment, :parent_id => nil).first
        else
          Page.find(parent_page_segment)
        end
      end

      def next_page(page)
        slug_or_id = path_segments.shift
        page.children.by_slug(slug_or_id).first || page.children.find(slug_or_id)
      end
    end

    class FinderByUnscopedPath < FinderByPath
      def find
        by_slug(path).first
      end
    end

    class FinderByPathOrId
      def initialize(path, id)
        @path = path
        @id = id
      end

      def find
        if path.present?
          if path.friendly_id?
            FinderByPath.new(path).find
          else
            Page.friendly.find(path)
          end
        elsif id.present?
          Page.friendly.find(id)
        end
      end

      private
      attr_accessor :id, :path
    end
  end
end