# frozen_string_literal: true module MediaWiktory class Generator class Param < Hashie::Mash disable_warnings class << self def from_html_nodes(name, dds, prefix: nil) new( full_name: name, name: name.sub(/^#{prefix}/, ''), prefix: prefix, description: extract_description(dds), type: extract_type(dds), vals: extract_values(dds) # TODO: lost params # limit: extract_limit(dds), # default: extract_default(dds) # TODO: mandatoriness: # * optional # * mandatory # * this OR that is mandatory # * this AND that can't intersect # * and so on ) end def extract_description(els) els.detect { |e| e.attr('class') == 'description' }.at('p')._n.text.to_s.strip.tr("\n", ' ') end def extract_type(els) # TODO: "Expiry time. May be relative (e.g. 5 months or 2 weeks) or absolute # (e.g. 2014-09-18T12:34:56Z). If set to infinite, indefinite, or never, the block will # never expire." els.select { |e| e.attr('class') == 'info' }.each do |el| case el.text when /^Type: ([^\(]+)\s*($|\()/ return Regexp.last_match(1).strip when /^Values \(separate with \|/, /^Separate values with \|/ return 'list' when /^One of the following values:/ return 'enum' end end 'string' end def extract_default(els) els.each { |el| return Regexp.last_match(1) if el =~ /^Default:\s*(.+)$/ } nil end def extract_values(els) # 1. try dl from definition els.detect { |e| e.attr('class') == 'description' }.tap { |d| if d.at('dl') return d.at('dl').each_term.map { |dts, dds| {name: dts.first.text, description: dds.first.text.tr("\n", ' ')} } end } # 2. ...or take from info els.select { |e| e.attr('class') == 'info' }.each do |el| if el.text =~ /^(?:One of the following values||Values \(separate with \|.*?\)):\s*(.+)$/ if el.search('a').count > 1 return el.search('a').reject { |a| a.text == 'alternative' } .map { |a| { name: a.text, module: a.attr('href').scan(/^.*[\#+](.+)$/).flatten.first } } else return Regexp.last_match(1).sub(/^Can be empty, or/, '').split(',') .map { |s| s.gsub(/^[[:space:]]|[[:space:]]$/, '') } end end end nil end end attr_accessor :api def inspect "#<#{self.class} #{name} (#{type})>" end alias_method :to_s, :inspect def prefix=(pre) super self.full_name = "#{prefix}#{name}" end def full_name self['full_name']._n.include?('-') ? "'#{self['full_name']}'" : self['full_name'] end def method_name name.tr('-', '_') end def modules_hash modules.map { |m| module_in_hash(m) }.join(', ') end def enum_values return [] unless vals if vals.all? { |v| v.is_a?(String) } vals.map(&:inspect).join(', ') elsif vals.all? { |v| v.is_a?(Hash) && v.key?(:description) } vals.map { |v| v[:name].inspect }.join(', ') elsif modules? vals.map { |v| v[:name].to_sym.inspect }.join(', ') else fail ArgumentError, "Unrenderable values: #{vals}" end end def list? type.start_with?('list') end def enum? type.start_with?('enum') end def impl_type type = real_type.sub(/^list of (.+)s$/, '\1') type = 'string' if type == 'list' type = 'enum of modules' if type == 'module' case type when 'string', 'user name', 'integer', 'integer or max' 'string' when 'boolean' 'boolean' when 'timestamp' 'timestamp' when 'enum' 'enum' when 'enum of modules' 'enum_of_modules' else fail ArgumentError, "Cannot render #{real_type} to Ruby still" end end def ruby_type case real_type when 'string', 'user name', 'enum' 'String' when 'boolean' 'true, false' when 'integer' 'Integer' when 'integer or max' 'Integer, "max"' when 'list', 'list of user names', 'list of enums' 'Array' when 'list of integers' 'Array' when 'list of timestamps' 'Array