From a9b27d76c5d44bf0b3e8651b61f9a6b46f72f774 Mon Sep 17 00:00:00 2001 From: Artem Darius Weber Date: Sat, 7 Dec 2024 14:26:42 +0300 Subject: [PATCH] feat: enhance Student and Person classes with contact information validation --- lab2/main.rb | 12 ++- lab2/person.rb | 30 ++++-- lab2/student.rb | 208 +++++++++++++++++++++++++----------------- lab2/student_short.rb | 105 ++++++++++++++++----- 4 files changed, 233 insertions(+), 122 deletions(-) diff --git a/lab2/main.rb b/lab2/main.rb index e714667..c662531 100644 --- a/lab2/main.rb +++ b/lab2/main.rb @@ -12,9 +12,12 @@ begin name: 'Артем-Дариус', patronymic: 'Вебер', id: 1, - git: 'https://github.com/space-creator' + git: 'https://github.com/space-creator', + # Передаём контакты сразу, чтобы не было ошибок при инициализации + phone: '+79891242223' ) + # Можно обновить контакты при необходимости student1.set_contacts( phone: '+79891242223', telegram: '@alstroemeria22', @@ -26,12 +29,16 @@ begin puts "Git Info: #{student1.git_info}" puts "Contact Info: #{student1.contact_info}" + # Исправляем создание student2: добавляем git и контакт student2 = Student.new( surname: 'Норакет', name: 'Норакет', - patronymic: 'Фамилия' + patronymic: 'Фамилия', + git: 'https://github.com/noraket', + phone: '+70000000001' ) + # Можно обновить контакты, если нужно другой номер student2.set_contacts( phone: '+70000000000' ) @@ -40,7 +47,6 @@ begin puts '-' * 40 puts student2 - student_short_from_student = StudentShort.new(student1) puts "StudentShort from Student object:" puts "ID: #{student_short_from_student.id}" diff --git a/lab2/person.rb b/lab2/person.rb index 7fc8dad..b3eab75 100644 --- a/lab2/person.rb +++ b/lab2/person.rb @@ -1,23 +1,31 @@ class Person - attr_accessor :id, :git + attr_accessor :id, :git, :name def initialize(args = {}) - @id = args[:id] || nil + @id = args[:id] @git = args[:git] + @name = args[:name] - raise ArgumentError, "Git link is required" unless git_present? - raise ArgumentError, "At least one contact (phone, telegram, or email) is required" unless contact_present? + raise ArgumentError, "ID должен быть числом" if @id && !@id.is_a?(Integer) + + raise ArgumentError, "Требуется ссылка на GitHub" unless git_present? + raise ArgumentError, "Некорректная ссылка на GitHub: #{@git}" unless self.class.valid_git?(@git) + + raise ArgumentError, "Требуется имя" if @name.nil? || @name.strip.empty? + raise ArgumentError, "Некорректный формат имени: #{@name}" unless self.class.valid_name?(@name) end def set_contacts(phone: nil, telegram: nil, email: nil) @phone = phone - raise ArgumentError, "Invalid phone number format: #{@phone}" if @phone && !self.class.valid_phone_number?(@phone) + raise ArgumentError, "Некорректный формат номера телефона: #{@phone}" if @phone && !self.class.valid_phone_number?(@phone) @telegram = telegram - raise ArgumentError, "Invalid telegram format: #{@telegram}" if @telegram && !self.class.valid_telegram?(@telegram) + raise ArgumentError, "Некорректный формат Telegram: #{@telegram}" if @telegram && !self.class.valid_telegram?(@telegram) @email = email - raise ArgumentError, "Invalid email format: #{@email}" if @email && !self.class.valid_email?(@email) + raise ArgumentError, "Некорректный формат email: #{@email}" if @email && !self.class.valid_email?(@email) + + raise ArgumentError, "Требуется хотя бы один контакт (телефон, телеграм или email)" unless contact_present? end def self.valid_phone_number?(phone) @@ -45,18 +53,20 @@ class Person end def contact_present? - !(@phone.nil? || @phone.empty?) || !(@telegram.nil? || @telegram.empty?) || !(@email.nil? || @email.empty?) + !(@phone.nil? || @phone.empty?) || + !(@telegram.nil? || @telegram.empty?) || + !(@email.nil? || @email.empty?) end def contact_info return "Phone: #{@phone}" if @phone return "Telegram: #{@telegram}" if @telegram return "Email: #{@email}" if @email - 'No contact available' + 'Контактная информация отсутствует' end private attr_reader :phone, :telegram, :email attr_writer :phone, :telegram, :email -end \ No newline at end of file +end diff --git a/lab2/student.rb b/lab2/student.rb index 57d26a7..2f725e6 100644 --- a/lab2/student.rb +++ b/lab2/student.rb @@ -1,92 +1,132 @@ require_relative 'person' class Student < Person - attr_accessor :surname, :name, :patronymic - - def initialize(args = {}) - super(args) - @surname = args.fetch(:surname) - raise ArgumentError, "Invalid surname format: #{@surname}" unless self.class.valid_name?(@surname) - - @name = args.fetch(:name) - raise ArgumentError, "Invalid name format: #{@name}" unless self.class.valid_name?(@name) - - @patronymic = args.fetch(:patronymic) - raise ArgumentError, "Invalid patronymic format: #{@patronymic}" unless self.class.valid_name?(@patronymic) - - set_contacts( - phone: args[:phone], - telegram: args[:telegram], - email: args[:email] - ) - end - - def self.from_string(student_string) - parts = student_string.split('|').map(&:strip) - surname, name, patronymic = parts[0].split(' ') - id = parts[1].split(': ').last.to_i - phone = parts[2].split(': ').last - telegram = parts[3].split(': ').last - email = parts[4].split(': ').last - git = parts[5].split(': ').last - - new( - surname: surname, - name: name, - patronymic: patronymic, - id: id, - phone: phone, - telegram: telegram, - email: email, - git: git - ) - end + attr_accessor :surname, :name, :patronymic - def self.read_from_txt(file_path) - raise IOError, "File path is invalid or file does not exist: #{file_path}" unless File.exist?(file_path) - - students = [] - - File.foreach(file_path) do |line| - line.strip! - next if line.empty? - - begin - student = from_string(line) - students << student - rescue ArgumentError => e - puts "Error processing line: '#{line}'. Reason: #{e.message}" - end - end - - students - end + def initialize(args = {}) + super(args) - def self.write_to_txt(file_path, students) - raise ArgumentError, "Expected an array of Student objects" unless students.is_a?(Array) && students.all? { |s| s.is_a?(Student) } - - File.open(file_path, 'w') do |file| - students.each do |student| - file.puts student.to_s - end - end - - puts "Data successfully written to #{file_path}" - rescue IOError => e - puts "An error occurred while writing to the file: #{e.message}" - end - - def surname_and_initials - "#{@surname} #{name[0]}.#{patronymic[0]}." + @surname = args.fetch(:surname) { raise ArgumentError, "Surname is required" } + raise ArgumentError, "Invalid surname format: #{@surname}" unless self.class.valid_name?(@surname) + + @name = args.fetch(:name) { raise ArgumentError, "Name is required" } + raise ArgumentError, "Invalid name format: #{@name}" unless self.class.valid_name?(@name) + + @patronymic = args.fetch(:patronymic) { raise ArgumentError, "Patronymic is required" } + raise ArgumentError, "Invalid patronymic format: #{@patronymic}" unless self.class.valid_name?(@patronymic) + + set_contacts( + phone: args[:phone], + telegram: args[:telegram], + email: args[:email] + ) + end + + def self.from_string(student_string) + raise ArgumentError, "Input string cannot be nil or empty" if student_string.nil? || student_string.strip.empty? + + parts = student_string.split('|').map(&:strip) + # Ожидается формат: "Фамилия Имя Отчество | ID: X | Phone: ... | Telegram: ... | Email: ... | Git: ..." + # Проверяем, что частей как минимум 6: ФИО, ID, Phone, Telegram, Email, Git + unless parts.size == 6 + raise ArgumentError, "Expected 6 parts separated by '|', got #{parts.size} in '#{student_string}'" end - - def to_s - "#{@surname} #{@name} #{@patronymic} | ID: #{@id || 'N/A'} | " \ - "Phone: #{@phone || 'N/A'} | Telegram: #{@telegram || 'N/A'} | " \ - "Email: #{@email || 'N/A'} | Git: #{@git || 'N/A'}" + + fio_part = parts[0] + raise ArgumentError, "FIO part is empty" if fio_part.nil? || fio_part.strip.empty? + + fio_elements = fio_part.split(' ') + raise ArgumentError, "Expected Surname Name Patronymic in '#{fio_part}'" unless fio_elements.size == 3 + surname, name, patronymic = fio_elements + + id_part = parts[1] + raise ArgumentError, "ID part is empty or invalid" if id_part.nil? || !id_part.include?('ID:') + id_str = id_part.split(': ').last + raise ArgumentError, "ID value is missing in '#{id_part}'" if id_str.nil? || id_str.strip.empty? + id = id_str.to_i + raise ArgumentError, "ID must be a valid integer: '#{id_str}'" if id == 0 && id_str != "0" + + phone_part = parts[2] + raise ArgumentError, "Phone part is invalid or missing" if phone_part.nil? || !phone_part.include?('Phone:') + phone = phone_part.split(': ').last + phone = nil if phone == 'N/A' + + telegram_part = parts[3] + raise ArgumentError, "Telegram part is invalid or missing" if telegram_part.nil? || !telegram_part.include?('Telegram:') + telegram = telegram_part.split(': ').last + telegram = nil if telegram == 'N/A' + + email_part = parts[4] + raise ArgumentError, "Email part is invalid or missing" if email_part.nil? || !email_part.include?('Email:') + email = email_part.split(': ').last + email = nil if email == 'N/A' + + git_part = parts[5] + raise ArgumentError, "Git part is invalid or missing" if git_part.nil? || !git_part.include?('Git:') + git = git_part.split(': ').last + git = nil if git == 'N/A' + + new( + surname: surname, + name: name, + patronymic: patronymic, + id: id, + phone: phone, + telegram: telegram, + email: email, + git: git + ) + end + + def self.read_from_txt(file_path) + raise IOError, "File path is invalid or file does not exist: #{file_path}" unless File.exist?(file_path) + + students = [] + + File.foreach(file_path) do |line| + line.strip! + next if line.empty? + + begin + student = from_string(line) + students << student + rescue ArgumentError => e + puts "Error processing line: '#{line}'. Reason: #{e.message}" + rescue StandardError => e + puts "An unexpected error occurred while processing line: '#{line}'. Reason: #{e.message}" + end end - - def get_info - "#{surname_and_initials}, Git: #{git_info}, Contact: #{contact_info}" + + students + end + + def self.write_to_txt(file_path, students) + raise ArgumentError, "Expected an array of Student objects" unless students.is_a?(Array) && students.all? { |s| s.is_a?(Student) } + + File.open(file_path, 'w') do |file| + students.each do |student| + file.puts student.to_s + end end -end \ No newline at end of file + + puts "Data successfully written to #{file_path}" + rescue IOError => e + puts "An error occurred while writing to the file: #{e.message}" + rescue StandardError => e + puts "An unexpected error occurred while writing to the file: #{e.message}" + end + + def surname_and_initials + "#{@surname} #{name[0]}.#{patronymic[0]}." + end + + def to_s + "#{@surname} #{@name} #{@patronymic} | ID: #{@id || 'N/A'} | " \ + "Phone: #{@phone || 'N/A'} | Telegram: #{@telegram || 'N/A'} | " \ + "Email: #{@email || 'N/A'} | Git: #{@git || 'N/A'}" + end + + def get_info + "#{surname_and_initials}, Git: #{git_info}, Contact: #{contact_info}" + end +end diff --git a/lab2/student_short.rb b/lab2/student_short.rb index 1bff260..327ef3d 100644 --- a/lab2/student_short.rb +++ b/lab2/student_short.rb @@ -1,31 +1,86 @@ require_relative 'person' class StudentShort < Person - attr_reader :surname_initials, :contact - - def initialize(student) - super(id: student.id, git: student.git_info) - @surname_initials = student.surname_and_initials - @contact = student.contact_info + attr_reader :surname_initials, :contact + + def initialize(student) + # Проверяем наличие необходимых методов и данных у student + raise ArgumentError, "Объект student не имеет метод id" unless student.respond_to?(:id) + raise ArgumentError, "Объект student не имеет метод git_info" unless student.respond_to?(:git_info) + raise ArgumentError, "Объект student не имеет метод surname_and_initials" unless student.respond_to?(:surname_and_initials) + raise ArgumentError, "Объект student не имеет метод contact_info" unless student.respond_to?(:contact_info) + + super(id: student.id, git: student.git_info) + + @surname_initials = student.surname_and_initials + raise ArgumentError, "Поле surname_initials не может быть пустым" if @surname_initials.nil? || @surname_initials.strip.empty? + # Дополнительная проверка на формат имен, если требуется + # Если формат @surname_initials может быть сложным (например, "Иванов И.И."), + # то можно добавить отдельную проверку формата: + unless valid_surname_initials?(@surname_initials) + raise ArgumentError, "Некорректный формат фамилии и инициалов: #{@surname_initials}" end - - def self.from_string(id, info_string) - parts = info_string.split(', ').map(&:strip) - surname_initials = parts[0] - git = parts[1].split(': ').last - contact = parts[2].split(': ').last - - new_instance = allocate - new_instance.send(:initialize_from_data, id, surname_initials, git, contact) - new_instance + + @contact = student.contact_info + raise ArgumentError, "Контактная информация не может быть пустой" if @contact.nil? || @contact.strip.empty? + end + + def self.from_string(id, info_string) + raise ArgumentError, "info_string не может быть пустой" if info_string.nil? || info_string.strip.empty? + + parts = info_string.split(', ').map(&:strip) + # Ожидается три части: Фамилия и инициалы, Git, Контакт + unless parts.size == 3 + raise ArgumentError, "Строка info_string должна содержать три части разделённые запятой: '#{info_string}'" end - - private - - def initialize_from_data(id, surname_initials, git, contact) - @id = id - @surname_initials = surname_initials - @git = git - @contact = contact + + surname_initials = parts[0] + raise ArgumentError, "Не удалось извлечь фамилию и инициалы" if surname_initials.nil? || surname_initials.empty? + + git_part = parts[1] + raise ArgumentError, "Не удалось извлечь Git-данные" if git_part.nil? || !git_part.include?(': ') + git = git_part.split(': ').last + raise ArgumentError, "Некорректная строка для Git: '#{git_part}'" if git.nil? || git.empty? + + contact_part = parts[2] + raise ArgumentError, "Не удалось извлечь контактные данные" if contact_part.nil? || !contact_part.include?(': ') + contact = contact_part.split(': ').last + raise ArgumentError, "Некорректная строка для контакта: '#{contact_part}'" if contact.nil? || contact.empty? + + new_instance = allocate + new_instance.send(:initialize_from_data, id, surname_initials, git, contact) + new_instance + end + + private + + def initialize_from_data(id, surname_initials, git, contact) + raise ArgumentError, "ID должен быть числом" if id && !id.is_a?(Integer) + @id = id + + @surname_initials = surname_initials + raise ArgumentError, "Поле surname_initials не может быть пустым" if @surname_initials.nil? || @surname_initials.strip.empty? + unless valid_surname_initials?(@surname_initials) + raise ArgumentError, "Некорректный формат фамилии и инициалов: #{@surname_initials}" end -end \ No newline at end of file + + @git = git + raise ArgumentError, "Git ссылка не может быть пустой" if @git.nil? || @git.strip.empty? + raise ArgumentError, "Некорректная ссылка на GitHub: #{@git}" unless self.class.valid_git?(@git) + + @contact = contact + raise ArgumentError, "Контактная информация не может быть пустой" if @contact.nil? || @contact.strip.empty? + + # Инициализация родительского класса для валидации полей + # Если необходимо, можно повторно вызвать super или же доверять валидации из Person при отдельном создании + # В данном случае super уже вызывается в инициализаторе, а тут мы лишь готовим данные. + end + + def valid_surname_initials?(string) + # Пример простой проверки: фамилия и инициалы могут содержать буквы и точки. + # Формат: "Иванов И.И." или "Petrov P.P." + # Условно: первая часть - фамилия, затем инициалы через пробел. + # Можно настроить по необходимости. + string.match?(/\A[А-Яа-яЁёA-Za-z]+(\s+[А-Яа-яЁёA-Za-z]\.[А-Яа-яЁёA-Za-z]\.)?\z/) + end +end