Merge branch 'restore-lab2' into lab2

lab2
Artem-Darius Weber 4 weeks ago
commit c222d7bdd0

@ -6,46 +6,63 @@
```mermaid
classDiagram
class Contact {
- phone : String?
- telegram : String?
- email : String?
+ Contact(phone : String?, telegram : String?, email : String?)
+ valid_phone_number() : Boolean
+ valid_telegram() : Boolean
+ valid_email() : Boolean
+ present() : Boolean
+ info() : String
- validate_contacts()
+ new_from_info(info_string : String) : Contact
}
class Person {
+id: String
+git: String
-phone: String
-telegram: String
-email: String
+initialize(args: Hash)
+set_contacts(phone: String, telegram: String, email: String)
+git_present(): Boolean
+contact_present(): Boolean
+contact_info(): String
+valid_phone_number?(phone: String): Boolean
+valid_name?(name: String): Boolean
+valid_telegram?(telegram: String): Boolean
+valid_email?(email: String): Boolean
+valid_git?(git: String): Boolean
- id : String
- git : String
- contact : Contact
+ Person(id : String, git : String, contact : Contact)
+ git_present() : Boolean
+ contact_present() : Boolean
+ contact_info() : String
+ valid_git(git : String) : Boolean
+ valid_id(id : String) : Boolean
- validate_person()
}
class Student {
+surname: String
+name: String
+patronymic: String
+initialize(args: Hash)
+from_string(student_string: String): Student
+read_from_txt(file_path: String): List~Student~
+write_to_txt(file_path: String, students: List~Student~)
+surname_and_initials(): String
+to_s(): String
+get_info(): String
class StudentRepository {
+ read_from_txt(file_path : String) : List~Student~
+ write_to_txt(file_path : String, students : List~Student~)
}
class StudentShort {
+surname_initials: String
+contact: String
+initialize(student: Student)
+from_string(id: String, info_string: String): StudentShort
- surname_initials : String
- contact : String
+ StudentShort(id : String, git : String, surname_initials : String, contact : Contact)
+ from_student(student : Student) : StudentShort
+ from_string(id : String, info_string : String) : StudentShort
+ to_s() : String
}
class Student {
- surname : String
- name : String
- patronymic : String
+ Student(id : String, git : String, contact : Contact, surname : String, name : String, patronymic : String)
+ from_string(student_string : String) : Student
+ surname_and_initials() : String
+ to_s() : String
+ get_info() : String
- validate_student()
}
Contact <|-- Person
Person <|-- Student
Person <|-- StudentShort
Student <.. StudentRepository
```
где + - public, - - private

@ -0,0 +1,67 @@
class Contact
attr_reader :phone, :telegram, :email
def initialize(phone: nil, telegram: nil, email: nil)
@phone = phone
@telegram = telegram
@email = email
validate_contacts
end
def valid_phone_number?
return true if @phone.nil?
/\A\+?[0-9]{10,15}\z/.match?(@phone)
end
def valid_telegram?
return true if @telegram.nil?
/\A@[A-Za-z0-9_]{5,32}\z/.match?(@telegram)
end
def valid_email?
return true if @email.nil?
/\A[^@\s]+@[^@\s]+\.[^@\s]+\z/.match?(@email)
end
def present?
@phone || @telegram || @email
end
def info
return "Phone: #{@phone}" if @phone
return "Telegram: #{@telegram}" if @telegram
return "Email: #{@email}" if @email
'No contact available'
end
private
def validate_contacts
if !present?
raise ArgumentError, 'At least one contact (phone, telegram, or email) is required'
end
raise ArgumentError, "Invalid phone number format: #{@phone}" if @phone && !valid_phone_number?
raise ArgumentError, "Invalid telegram format: #{@telegram}" if @telegram && !valid_telegram?
raise ArgumentError, "Invalid email format: #{@email}" if @email && !valid_email?
end
def self.new_from_info(info_string)
info_string = info_string.sub(/^Contact: /, '').strip
case info_string
when /\APhone: (.+)\z/i
new(phone: Regexp.last_match(1).strip)
when /\ATelegram: (.+)\z/i
new(telegram: Regexp.last_match(1).strip)
when /\AEmail: (.+)\z/i
new(email: Regexp.last_match(1).strip)
else
raise ArgumentError, "Invalid contact info format: #{info_string}"
end
end
end

@ -1,65 +1,61 @@
require_relative 'contact'
require_relative 'person'
require_relative 'student'
require_relative 'student_short'
require_relative 'student_repository'
def test_classes
contact = Contact.new(phone: '+798912465', telegram: '@example_user', email: 'test@example.com')
puts "Contact Info: #{contact.info}"
person = Person.new(id: '1', git: 'https://github.com/example', contact: contact)
puts "Person Contact Info: #{person.contact_info}"
student = Student.new(
id: '2',
git: 'https://github.com/student_example',
contact: contact,
surname: 'Иванов',
name: 'Иван',
patronymic: 'Иванович'
)
puts "Student Full Info: #{student.to_s}"
puts "Student Initials: #{student.surname_and_initials}"
puts "Student Contact Info: #{student.contact_info}"
student_short = StudentShort.from_student(student)
puts "Student Short Info: #{student_short.to_s}"
file_path = 'students.txt'
students = StudentRepository.read_from_txt(file_path)
puts "Read Students from File:"
students.each { |s| puts s.to_s }
output_path = 'output_students.txt'
StudentRepository.write_to_txt(output_path, students)
puts "Students written to file: #{output_path}"
end
begin
student_string = "Норакет Норакет Норакет | ID: 2 | Phone: +1234567890 | Telegram: @noracat | Email: nora@example.com | Git: https://github.com/nora"
student_from_string = Student.from_string(student_string)
puts "Создан объект из строки:\n#{student_from_string}"
student1 = Student.new(
surname: 'Алексеевич',
name: 'Артем-Дариус',
patronymic: 'Вебер',
id: 1,
git: 'https://github.com/space-creator',
# Передаём контакты сразу, чтобы не было ошибок при инициализации
phone: '+79891242223'
)
# Можно обновить контакты при необходимости
student1.set_contacts(
phone: '+79891242223',
telegram: '@alstroemeria22',
email: 'no-replay@djft.ru'
)
puts student1.get_info
puts "Surname and Initials: #{student1.surname_and_initials}"
puts "Git Info: #{student1.git_info}"
puts "Contact Info: #{student1.contact_info}"
# Исправляем создание student2: добавляем git и контакт
student2 = Student.new(
surname: 'Норакет',
name: 'Норакет',
patronymic: 'Фамилия',
git: 'https://github.com/noraket',
phone: '+70000000001'
)
def test_parsing
student_string = 'Иванов Иван Иванович | ID: 3 | Phone: +79876543210 | Telegram: @ivan_user | Email: ivan@example.com | Git: https://github.com/ivanov'
student = Student.from_string(student_string)
puts "Parsed Student: #{student.to_s}"
# Можно обновить контакты, если нужно другой номер
student2.set_contacts(
phone: '+70000000000'
)
short_string = 'Иванов И.И., Git: https://github.com/ivanov, Contact: Phone: +79876543210'
student_short = StudentShort.from_string('4', short_string)
puts "Parsed StudentShort: #{student_short.to_s}"
end
puts student1
puts '-' * 40
puts student2
student_short_from_student = StudentShort.new(student1)
puts "StudentShort from Student object:"
puts "ID: #{student_short_from_student.id}"
puts "Surname and Initials: #{student_short_from_student.surname_initials}"
puts "Git: #{student_short_from_student.git}"
puts "Contact: #{student_short_from_student.contact}"
if __FILE__ == $0
puts "=== Testing Classes ==="
test_classes
student_short_from_string = StudentShort.from_string(5, 'Skye A.A., Git: https://github.com/skye, Contact: Phone: +4923467890')
puts "StudentShort from string:"
puts "ID: #{student_short_from_string.id}"
puts "Surname and Initials: #{student_short_from_string.surname_initials}"
puts "Git: #{student_short_from_string.git}"
puts "Contact: #{student_short_from_string.contact}"
rescue ArgumentError => e
puts "Ошибка: #{e.message}"
puts "\n=== Testing Parsing ==="
test_parsing
end

@ -1,72 +1,42 @@
class Person
attr_accessor :id, :git, :name
def initialize(args = {})
@id = args[:id]
@git = args[:git]
@name = args[:name]
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
require_relative 'contact'
def set_contacts(phone: nil, telegram: nil, email: nil)
@phone = phone
raise ArgumentError, "Некорректный формат номера телефона: #{@phone}" if @phone && !self.class.valid_phone_number?(@phone)
@telegram = telegram
raise ArgumentError, "Некорректный формат Telegram: #{@telegram}" if @telegram && !self.class.valid_telegram?(@telegram)
@email = email
raise ArgumentError, "Некорректный формат email: #{@email}" if @email && !self.class.valid_email?(@email)
class Person
attr_accessor :id, :git
attr_reader :contact
raise ArgumentError, "Требуется хотя бы один контакт (телефон, телеграм или email)" unless contact_present?
end
def initialize(id:, git:, contact: Contact.new)
@id = id
@git = git
@contact = contact
def self.valid_phone_number?(phone)
phone.match?(/\A\+?[0-9]{10,15}\z/)
validate_person
end
def self.valid_name?(name)
name.match?(/\A[А-Яа-яЁёA-Za-z\-]+\z/)
def git_present?
!@git.nil? && !@git.empty?
end
def self.valid_telegram?(telegram)
telegram.nil? || telegram.match?(/\A@[A-Za-z0-9_]{5,32}\z/)
def contact_present?
@contact.present?
end
def self.valid_email?(email)
email.nil? || email.match?(/\A[^@\s]+@[^@\s]+\.[^@\s]+\z/)
def contact_info
@contact.info
end
def self.valid_git?(git)
git.nil? || git.match?(/\Ahttps:\/\/github\.com\/[A-Za-z0-9_\-]+\z/)
/\Ahttps:\/\/github\.com\/[A-Za-z0-9_\-]+\z/.match?(git)
end
def git_present?
!@git.nil? && !@git.empty?
end
def contact_present?
!(@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
'Контактная информация отсутствует'
def self.valid_id?(id)
id.is_a?(String) && !id.strip.empty?
end
private
attr_reader :phone, :telegram, :email
attr_writer :phone, :telegram, :email
def validate_person
raise ArgumentError, 'ID is required and must be a non-empty string' unless self.class.valid_id?(@id)
raise ArgumentError, 'Git link is required' unless git_present?
raise ArgumentError, 'Invalid Git link format' unless self.class.valid_git?(@git)
end
end

@ -1,132 +1,81 @@
require_relative 'person'
require_relative 'contact'
class Student < Person
attr_accessor :surname, :name, :patronymic
def initialize(args = {})
super(args)
NAME_REGEX = /\A[А-Яа-яЁёA-Za-z\-]+\z/
@surname = args.fetch(:surname) { raise ArgumentError, "Surname is required" }
raise ArgumentError, "Invalid surname format: #{@surname}" unless self.class.valid_name?(@surname)
def initialize(id:, git:, contact:, surname:, name:, patronymic:)
super(id: id, git: git, contact: contact)
@surname = surname
@name = name
@patronymic = patronymic
@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]
)
validate_student
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
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'
raise ArgumentError, 'Invalid student string format' if parts.size < 6
name_parts = parts[0].split(' ')
raise ArgumentError, 'Invalid name format' if name_parts.size != 3
surname, name, patronymic = name_parts
id = parts[1].split(': ').last
phone = parts[2].split(': ').last
telegram = parts[3].split(': ').last
email = parts[4].split(': ').last
git = parts[5].split(': ').last
contact = Contact.new(phone: phone, telegram: telegram, email: email)
new(
id: id,
git: git,
contact: contact,
surname: surname,
name: name,
patronymic: patronymic,
id: id,
phone: phone,
telegram: telegram,
email: email,
git: git
patronymic: patronymic
)
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?
def surname_and_initials
"#{@surname} #{name_initial(@name)}.#{patronymic_initial(@patronymic)}."
end
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 to_s
"#{@surname} #{@name} #{@patronymic} | ID: #{@id} | " \
"Phone: #{@contact.phone || 'N/A'} | Telegram: #{@contact.telegram || 'N/A'} | " \
"Email: #{@contact.email || 'N/A'} | Git: #{@git}"
end
students
def get_info
"#{surname_and_initials}, Git: #{@git}, Contact: #{contact_info}"
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) }
private
File.open(file_path, 'w') do |file|
students.each do |student|
file.puts student.to_s
end
end
def validate_student
raise ArgumentError, 'Surname is required' if @surname.nil? || @surname.strip.empty?
raise ArgumentError, 'Name is required' if @name.nil? || @name.strip.empty?
raise ArgumentError, 'Patronymic is required' if @patronymic.nil? || @patronymic.strip.empty?
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}"
raise ArgumentError, "Invalid surname format: #{@surname}" unless valid_name?(@surname)
raise ArgumentError, "Invalid name format: #{@name}" unless valid_name?(@name)
raise ArgumentError, "Invalid patronymic format: #{@patronymic}" unless valid_name?(@patronymic)
end
def surname_and_initials
"#{@surname} #{name[0]}.#{patronymic[0]}."
def valid_name?(name)
NAME_REGEX.match?(name)
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'}"
def name_initial(name)
name[0].upcase
end
def get_info
"#{surname_and_initials}, Git: #{git_info}, Contact: #{contact_info}"
def patronymic_initial(patronymic)
patronymic[0].upcase
end
end

@ -0,0 +1,39 @@
require_relative 'student'
class StudentRepository
def self.read_from_txt(file_path)
raise IOError, "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 = Student.from_string(line)
students << student
rescue ArgumentError => e
puts "Error processing line: '#{line}'. Reason: #{e.message}"
end
end
students
end
def self.write_to_txt(file_path, students)
unless students.is_a?(Array) && students.all? { |s| s.is_a?(Student) }
raise ArgumentError, 'Expected an array of Student objects'
end
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
end

@ -3,84 +3,39 @@ require_relative 'person'
class StudentShort < Person
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 initialize(id:, git:, surname_initials:, contact:)
super(id: id, git: git, contact: contact)
@surname_initials = surname_initials
end
@contact = student.contact_info
raise ArgumentError, "Контактная информация не может быть пустой" if @contact.nil? || @contact.strip.empty?
def self.from_student(student)
new(
id: student.id,
git: student.git,
surname_initials: student.surname_and_initials,
contact: student.contact_info
)
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
parts = info_string.split(',').map(&:strip)
raise ArgumentError, 'Invalid info string format' if parts.size < 3
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
@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 уже вызывается в инициализаторе, а тут мы лишь готовим данные.
git = parts[1].split(': ').last.strip
contact_string = parts[2].split(': ', 2).last.strip
contact = Contact.new_from_info(contact_string)
new(
id: id,
git: git,
surname_initials: surname_initials,
contact: contact.info
)
end
def valid_surname_initials?(string)
# Пример простой проверки: фамилия и инициалы могут содержать буквы и точки.
# Формат: "Иванов И.И." или "Petrov P.P."
# Условно: первая часть - фамилия, затем инициалы через пробел.
# Можно настроить по необходимости.
string.match?(/\A[А-Яа-яЁёA-Za-z]+(\s+[А-Яа-яЁёA-Za-z]\.[А-Яа-яЁёA-Za-z]\.)?\z/)
def to_s
"#{@surname_initials}, Git: #{@git}, Contact: #{@contact}"
end
end

@ -0,0 +1,3 @@
Иванов Иван Иванович | ID: 1 | Phone: +12345678901 | Telegram: @ivanov_user | Email: ivanov@example.com | Git: https://github.com/ivanov
Петров Петр Петрович | ID: 2 | Phone: +98765432101 | Telegram: @petrov_user | Email: petrov@example.com | Git: https://github.com/petrov
Сидоров Сидор Сидорович | ID: 3 | Phone: +56789012345 | Telegram: @sidorov_user | Email: sidorov@example.com | Git: https://github.com/sidorov
Loading…
Cancel
Save