twitter/twitter-cldr-rb

View on GitHub
lib/twitter_cldr/resources/cldr_document_set.rb

Summary

Maintainability
A
0 mins
Test Coverage
# encoding: UTF-8

# Copyright 2012 Twitter, Inc
# http://www.apache.org/licenses/LICENSE-2.0

require 'forwardable'
require 'nokogiri'

module TwitterCldr
  module Resources
    class CldrDocumentSet

      class Element
        extend Forwardable

        def_delegators :@element, :attribute, :content, :name, :path

        attr_reader :docset, :element

        def initialize(docset, element)
          @docset = docset
          @element = element
        end

        def xpath(path)
          path = CldrDocumentSet.join_xpaths(docset.path_for(element), path)
          docset.xpath(path)
        end
      end

      class ElementList
        include Enumerable
        extend Forwardable

        def_delegators :@elements, :size

        attr_reader :docset, :elements

        def initialize(docset, elements)
          @docset = docset
          @elements = elements
        end

        def [](idx)
          return unless elements[idx]

          Element.new(docset, elements[idx])
        end

        def first
          self[0]
        end

        def each
          return to_enum(__method__) unless block_given?

          elements.size.times do |idx|
            yield self[idx]
          end
        end
      end


      def self.join_xpaths(*paths)
        segments = paths.flat_map { |a| a.chomp('/').split('/') }
        segments = segments.each_with_object([]) do |segment, result|
          if segment == '..'
            result.pop
          else
            result << segment
          end
        end
        segments.join('/')
      end

      attr_reader :path, :cldr_locale, :cldr_requirement

      def initialize(path, cldr_locale, cldr_requirement)
        @path = path
        @cldr_locale = cldr_locale
        @cldr_requirement = cldr_requirement
      end


      def xpath(path)
        cldr_locale.ancestors.each do |ancestor_locale|
          data = doc_for(ancestor_locale).xpath(path)

          unless data.empty?
            return ElementList.new(self, resolve_aliases_in(data))
          end
        end

        ElementList.new(self, [])
      end

      def path_for(node)
        orig_node = node
        path = []

        while node
          path << selector_for(node)
          node = node.parent
          break if node.name == 'document'
        end

        "//#{path.reverse.join('/')}"
      end

      private

      def resolve_aliases_in(data)
        alias_nodes = data.xpath('.//alias')
        alias_nodes.each do |alias_node|
          alias_path = alias_node.attribute('path').value
          full_path = join_xpaths(path_for(alias_node.parent), alias_path)

          cldr_locale.ancestors.find do |ancestor_locale|
            resolved_node = doc_for(ancestor_locale).xpath(full_path).first.dup

            if resolved_node
              resolved_copy = Nokogiri::XML(resolved_node.to_xml).children.first
              parent = alias_node.parent
              alias_node.replace(resolved_copy.children)
              resolve_aliases_in(parent)
              break
            end
          end
        end

        data
      end

      def join_xpaths(*paths)
        self.class.join_xpaths(*paths)
      end

      def selector_for(node)
        node.name.dup.tap do |selector|
          if type = node.attribute('type')
            selector << "[@type='#{type.value}']"
          end
        end
      end

      def doc_for(locale)
        locale_fs = locale.to_s.gsub('-', '_')
        docs[locale_fs] ||= Nokogiri.XML(File.read(File.join(path, "#{locale_fs}.xml")))
      end

      def docs
        @docs ||= {}
      end
    end

  end
end