lib/lookbook/page.rb



module Lookbook
  class Page < Entity

    FRONTMATTER_FIELDS = [
      :id,
      :label,
      :title,
      :hidden,
      :landing,
      :position,
      :markdown,
      :header,
      :footer,
      :data
    ]

    attr_reader :errors
    attr_accessor :sections

    def initialize(path, base_path)
      @pathname = Pathname.new path
      @base_path = Pathname.new base_path
      @options = nil
      @errors = []
      @sections = []
      @page_name = remove_position_prefix(path_name)
      rel_path = @pathname.relative_path_from(@base_path)
      page_path = rel_path.dirname.to_s == "." ? @page_name : "#{rel_path.dirname}/#{@page_name}"
      super(page_path)
    end

    def url_path
      lookbook_page_path lookup_path
    end

    def full_path
      Rails.root.join(@pathname.to_s)
    end

    def name
      @page_name
    end

    def hidden?
      options[:hidden] == true
    end

    def markdown?
      options[:markdown] == true
    end

    def header?
      options[:header] == true
    end

    def footer?
      options[:footer] == true
    end

    def get(key)
      options[key]
    end

    def content
      @content ||= strip_frontmatter(file_contents).strip
    end

    def matchers
      normalize_matchers(label)
    end

    def parent_collections_names
      File.dirname(path).split("/")
    end

    def type
      :page
    end
    
    def id
      options[:id]
    end

    def position
      options[:position]
    end

    def hidden
      options[:hidden]
    end

    def label
      options[:label]
    end

    def method_missing(method_name, *args, &block)
      if args.none? && !block
        options[method_name]
      else
        super
      end
    end

    def respond_to_missing?(method_name, include_private = false)
      FRONTMATTER_FIELDS.include? method_name
    end

    protected

    def file_contents
      File.read(full_path)
    end

    def options
      return @options if @options
      begin
        frontmatter = (get_frontmatter(file_contents) || {}).deep_symbolize_keys
      rescue => exception
        frontmatter = {}
        line_number_match = exception.message.match(/.*line\s(\d+)/)
        @errors.push(Lookbook::Error.new(exception, **{
          title: "YAML frontmatter parsing error",
          file_path: @pathname.to_s,
          line_number: line_number_match ? line_number_match[1] : false
        }))
      end
      @options = Lookbook.config.page_options.deep_merge(frontmatter).with_indifferent_access
      @options[:id] = @options[:id] ? generate_id(@options[:id]) : generate_id(lookup_path)
      @options[:label] ||= name.titleize
      @options[:title] ||= @options[:label]
      @options[:hidden] ||= false
      @options[:landing] ||= false
      @options[:position] = @options[:position] ? @options[:position].to_i : get_position_prefix(path_name)
      @options[:markdown] ||= markdown_file?
      @options[:header] = true unless @options.key? :header
      @options[:footer] = true unless @options.key? :footer
      @options
    end

    def path_name
      @pathname.basename(@pathname.extname).to_s.gsub(/\.(html|md)$/, "")
    end

    def markdown_file?
      @pathname.basename(@pathname.extname).to_s.end_with?(".md")
    end

    class << self
      def find(path)
        all.find { |p| p.lookup_path == path }
      end

      def exists?(path)
        !!find(path)
      end

      def all
         pages, sections =
          Array(page_paths).flat_map do |dir|
            Dir["#{dir}/**/*.html.*", "#{dir}/**/*.md.*"].sort.map do |path|
              create(path, dir)
            end
          end.partition { |page| page.type == :page }

        sorted_pages = pages
          .uniq { |page| page.path }
          .sort_by { |page| [page.position, page.label] }

        page_dict = sorted_pages.index_by(&:path)
        sorted_sections = sections.sort_by { |section| [section.position, section.label] }
        
        sorted_sections.each do |section|
          page_dict[section.path].sections << section
        end

        PageCollection.new(sorted_pages)
      end

      def page_paths
        Lookbook.config.page_paths.select { |dir| Dir.exist? dir }
      end

      def section_path?(path)
        !!path.match(%r{\[(.*?\w+)\]})
      end

      def create(path, base_path)
        (section_path?(path) ? PageSection : Page).new(path, base_path)
      end
    end
  end
end