# frozen_string_literal: true require 'amazing_print' require 'nokogiri' require 'erubis' require 'active_support' require 'active_support/core_ext' require 'pry' require 'helpers/erubis_helper' namespace :usps do namespace :api do task :update do def dig_set(obj, keys, value) key = keys.first if keys.length == 1 obj[key] = value else obj[key] = {} unless obj[key] dig_set(obj[key], keys.slice(1..-1), value) end end def table_array_to_hash_array(columns, rows) rows.map do |row| row_data = {} columns.each_with_index do |k, i| row_data[k.to_s.gsub(/[[:space:]]+/, ' ').strip] = row[i].is_a?(String) ? row[i].to_s.gsub(/[[:space:]]+/, ' ').strip : row[i] end row_data end end def standardize_table_hash_array(arr) arr.map do |option| next if option['Tag Name'].blank? || option['Tag Name'].split('/').pop.strip.casecmp('userid').zero? { type: option['Type'], name: option['Tag Name'].split('/').pop.strip, required: option['Occurs'] == 'Required', description: option['Description'], } end.compact end def table_hash_array_to_shape(arr) shape = {} nesting = [] arr[0][:type] = '(Alias)' unless arr[0][:type].casecmp('(alias)').zero? arr.each do |tag| if tag[:type].casecmp('(alias)').zero? if nesting.last == tag[:name] nesting.pop else nesting.push(tag[:name]) dig_set(shape, nesting, tag.merge(children: {})) end elsif tag[:type].casecmp('(group)').zero? if nesting.last == tag[:name] nesting.pop(2) else nesting.push(:children) nesting.push(tag[:name]) dig_set(shape, nesting, tag.merge(children: {})) end elsif nesting.length.positive? nesting_with_children = nesting + [:children] dig_set(shape, nesting_with_children, shape.dig(*nesting_with_children).merge({ tag[:name] => tag.merge(children: {}) })) else shape[tag[:name]] = tag end end shape end def pp_xml(xml = '') doc = Nokogiri.XML(xml) do |config| config.default_xml.noblanks end doc.to_xml(indent: 2) end @failed_methods = [] @endpoint_files = [] def parse_doc(path) doc = Nokogiri::HTML(File.open(path)) table_of_contents = {} raw_table_of_contents = doc.search('sdt[docparttype="Table of Contents"] p a').map do |link| full_title = link.content.tr("\n", ' ') next if full_title.blank? full_version = full_title.match(/^[\d.]+/)[0] major, minor, tiny = full_version.split('.') title = full_title.match(/\s+[A-Za-z\s\t.:]*\s+/)[0].gsub(/[[:space:]]+/, ' ').strip.chomp('.') data = { # full_title: full_title, # link: link['href'], anchor: link['href'].split('#')[1], version: full_version, major: major, minor: minor, tiny: tiny, title: title, } table_of_contents[major] ||= [] table_of_contents[major] << data data end data = table_of_contents.map do |section_number, section| section_data = {} section.each do |subsection| header_node = doc.search("a[name=#{subsection[:anchor]}]")[0].parent header_node = header_node.next_element if header_node.content.blank? && header_node.next_element content_node = if header_node.next_element && header_node.next_element.content.present? header_node.next_element elsif header_node.parent.next_element && header_node.parent.next_element.content.present? header_node.parent.next_element else header_node.parent.parent.next_element end next unless content_node # For section 1 section_data[:link] = subsection[:link] if subsection[:version] == "#{section_number}.0" section_data[:title] = subsection[:title] elsif subsection[:title] == 'Overview' section_data[:description] = content_node.content.tr("\n", ' ') elsif subsection[:title] == 'API Signature' columns, *rows = content_node.search('tr').map do |tr| tr.search('td').map do |td| td.content.gsub(/\n\s/, '').strip end end section_data[:signature] = table_array_to_hash_array(columns, rows) section_data[:url] = "#{section_data[:signature][0]['Scheme']}#{section_data[:signature][0]['Host']}#{section_data[:signature][0]['Path'].chomp('?')}" section_data[:group] = section_data[:signature][0]['API'].sub('API=', '').gsub(/[[:space:]]+/, ' ').strip # ap section_data[:signature] # ap section_data[:signature].map{|s| s[]} # elsif subsection[:title] == "Request Descriptions" # columns, *rows = content_node.search('tr').map{|tr| tr.search('td').map{|td| td.content.gsub(/\n\s/, '').strip}} # section_data[:request_descriptions] = table_hash_array_to_shape(standardize_table_hash_array(table_array_to_hash_array(columns, rows))) elsif subsection[:title] == 'Sample Request' title, xml = content_node.content.gsub(/[[:space:]]+/, ' ').strip.split('<', 2) xml = pp_xml("<#{xml}") section_data[:sample_request] = <<~XML #{title} #{xml} XML elsif subsection[:title] == 'Response Descriptions' || subsection[:title].match?(/Request (Tag )*Descriptions/) # Some documents have Tag, some don't subsection[:title] = 'Request Descriptions' if subsection[:title] == 'Request Tag Descriptions' columns, *rows = content_node.search('tr').map do |tr| next if tr.ancestors('table').length > 1 tr.search('td').map do |td| has_table = td.search('table').length.positive? if !has_table td.content.gsub(/\n\s/, '').strip else table = td.search('table')[0] c, *r = table.search('tr').map do |tr| tr.search('td').map do |td| td.content.gsub(/\n\s/, '').strip end end table_array_to_hash_array(c, r) end end end.compact binding.pry if rows.blank? section_data[subsection[:title].downcase.gsub(/\s+/, '_').to_sym] = table_hash_array_to_shape(standardize_table_hash_array(table_array_to_hash_array( columns, rows ))) elsif subsection[:title] == 'Sample Response' title, xml = content_node.content.gsub(/[[:space:]]+/, ' ').strip.split('<', 2) xml = pp_xml("<#{xml}") section_data[:sample_response] = <<~XML #{title} #{xml} XML end end section_data end method_template = Erubis::Eruby.new(File.read('lib/usps/api/templates/method.erb')) method_spec_template = Erubis::Eruby.new(File.read('lib/usps/api/templates/method_spec.erb')) data.each do |api| next if api[:group].nil? || api[:group].empty? # begin rendered_method = method_template.result(group: api[:group], data: api) File.write("lib/usps/api/endpoints/#{api[:group].underscore}.rb", rendered_method) rendered_method_spec = method_spec_template.result(group: api[:group], data: api) File.write("spec/usps/api/endpoints/#{api[:group].underscore}_spec.rb", rendered_method_spec) @endpoint_files << { file: api[:group].underscore, module: api[:group].camelize, } # rescue => e # @failed_methods << { # file: path, # api: api[:group], # } # end end end failed_docs = [] Dir.glob('lib/data/api/*.html').each do |f| ap f # begin parse_doc(f) # rescue => e # failed_docs << f # end end endpoints_template = Erubis::Eruby.new(File.read('lib/usps/api/templates/endpoints.erb')) File.write( 'lib/usps/api/endpoints.rb', endpoints_template.result(files: @endpoint_files.sort_by { |f| f[:file] }) ) end end end