Skip to content

Commit

Permalink
clean up & constrains min/max/eq fixed & empty classes building
Browse files Browse the repository at this point in the history
  • Loading branch information
seorgiy committed Nov 17, 2024
1 parent 458ffae commit f8b979e
Show file tree
Hide file tree
Showing 35 changed files with 625 additions and 3,697 deletions.
127 changes: 0 additions & 127 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,130 +15,3 @@ end
RSpec::Core::RakeTask.new(:spec)

task default: :spec

desc 'Dump type definitions from docs to YAML'
task :dump_type_attributes do
require_relative 'lib/telegram/bot'
require 'nokogiri'
require 'open-uri'
require 'yaml'

# Preload every type we have
Zeitwerk::Loader.eager_load_all
types = Telegram::Bot::Types::Base.descendants.map { |c| c.name.split('::').last }

# Fetch and parse docs
doc = Nokogiri::HTML(URI.open('https://core.telegram.org/bots/api').read)

next_type_element_names = %w[table h4]

result = types.to_h do |type|
# This is very hacky but working way to find table containing attributes for
# given type. Basic idea is to find heading with type and then iterate until
# we find table with attributes or next heading (because sometimes type
# doesn't have any attributes).
element = doc.at_xpath(%{//h4[text() = "#{type}"]})
loop do
element = element.next_element
break if next_type_element_names.include?(element.name)
end

attributes = element.xpath('.//tbody//tr').map do |el|
cells = el.children.select { |c| c.name == 'td' }
{
'name' => cells[0].text,
'type' => cells[1].text,
'required' => !cells[2].text.start_with?('Optional.'),
'required_value' =>
cells[2].text.match(/^.+, (?:must be (?<found_type>\w+)|always “(?<found_type>\w+)”)$/)&.[](:found_type)
}.compact
end

[type, attributes]
end

# Write everything to fixture file
File.write "#{__dir__}/spec/support/type_attributes.yml", result.to_yaml
end

DRY_TYPES = %w[string integer float decimal array hash symbol boolean date date_time time range].freeze
desc 'Rebuild types'
task :rebuild_types do
require 'json'
require 'erb'

types = JSON.parse(File.read("#{__dir__}/spec/support/openapi_type_attributes.json"), symbolize_names: true)

types.each_pair do |name, attributes|
template = ERB.new(File.read("#{__dir__}/spec/support/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
attributes[attr_name][:type] += ".of(#{add_module_types(properties[:items])})" if properties[:items]
if properties[:default]
attributes[attr_name][:type] += ".default(#{typecast_default(properties[:type],
properties[:default])})"
end
attributes[attr_name][:type] = 'Types::True' if attributes[attr_name][:type] == 'Types::Boolean.default(true)'
attributes[attr_name][:type] = 'Types::Bool' if attributes[attr_name][:type] == 'Types::Boolean'
end

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

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

document.components.schemas.each do |schema|
result_schema = {}
schema[1].properties.each do |property|
required_keys = schema[1].required.to_a || []

result_schema[property[0]] = {
type: property[1].type == 'object' ? property[1].name : property[1].type
}

unless property[1].any_of.nil?
result_schema[property[0]][:type] = property[1].any_of.map do |item|
property[1].name || item.name || item.type
end.uniq
end
if result_schema[property[0]][:type]&.length == 1
result_schema[property[0]][:type] =
result_schema[property[0]][:type].join
end
result_schema[property[0]][:required] = true if required_keys.include?(property[0])
result_schema[property[0]][:items] = property[1].items.type if property[1]&.items
result_schema[property[0]][:items] = property[1].items.name if property[1]&.items&.type == 'object'
result_schema[property[0]][:default] = property[1].default if property[1].default
end
result[schema[0]] = result_schema
end

File.write "#{__dir__}/spec/support/openapi_type_attributes.json", result.to_json
end

def add_module_types(type)
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_default(type, obj)
type == 'Types::String' ? "'#{obj}'" : obj
end
4 changes: 2 additions & 2 deletions lib/telegram/bot/types/bot_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ module Telegram
module Bot
module Types
class BotCommand < Base
attribute :command, Types::String
attribute :description, Types::String
attribute :command, Types::String.constrained(min_size: 1, max_size: 32)
attribute :description, Types::String.constrained(min_size: 1, max_size: 256)
end
end
end
Expand Down
5 changes: 4 additions & 1 deletion lib/telegram/bot/types/chat_boost_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
module Telegram
module Bot
module Types
ChatBoostSource = ( # rubocop:disable Naming/ConstantName
## Just for classes consistency
# rubocop:disable Naming/ConstantName
ChatBoostSource = (
ChatBoostSourcePremium |
ChatBoostSourceGiftCode |
ChatBoostSourceGiveaway
)
# rubocop:enable Naming/ConstantName
end
end
end
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/chat_full_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ChatFullInfo < Base
attribute? :business_location, BusinessLocation
attribute? :business_opening_hours, BusinessOpeningHours
attribute? :personal_chat, Chat
attribute? :available_reactions, Types::Array
attribute? :available_reactions, Types::Array.of(ReactionType)
attribute? :background_custom_emoji_id, Types::String
attribute? :profile_accent_color_id, Types::Integer
attribute? :profile_background_custom_emoji_id, Types::String
Expand Down
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/chat_location.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Bot
module Types
class ChatLocation < Base
attribute :location, Location
attribute :address, Types::String
attribute :address, Types::String.constrained(min_size: 1, max_size: 64)
end
end
end
Expand Down
8 changes: 4 additions & 4 deletions lib/telegram/bot/types/chat_member.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ module Types
## Just for classes consistency
# rubocop:disable Naming/ConstantName
ChatMember = (
ChatMemberOwner |
ChatMemberAdministrator |
ChatMemberBanned |
ChatMemberLeft |
ChatMemberMember |
ChatMemberOwner |
ChatMemberRestricted
ChatMemberRestricted |
ChatMemberLeft |
ChatMemberBanned
)
# rubocop:enable Naming/ConstantName
end
Expand Down
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/force_reply.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Bot
module Types
class ForceReply < Base
attribute :force_reply, Types::True
attribute? :input_field_placeholder, Types::String
attribute? :input_field_placeholder, Types::String.constrained(min_size: 1, max_size: 64)
attribute? :selective, Types::Bool
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/inline_keyboard_button.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class InlineKeyboardButton < Base
attribute? :switch_inline_query, Types::String
attribute? :switch_inline_query_current_chat, Types::String
attribute? :switch_inline_query_chosen_chat, SwitchInlineQueryChosenChat
attribute? :copy_text, CopyTextButton
attribute? :callback_game, CallbackGame
attribute? :pay, Types::Bool
attribute? :copy_text, CopyTextButton
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/inline_query_results_button.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Types
class InlineQueryResultsButton < Base
attribute :text, Types::String
attribute? :web_app, WebAppInfo
attribute? :start_parameter, Types::String
attribute? :start_parameter, Types::String.constrained(min_size: 1, max_size: 64)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/input_contact_message_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Telegram
module Bot
module Types
class InputContactMessageContent < InputMessageContent
class InputContactMessageContent < Base
attribute :phone_number, Types::String
attribute :first_name, Types::String
attribute? :last_name, Types::String
Expand Down
6 changes: 3 additions & 3 deletions lib/telegram/bot/types/input_invoice_message_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
module Telegram
module Bot
module Types
class InputInvoiceMessageContent < InputMessageContent
attribute :title, Types::String
attribute :description, Types::String
class InputInvoiceMessageContent < Base
attribute :title, Types::String.constrained(min_size: 1, max_size: 32)
attribute :description, Types::String.constrained(min_size: 1, max_size: 255)
attribute :payload, Types::String
attribute? :provider_token, Types::String
attribute :currency, Types::String
Expand Down
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/input_location_message_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Telegram
module Bot
module Types
class InputLocationMessageContent < InputMessageContent
class InputLocationMessageContent < Base
attribute :latitude, Types::Float
attribute :longitude, Types::Float
attribute? :horizontal_accuracy, Types::Float
Expand Down
12 changes: 10 additions & 2 deletions lib/telegram/bot/types/input_message_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@
module Telegram
module Bot
module Types
class InputMessageContent < Base
end
## Just for classes consistency
# rubocop:disable Naming/ConstantName
InputMessageContent = (
InputTextMessageContent |
InputLocationMessageContent |
InputVenueMessageContent |
InputContactMessageContent |
InputInvoiceMessageContent
)
# rubocop:enable Naming/ConstantName
end
end
end
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/input_poll_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Telegram
module Bot
module Types
class InputPollOption < Base
attribute :text, Types::String
attribute :text, Types::String.constrained(min_size: 1, max_size: 100)
attribute? :text_parse_mode, Types::String
attribute? :text_entities, Types::Array.of(MessageEntity)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/telegram/bot/types/input_text_message_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
module Telegram
module Bot
module Types
class InputTextMessageContent < InputMessageContent
attribute :message_text, Types::String
class InputTextMessageContent < Base
attribute :message_text, Types::String.constrained(min_size: 1, max_size: 4096)
attribute? :parse_mode, Types::String
attribute? :entities, Types::Array.of(MessageEntity)
attribute? :link_preview_options, LinkPreviewOptions
Expand Down
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/input_venue_message_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Telegram
module Bot
module Types
class InputVenueMessageContent < InputMessageContent
class InputVenueMessageContent < Base
attribute :latitude, Types::Float
attribute :longitude, Types::Float
attribute :title, Types::String
Expand Down
5 changes: 4 additions & 1 deletion lib/telegram/bot/types/maybe_inaccessible_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
module Telegram
module Bot
module Types
MaybeInaccessibleMessage = ( # rubocop:disable Naming/ConstantName
## Just for classes consistency
# rubocop:disable Naming/ConstantName
MaybeInaccessibleMessage = (
Message |
InaccessibleMessage
)
# rubocop:enable Naming/ConstantName
end
end
end
5 changes: 4 additions & 1 deletion lib/telegram/bot/types/message_origin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
module Telegram
module Bot
module Types
MessageOrigin = ( # rubocop:disable Naming/ConstantName
## Just for classes consistency
# rubocop:disable Naming/ConstantName
MessageOrigin = (
MessageOriginUser |
MessageOriginHiddenUser |
MessageOriginChat |
MessageOriginChannel
)
# rubocop:enable Naming/ConstantName
end
end
end
10 changes: 8 additions & 2 deletions lib/telegram/bot/types/paid_media.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
module Telegram
module Bot
module Types
class PaidMedia < Base
end
## Just for classes consistency
# rubocop:disable Naming/ConstantName
PaidMedia = (
PaidMediaPreview |
PaidMediaPhoto |
PaidMediaVideo
)
# rubocop:enable Naming/ConstantName
end
end
end
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/paid_media_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Bot
module Types
class PaidMediaInfo < Base
attribute :star_count, Types::Integer
attribute :paid_media, Types::Array
attribute :paid_media, Types::Array.of(PaidMedia)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/paid_media_photo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Telegram
module Bot
module Types
class PaidMediaPhoto < Base
attribute :type, Types::String.default('photo')
attribute :type, Types::String.constrained(eql: 'photo').default('photo')
attribute :photo, Types::Array.of(PhotoSize)
end
end
Expand Down
16 changes: 14 additions & 2 deletions lib/telegram/bot/types/passport_element_error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,20 @@
module Telegram
module Bot
module Types
class PassportElementError < Base
end
## Just for classes consistency
# rubocop:disable Naming/ConstantName
PassportElementError = (
PassportElementErrorDataField |
PassportElementErrorFrontSide |
PassportElementErrorReverseSide |
PassportElementErrorSelfie |
PassportElementErrorFile |
PassportElementErrorFiles |
PassportElementErrorTranslationFile |
PassportElementErrorTranslationFiles |
PassportElementErrorUnspecified
)
# rubocop:enable Naming/ConstantName
end
end
end
2 changes: 1 addition & 1 deletion lib/telegram/bot/types/poll.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Bot
module Types
class Poll < Base
attribute :id, Types::String
attribute :question, Types::String
attribute :question, Types::String.constrained(min_size: 1, max_size: 300)
attribute? :question_entities, Types::Array.of(MessageEntity)
attribute :options, Types::Array.of(PollOption)
attribute :total_voter_count, Types::Integer
Expand Down
Loading

0 comments on commit f8b979e

Please sign in to comment.