Skip to content

Commit

Permalink
happy linter
Browse files Browse the repository at this point in the history
  • Loading branch information
seorgiy committed Nov 18, 2024
1 parent f8b979e commit 3c431f5
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 87 deletions.
75 changes: 25 additions & 50 deletions rakelib/parse_schema.rake
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,21 @@ DRY_TYPES = %w[string integer float decimal array hash symbol boolean date date_

desc 'Parse types from public json, should be up to date https://github.com/ark0f/tg-bot-api'
task :parse_schema do
result = {}
document = Openapi3Parser.load_url('https://ark0f.github.io/tg-bot-api/openapi.json')

document.components.schemas.each do |schema|
# Input File is literally just a string for our purpose
next if schema[0] == 'InputFile'

result_schema = {}

schema[1].properties.each do |property|
required_keys = schema[1].required.to_a || []
property_name = property[0]
property_schema = property[1]

attribute_type =
case property_schema.type
when nil then property_name.capitalize.gsub(/_(\w)/) { Regexp.last_match(1).upcase }
when 'object' then property_schema.name
else property_schema.type
end

attribute = {
type: attribute_type
}
result = document.components.schemas.to_h do |type_name, schema|
type_schema = schema.properties.to_h do |property_name, property_schema|
attribute = { type: parse_initial_type(property_schema, property_name) }

unless property_schema.any_of.nil?
attribute[:type] = property_schema.any_of.map do |item|
property_schema.name || item.name || item.type
end.uniq
attribute[:type] = property_schema.any_of.map { |item| property_schema.name || item.name || item.type }.uniq
end

# Input File is literally just a string for our purpose
attribute[:type]&.delete('InputFile')
attribute[:type] = attribute[:type].join if attribute[:type]&.length == 1

attribute[:required] = true if required_keys.include?(property_name)
attribute[:required] = true if required_keys(schema).include?(property_name)

# getting required values from the description, no values in json 😔
required_value = property_schema.description&.match(/always “(.+)”|must be \*(.+)\*/)
Expand All @@ -59,41 +38,37 @@ task :parse_schema do

# array of arrays
if property_schema&.items&.type == 'array'
attribute[:items] = {
type: 'array',
items: property_schema&.items&.items&.name
}
attribute[:items] = { type: 'array', items: property_schema.items.items.name }
end

attribute[:default] = property_schema.default if property_schema.default
# previous line would have been enough, but had to check the description due to issue: https://github.com/kevindew/openapi3_parser/issues/28
attribute[:default] = false if property_schema.description&.include?('Defaults to *false*')
result_schema[property_name] = attribute
attribute = apply_default(attribute, property_schema)
[property_name, attribute]
end

result[schema[0]] = result_schema

# find empty classes
result[schema[0]][:type] = schema[1].any_of.map(&:name) if schema[1].properties.empty? && schema[1].any_of
type_schema[:type] = schema.any_of.map(&:name) if schema.properties.empty? && schema.any_of
[type_name, type_schema]
end

File.write "#{__dir__}/../utility/type_attributes.json", JSON.pretty_generate(result)
# Input File is literally just a string for our purpose
File.write "#{__dir__}/../utility/type_attributes.json", JSON.pretty_generate(result.except('InputFile'))
end

def add_module_types(type)
return 'Types::Float' if type == 'number'

DRY_TYPES.include?(type) ? "Types::#{type.capitalize}" : type
def required_keys(schema)
schema.required.to_a || []
end

def underscore(camel_cased_word)
camel_cased_word.to_s.gsub('::', '/')
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
.tr('-', '_')
.downcase
def apply_default(attribute, property_schema)
attribute[:default] = property_schema.default unless property_schema.default.nil?
# previous line would have been enough, but had to check the description due to issue: https://github.com/kevindew/openapi3_parser/issues/28
attribute[:default] = false if property_schema.description&.include?('Defaults to *false*')
attribute
end

def typecast_default(type, obj)
type == 'Types::String' ? "'#{obj}'" : obj
def parse_initial_type(property_schema, property_name)
case property_schema.type
when nil then property_name.capitalize.gsub(/_(\w)/) { Regexp.last_match(1).upcase }
when 'object' then property_schema.name
else property_schema.type
end
end
110 changes: 73 additions & 37 deletions rakelib/rebuild_types.rake
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,82 @@ task :rebuild_types do
types = JSON.parse(File.read("#{__dir__}/../utility/type_attributes.json"), symbolize_names: true)

types.each_pair do |name, attributes|
attributes.delete(:inherited_from)
if attributes[:type].instance_of?(Array)
attributes = attributes[:type].join(" |\n ")
template = ERB.new(File.read("#{__dir__}/../utility/empty_type.erb"))
else
template = ERB.new(File.read("#{__dir__}/../utility/type.erb"))
attributes.each_pair do |attr_name, properties|
attributes[attr_name][:type] = add_module_types(properties[:type]) unless properties[:type].is_a?(Array)
if properties[:type].is_a?(Array)
attributes[attr_name][:type] = properties[:type].map do |type|
add_module_types(type)
end.join(' | ')
end

if properties[:items].instance_of?(String)
attributes[attr_name][:type] += ".of(#{add_module_types(properties[:items])})"
elsif properties[:items] && properties[:items][:type] == 'array'
attributes[attr_name][:type] += ".of(Types::Array.of(#{properties[:items][:items]}))"
end
original_type = properties[:type]
if properties[:required_value]
attributes[attr_name][:type] += ".constrained(eql: #{typecast_default(original_type,
properties[:required_value])})"
end
if properties[:min_size] || properties[:max_size]
constrain = '.constrained(minmax)'
constrain = properties[:min_size] ? constrain.gsub('min', "min_size: #{properties[:min_size]}, ") : ''
constrain = constrain.gsub('max', "max_size: #{properties[:max_size]}") if properties[:max_size]
attributes[attr_name][:type] += constrain
end
unless properties[:default].nil?
attributes[attr_name][:type] += ".default(#{typecast_default(original_type,
properties[:default])})"
end
attributes[attr_name][:type] = 'Types::True' if attributes[attr_name][:type] == 'Types::Boolean.default(true)'
attributes[attr_name][:type] = attributes[attr_name][:type].gsub('Types::Boolean', 'Types::Bool')
next build_empty_type(name, attributes) if attributes[:type].instance_of?(Array)

attributes.except(:inherited_from).each_pair do |attr_name, properties|
attributes[attr_name][:type] = add_module_types(properties[:type]) unless properties[:type].is_a?(Array)
if properties[:type].is_a?(Array)
attributes[attr_name][:type] = properties[:type].map do |type|
add_module_types(type)
end.join(' | ')
end

if properties[:items].instance_of?(String)
attributes[attr_name][:type] += ".of(#{add_module_types(properties[:items])})"
elsif properties[:items] && properties[:items][:type] == 'array'
attributes[attr_name][:type] += ".of(Types::Array.of(#{properties[:items][:items]}))"
end
original_type = properties[:type]

attributes = apply_required(attributes, attr_name, properties, original_type)
attributes = apply_min_max(attributes, attr_name, properties)
attributes = apply_default(attributes, attr_name, properties, original_type)

attributes[attr_name][:type] = 'Types::True' if attributes[attr_name][:type] == 'Types::Boolean.default(true)'
attributes[attr_name][:type] = attributes[attr_name][:type].gsub('Types::Boolean', 'Types::Bool')
end

File.write "#{__dir__}/../lib/telegram/bot/types/#{underscore(name)}.rb",
template.result(binding).gsub(" \n", '')
ERB.new(File.read("#{__dir__}/../utility/type.erb")).result(binding).gsub(" \n", '')
end
end

def build_empty_type(name, attributes)
attributes = attributes[:type].join(" |\n ")
File.write "#{__dir__}/../lib/telegram/bot/types/#{underscore(name)}.rb",
ERB.new(File.read("#{__dir__}/../utility/empty_type.erb")).result(binding).gsub(" \n", '')
end

def apply_default(attributes, attr_name, properties, original_type)
return attributes if properties[:default].nil?

attributes[attr_name][:type] += ".default(#{typecast(original_type,
properties[:default])})"
attributes
end

def apply_required(attributes, attr_name, properties, original_type)
return attributes unless properties[:required_value]

attributes[attr_name][:type] += ".constrained(eql: #{typecast(original_type,
properties[:required_value])})"
attributes
end

def apply_min_max(attributes, attr_name, properties)
return attributes unless properties[:min_size] || properties[:max_size]

constrain = '.constrained(minmax)'
constrain = properties[:min_size] ? constrain.gsub('min', "min_size: #{properties[:min_size]}, ") : ''
constrain = constrain.gsub('max', "max_size: #{properties[:max_size]}") if properties[:max_size]
attributes[attr_name][:type] += constrain
attributes
end

def add_module_types(type)
return 'Types::Float' if type == 'number'

DRY_TYPES.include?(type) ? "Types::#{type.capitalize}" : type
end

def underscore(camel_cased_word)
camel_cased_word.to_s.gsub('::', '/')
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
.tr('-', '_')
.downcase
end

def typecast(type, obj)
type == 'Types::String' ? "'#{obj}'" : obj
end

0 comments on commit 3c431f5

Please sign in to comment.