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