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

193 lines
4.9 KiB
Ruby
Raw Normal View History

2011-12-18 05:12:13 +01:00
# Data formats
2011-04-15 00:35:41 +02:00
require "yaml"
2011-09-12 19:20:34 +02:00
require "active_support/json"
2011-12-18 05:12:13 +01:00
# The data extension parses YAML and JSON files in the data/ directory
# and makes them available to config.rb, templates and extensions
module Middleman::CoreExtensions::Data
2011-12-18 05:12:13 +01:00
# Extension registered
2011-04-15 00:35:41 +02:00
class << self
2011-12-18 05:12:13 +01:00
# @private
2011-04-15 00:35:41 +02:00
def registered(app)
2011-07-06 19:30:24 +02:00
app.set :data_dir, "data"
2011-11-18 09:34:56 +01:00
app.send :include, InstanceMethods
2011-04-15 00:35:41 +02:00
end
2011-04-15 18:57:45 +02:00
alias :included :registered
end
2011-12-18 05:12:13 +01:00
# Instance methods
2011-11-18 09:34:56 +01:00
module InstanceMethods
2011-12-18 05:12:13 +01:00
# Setup data files before anything else so they are available when
# parsing config.rb
2011-11-18 09:34:56 +01:00
def initialize
self.files.changed DataStore.matcher do |file|
self.data.touch_file(file) if file.match(%r{^#{self.data_dir}\/})
2011-11-18 09:34:56 +01:00
end
self.files.deleted DataStore.matcher do |file|
self.data.remove_file(file) if file.match(%r{^#{self.data_dir}\/})
2011-11-18 09:34:56 +01:00
end
super
end
2011-12-18 05:12:13 +01:00
# The data object
#
# @return [DataStore]
2011-04-15 18:57:45 +02:00
def data
2011-11-18 09:34:56 +01:00
@data ||= DataStore.new(self)
end
# Makes a hash available on the data var with a given name
2011-12-18 05:12:13 +01:00
#
# @param [Symbol] name Name of the data, used for namespacing
# @param [Hash] content The content for this data
# @return [void]
2011-11-18 09:34:56 +01:00
def data_content(name, content)
DataStore.data_content(name, content)
end
# Makes a hash available on the data var with a given name
2011-12-18 05:12:13 +01:00
#
# @param [Symbol] name Name of the data, used for namespacing
# @return [void]
2011-11-18 09:34:56 +01:00
def data_callback(name, &block)
DataStore.data_callback(name, block)
2011-04-15 18:57:45 +02:00
end
end
2011-12-18 05:12:13 +01:00
# The core logic behind the data extension.
class DataStore
2011-12-18 05:12:13 +01:00
# Static methods
2011-11-21 06:21:19 +01:00
class << self
2011-12-18 05:12:13 +01:00
# The regex which tells Middleman which files are for data
#
# @return [Regexp]
2011-11-21 06:21:19 +01:00
def matcher
%r{[\w-]+\.(yml|yaml|json)$}
end
2011-12-18 05:12:13 +01:00
# Store static data hash
#
# @param [Symbol] name Name of the data, used for namespacing
# @param [Hash] content The content for this data
# @return [void]
2011-11-21 06:21:19 +01:00
def data_content(name, content)
@@local_sources ||= {}
@@local_sources[name.to_s] = content
end
2011-12-18 05:12:13 +01:00
# Store callback-based data
#
# @param [Symbol] name Name of the data, used for namespacing
# @param [Proc] proc The callback which will return data
# @return [void]
2011-11-21 06:21:19 +01:00
def data_callback(name, proc)
@@callback_sources ||= {}
@@callback_sources[name.to_s] = proc
end
end
2011-12-18 05:12:13 +01:00
# Setup data store
#
# @param [Middleman::Base] app The current instance of Middleman
2011-04-15 18:57:45 +02:00
def initialize(app)
@app = app
2011-11-10 06:19:11 +01:00
@local_data = {}
end
2011-12-18 05:12:13 +01:00
# Update the internal cache for a given file path
#
# @param [String] file The file to be re-parsed
# @return [void]
2011-11-10 06:19:11 +01:00
def touch_file(file)
file = File.expand_path(file, @app.root)
2011-11-10 06:19:11 +01:00
extension = File.extname(file)
basename = File.basename(file, extension)
if %w(.yaml .yml).include?(extension)
data = YAML.load_file(file)
elsif extension == ".json"
data = ActiveSupport::JSON.decode(File.read(file))
else
return
end
@local_data[basename] = ::Middleman.recursively_enhance(data)
2011-11-10 06:19:11 +01:00
end
2011-12-18 05:12:13 +01:00
# Remove a given file from the internal cache
#
# @param [String] file The file to be cleared
# @return [void]
2011-11-10 06:19:11 +01:00
def remove_file(file)
extension = File.extname(file)
basename = File.basename(file, extension)
@local_data.delete(basename) if @local_data.has_key?(basename)
2011-04-15 00:35:41 +02:00
end
2011-12-18 05:12:13 +01:00
# Get a hash hash from either internal static data or a callback
#
# @param [String, Symbol] path The name of the data namespace
# @return [Hash, nil]
def data_for_path(path)
response = nil
@@local_sources ||= {}
@@callback_sources ||= {}
if @@local_sources.has_key?(path.to_s)
response = @@local_sources[path.to_s]
elsif @@callback_sources.has_key?(path.to_s)
response = @@callback_sources[path.to_s].call()
end
response
end
2011-12-18 05:12:13 +01:00
# "Magically" find namespaces of data if they exist
#
# @param [String] path The namespace to search for
# @return [Hash, nil]
def method_missing(path)
2011-11-10 06:19:11 +01:00
if @local_data.has_key?(path.to_s)
return @local_data[path.to_s]
else
2011-11-10 06:19:11 +01:00
result = data_for_path(path)
if result
return ::Middleman.recursively_enhance(result)
2011-11-10 06:19:11 +01:00
end
end
2011-11-10 06:19:11 +01:00
super
end
2011-12-18 05:12:13 +01:00
# Convert all the data into a static hash
#
# @return [Hash]
def to_h
data = {}
@@local_sources ||= {}
@@callback_sources ||= {}
(@@local_sources || {}).each do |k, v|
data[k] = data_for_path(k)
end
(@@callback_sources || {}).each do |k, v|
data[k] = data_for_path(k)
end
2011-11-10 06:19:11 +01:00
(@local_data || {}).each do |k, v|
data[k] = v
2011-09-12 19:20:34 +02:00
end
data
end
2011-04-15 00:35:41 +02:00
end
end