lib/hx.rb in hx-0.13.0 vs lib/hx.rb in hx-0.14.0
- old
+ new
@@ -218,64 +218,92 @@
def get_entry(path)
@input.get_entry(add_circumfix(path))
end
end
+def self.cache_scope
+ saved_records = Thread.current[:hx_cache_records]
+ Thread.current[:hx_cache_records] = {}
+ begin
+ yield
+ ensure
+ Thread.current[:hx_cache_records] = saved_records
+ end
+end
+
class Cache
include Filter
+ class Record
+ def initialize
+ @paths = nil
+ @entries = {}
+ end
+
+ def clear_path(path)
+ @paths = nil
+ @entries.delete path
+ end
+
+ def get_entry_paths
+ @paths || (@paths = yield)
+ end
+
+ def get_entry(path)
+ begin
+ entry = @entries.fetch(path)
+ rescue IndexError
+ begin
+ entry = yield
+ rescue NoSuchEntryError
+ entry = nil
+ end
+ @entries[path] = entry
+ end
+ raise NoSuchEntryError, path unless entry
+ entry
+ end
+ end
+
+ attr_reader :input
+
def initialize(input, options={})
+ input = input.input while Cache === input
@input = input
- @lock = Mutex.new
- @entries = nil
- @entries_by_path = {}
end
+ def get_cache_record
+ (Thread.current[:hx_cache_records] ||= {})[@input] ||= Record.new
+ end
+
def edit_entry(path, prototype=nil)
@input.edit_entry(path, prototype) { |text| yield text }
+ get_cache_record.clear_path(path)
self
end
- def each_entry(selector)
- entries = nil
- @lock.synchronize do
- if @entries
- entries = @entries
- else
- entries = []
- @input.each_entry(Path::ALL) do |path, entry|
- @entries_by_path[path] = entry
- entries << [path, entry]
- end
- @entries = entries
- end
+ def each_entry_path(selector)
+ get_cache_record.get_entry_paths do
+ paths = []
+ @input.each_entry_path(Path::ALL) { |path| paths << path }
+ paths
+ end.each do |path|
+ yield path if selector.accept_path? path
end
- entries.each do |path, entry|
- yield path, entry.dup if selector.accept_path? path
- end
self
end
def get_entry(path)
- entry = nil
- @lock.synchronize do
- if @entries_by_path.has_key? path
- entry = @entries_by_path[path]
- else
- entry = @input.get_entry(path)
- @entries_by_path[path] = entry
- end
- end
- return entry.dup
+ get_cache_record.get_entry(path) { @input.get_entry(path) }
end
end
class Sort
include Filter
def initialize(input, options)
- @input = input
+ @input = Cache.new(input)
@key_fields = Array(options[:sort_by] || []).map { |f| f.to_s }
@reverse = !!options[:reverse]
end
def edit_entry(path, prototype=nil)
@@ -436,12 +464,10 @@
if raw_source.has_key? 'sort_by' or raw_source.has_key? 'reverse'
source = Sort.new(source, :sort_by => raw_source['sort_by'],
:reverse => raw_source['reverse'])
end
- source = Cache.new(source) if raw_source['cache']
-
source
end
class Site
include Filter
@@ -450,46 +476,54 @@
attr_reader :sources
class << self
private :new
- def load_file(config_file)
+ def load_file(config_file, option_overrides={})
File.open(config_file, 'r') do |stream|
- load(stream, config_file)
+ load(stream, config_file, option_overrides)
end
end
- def load(io, config_path)
+ def load(io, config_file, option_overrides={})
raw_config = YAML.load(io)
- load_raw(raw_config, config_path)
- end
-
- def load_raw(raw_config, config_path)
options = {}
- options[:base_dir] = File.dirname(config_path)
+ options[:base_dir] = File.dirname(config_file)
for key, value in raw_config.fetch('options', {})
options[key.intern] = value
end
- options[:config_file] = config_path
+ options[:config_file] = config_file
+ options.update(option_overrides)
if raw_config.has_key? 'require'
for library in raw_config['require']
Hx.local_require(options, library)
end
end
raw_sources_by_name = raw_config.fetch('sources', {})
+ raw_outputs = raw_config.fetch('output', [])
source_names = raw_sources_by_name.keys
# build input dependency graph
source_dependencies = {}
+ source_count_by_dependency = Hash.new(0)
for name, raw_source in raw_sources_by_name
raw_source = Hx.expand_chain(raw_source)
if raw_source.has_key? 'input'
- source_dependencies[name] = raw_source['input']
+ input_name = raw_source['input']
+ source_dependencies[name] = input_name
+ source_count_by_dependency[input_name] += 1
end
end
+ for raw_output in raw_outputs
+ raw_output = Hx.expand_chain(raw_output)
+ if raw_output.has_key? 'input'
+ input_name = raw_source['input']
+ source_count_by_dependency[input_name] += 1
+ end
+ end
# calculate depth for each input in the graph
source_depths = Hash.new(0)
for name in source_names
seen = Set[] # for cycle detection
@@ -508,15 +542,18 @@
depth_first_names = source_names.sort_by { |n| -source_depths[n] }
sources = {}
for name in depth_first_names
raw_source = raw_sources_by_name[name]
- sources[name] = Hx.build_source(options, NULL_INPUT, sources,
- raw_source)
+ source = Hx.build_source(options, NULL_INPUT, sources, raw_source)
+ if source_count_by_dependency[name] > 1
+ source = Cache.new(source, options)
+ end
+ sources[name] = source
end
outputs = []
- for raw_output in raw_config.fetch('output', [])
+ for raw_output in raw_outputs
outputs << Hx.build_source(options, NULL_INPUT, sources, raw_output)
end
new(options, sources, outputs)
end