diff --git a/lib/jekyll/rp_logs/rp_log_converter.rb b/lib/jekyll/rp_logs/rp_log_converter.rb index 32c28cd..ce872d6 100644 --- a/lib/jekyll/rp_logs/rp_log_converter.rb +++ b/lib/jekyll/rp_logs/rp_log_converter.rb @@ -42,6 +42,7 @@ def initialize(config) RpLogGenerator.extract_settings(config) LogLine.extract_settings(config) Page.extract_settings(config) + Tag.extract_settings(config) Jekyll.logger.info "Loaded jekyll-rp_logs #{RpLogs::VERSION}" end @@ -122,7 +123,7 @@ def doc.render_with_liquid? ## # Returns a list of RpLogs::Page objects that are error-free. def extract_valid_rps(site) - site.collections[rp_key].docs.map { |p| RpLogs::Page.new(p,site.config) } + site.collections[rp_key].docs.map { |p| RpLogs::Page.new(p) } .reject do |p| message = p.errors?(self.class.parsers) skip_page(site, p, message) if message @@ -161,7 +162,7 @@ def convert_page(page, site, main_index, arcs, no_arc_rps) # Add key for canon/noncanon main_index.data["rps"][key] << page # Add tag for canon/noncanon - page[:rp_tags] << (Tag.new(key, site.config)) + page[:rp_tags] << (Tag.new(key)) page[:rp_tags].sort! arc_name = page[:arc_name] @@ -212,7 +213,7 @@ def sort_chronologically!(pages) def convert_rp(site, page) msg = catch :skip_page do - page.convert_rp(self.class.parsers,site.config) + page.convert_rp(self.class.parsers) return true end skip_page(site, page, msg) diff --git a/lib/jekyll/rp_logs/rp_page.rb b/lib/jekyll/rp_logs/rp_page.rb index f16ec26..34c0058 100644 --- a/lib/jekyll/rp_logs/rp_page.rb +++ b/lib/jekyll/rp_logs/rp_page.rb @@ -33,12 +33,12 @@ def tag_config(config) end end - def initialize(page,config) + def initialize(page) @page = page # If the tags exist, try to convert them to a list of Tag objects return unless self[:rp_tags].is_a?(String) - self[:rp_tags] = Tag[config, self[:rp_tags].split(",")] + self[:rp_tags] = Tag[self[:rp_tags].split(",")] end ## @@ -60,6 +60,28 @@ def tag_strings tags.map(&:to_s) end + def tag_set + if tags.uniq.length == tags.length + tags.to_set + else + tags.group_by{|i|i.to_s}.each_with_object([]){|(_,v),o| + if v.length == 1 + o << v[0] + else + tag = nil + v.each_with_object{|t| + if tag + tag.update_stats! t.stats + else + tag = t + end + } + o << tag + end + }.to_set + end + end + def canon self[:canon] ? "canon" : "noncanon" end @@ -68,7 +90,7 @@ def description self[:description] end - def convert_rp(parsers, config) + def convert_rp(parsers) compiled_lines = convert_all_lines(parsers) merge_lines! compiled_lines @@ -78,7 +100,7 @@ def convert_rp(parsers, config) split_output = compiled_lines.map(&:output) page.content = split_output.join("\n") - update_page_properties(stats, config) + update_page_properties(stats) true end @@ -113,8 +135,8 @@ def options ## # Updates tags with implications and aliases. - def update_tags(config) - self[:rp_tags] = Tag[config, self.class.tag_implication_handler.update_tags(tag_strings.to_set)] + def update_tags + self[:rp_tags] = self.class.tag_implication_handler.update_tags(tag_set).to_a self end @@ -215,13 +237,13 @@ def extract_stats(compiled_lines) # - Adds tags based on nicks involved, if the infer_char_tags option is # set to true. # - Updated end and start date. - def update_page_properties(stats, config) + def update_page_properties(stats) if self[:infer_char_tags] # Turn the nicks into characters - nick_tags = stats[:nicks].map! { |n| Tag.new("char:#{n}",config) } + nick_tags = stats[:nicks].map! { |n| Tag.new("char:#{n}") } self[:rp_tags] = (nick_tags.merge self[:rp_tags]).to_a.sort end - update_tags(config) + update_tags self[:end_date] = stats[:end_date] self[:start_date] ||= stats[:start_date] diff --git a/lib/jekyll/rp_logs/rp_tag_implication_handler.rb b/lib/jekyll/rp_logs/rp_tag_implication_handler.rb index dadd3fc..f8aaa79 100644 --- a/lib/jekyll/rp_logs/rp_tag_implication_handler.rb +++ b/lib/jekyll/rp_logs/rp_tag_implication_handler.rb @@ -24,13 +24,13 @@ def initialize(config) def update_tags(tag_set, verbose: false) removed_tags = Set.new loop do - previous_tags = tag_set.clone + previous_tags = tag_set.map{|t|t.to_s} implicate_tags(tag_set, removed_tags, verbose) alias_tags(tag_set, removed_tags) # Break when there is no change in tags. - return tag_set if tag_set == previous_tags + return tag_set if tag_set.map{|t|t.to_s} == previous_tags end end @@ -50,7 +50,7 @@ def validate_tag_rules error_for_aliases_that_should_be_implications # Check for loooops. - starter_tags = @tag_implications.keys.to_set.merge @tag_aliases.keys + starter_tags = Tag[@tag_implications.keys].to_set.merge Tag[@tag_aliases.keys] update_tags(starter_tags, verbose: true) end @@ -61,7 +61,7 @@ def validate_tag_rules # They can't remove tags. def implicate_tags(tag_set, removed_tags, verbose) until_tags_stabilize(tag_set) do |tag, to_add| - imply = @tag_implications.fetch(tag, []) + imply = @tag_implications.fetch(tag.to_s, []) removed, imply = imply.partition { |t| removed_tags.include? t } # It's okay if we want to imply a removed tag. Maybe? @@ -70,7 +70,9 @@ def implicate_tags(tag_set, removed_tags, verbose) Jekyll.logger.warn "#{tag} implies #{removed}, which #{string}. Consider implying "\ "the alised tag directly." end - + + imply = Tag[imply] + imply.each{|t| t.update_stats! tag.stats} to_add.merge imply end end @@ -79,18 +81,21 @@ def implicate_tags(tag_set, removed_tags, verbose) # Iteratively apply tag aliases until no more can be applied def alias_tags(tag_set, removed_tags) until_tags_stabilize(tag_set) do |tag, to_add| - next unless @tag_aliases.key? tag - aliased = @tag_aliases[tag] + next unless @tag_aliases.key? tag.to_s + aliased = @tag_aliases[tag.to_s] # If we are trying to alias back a tag already removed, there is a # cycle in the tag aliases and implications. removed = aliased.find { |t| removed_tags.include? t } - error_for_cyclical_tags(tag, removed) if removed + error_for_cyclical_tags(tag.to_s, removed) if removed # if it's already in the set, something weird happened - removed_tags << tag + removed_tags << tag.to_s tag_set.delete tag + aliased = Tag[aliased] + aliased.each{|t| t.update_stats! tag.stats} to_add.merge aliased + alias_loop = true end end @@ -107,6 +112,7 @@ def error_for_cyclical_tags(tag, removed_tag) # implications to add. def until_tags_stabilize(tag_set) tags_to_check = tag_set + alias_loop = false loop do # Because we use this set again as the tags to check we don't want # to clear it. @@ -116,6 +122,16 @@ def until_tags_stabilize(tag_set) end break if to_add.empty? + if alias_loop + tag_set = tag_set.to_a + to_add.each{|t| + index = tag_set.find_index{|v|v.eql? t} + if index + tag_set[index].update_stats t + end + } + tag_set = tag_set.to_set + end tag_set.merge to_add tags_to_check = to_add end diff --git a/lib/jekyll/rp_logs/rp_tags.rb b/lib/jekyll/rp_logs/rp_tags.rb index 6cab5e4..3b45c9c 100644 --- a/lib/jekyll/rp_logs/rp_tags.rb +++ b/lib/jekyll/rp_logs/rp_tags.rb @@ -16,6 +16,14 @@ module RpLogs class Tag include Comparable + class << self + attr_reader :char_tag_format + + def extract_settings(config) + @char_tag_format = (config["char_tag_format"] || "").downcase.freeze + end + end + TYPES = [:meta, :character, :general].freeze CHAR_FLAG = /^(char:|char-)(?.*)/ META_TAGS = /(safe|questionable|explicit|canon|noncanon|complete|incomplete)/ @@ -31,15 +39,15 @@ class Tag ## # Inspired by Hash, convert a list of strings to a list of Tags. - def self.[](config, *args) - args[0].map { |t| Tag.new(t,config) } + def self.[](*args) + args[0].map { |t| Tag.new(t) } end - def initialize(name,config={"char_tag_format"=>"none"} ) + def initialize(name) # inspect types my_name = name.strip if CHAR_FLAG =~ my_name - case (config["char_tag_format"] || "").downcase + case @char_tag_format when "upcase"; @name = $LAST_MATCH_INFO[:char_name].upcase when "downcase"; @name = $LAST_MATCH_INFO[:char_name].downcase when "capitalize_preserve"; @name = $LAST_MATCH_INFO[:char_name].gsub(/(?