lib/middleman-core/core_extensions/data/stores/local_file.rb



require 'hamster'
require 'set'
require 'middleman-core/contracts'
require 'middleman-core/core_extensions/data/stores/base'
require 'middleman-core/dependencies/vertices/vertex'
require 'middleman-core/dependencies/vertices/file_vertex'

module Middleman
  module CoreExtensions
    module Data
      module Stores
        # JSON and YAML files in the data/ directory
        class LocalFileDataStore < BaseDataStore
          extend Forwardable
          include Contracts

          YAML_EXTS = Set.new %w[.yaml .yml]
          JSON_EXTS = Set.new %w[.json]
          ALL_EXTS = YAML_EXTS | JSON_EXTS

          def_delegators :@local_data, :keys, :key?, :[]

          # Contract IsA['::Middleman::Application'] => Any
          def initialize(app)
            super()

            @app = app
            @local_data = {}
          end

          Contract ArrayOf[IsA['Middleman::SourceFile']], ArrayOf[IsA['Middleman::SourceFile']] => Any
          def update_files(updated_files, removed_files)
            updated_files.each(&method(:touch_file))
            removed_files.each(&method(:remove_file))

            @app.sitemap.rebuild_resource_list!(:touched_data_file)
          end

          # Update the internal cache for a given file path
          #
          # @param [String] file The file to be re-parsed
          # @return [void]
          Contract IsA['Middleman::SourceFile'] => Any
          def touch_file(file)
            data_path = file[:relative_path]
            extension = File.extname(data_path)
            basename  = File.basename(data_path, extension)

            return unless ALL_EXTS.include?(extension)

            if YAML_EXTS.include?(extension)
              data, postscript = ::Middleman::Util::Data.parse(file, @app.config[:frontmatter_delims], :yaml)
              data[:postscript] = postscript if !postscript.nil? && data.is_a?(Hash)
            elsif JSON_EXTS.include?(extension)
              data, _postscript = ::Middleman::Util::Data.parse(file, @app.config[:frontmatter_delims], :json)
            end

            data_branch = @local_data

            paths = data_path.to_s.split(File::SEPARATOR)[0..-2]
            paths.each do |dir|
              data_branch[dir.to_sym] ||= {}
              data_branch = data_branch[dir.to_sym]
            end

            data_branch[basename.to_sym] = data
          end

          # Remove a given file from the internal cache
          #
          # @param [String] file The file to be cleared
          # @return [void]
          Contract IsA['Middleman::SourceFile'] => Any
          def remove_file(file)
            data_path = file[:relative_path]
            extension = File.extname(data_path)
            basename  = File.basename(data_path, extension)

            data_branch = @local_data

            path = data_path.to_s.split(File::SEPARATOR)[0..-2]
            path.each do |dir|
              data_branch = data_branch[dir.to_sym]
            end

            data_branch.delete(basename.to_sym) if data_branch.key?(basename.to_sym)
          end
        end
      end
    end
  end
end