students_list_yaml 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4a1156ccf6aab89dc74f1cfb32eb6069bc558d4363692afbdb76588f505cc14c
4
+ data.tar.gz: 3e6af363bcceb9bcbfe4088011b29a26ffdc2356989b5c529df72fe18a073f31
5
+ SHA512:
6
+ metadata.gz: ec4a0460228be9775535087d6dda30f379c60575984726e3a50eca60d7053939c0fcc52d412cad0c0016916bb39cc3ab3027219cb19148a66ebe28bb661ec600
7
+ data.tar.gz: 85fef37b60095af40a2c35a3fa07fcdd600b2030ec4f2d6b505bfc31f28b8528cdd381956fb92412bc15c17967251e51aefde89ebc2ae10a9954184d5e97b32d
data/lib/data_list.rb ADDED
@@ -0,0 +1,58 @@
1
+ class DataList
2
+ def initialize(elements)
3
+ @elements = elements
4
+ @selected = []
5
+ end
6
+
7
+ def elements=(new_elements)
8
+ @elements = new_elements.dup
9
+ clear_selected
10
+ end
11
+
12
+ def select(number)
13
+ validate_index(number)
14
+ @selected << number unless @selected.include?(number)
15
+ end
16
+
17
+ def get_selected
18
+ @selected.map { |index| @elements[index].id }.dup
19
+ end
20
+
21
+ def get_names
22
+ raise NotImplementedError, "Метод должен быть реализован в наследниках"
23
+ end
24
+
25
+ def get_data
26
+ data = []
27
+
28
+ @elements.each_with_index do |student, index|
29
+ row = [index + 1]
30
+ row.concat(student_info(student))
31
+ data << row
32
+ end
33
+
34
+ create_data_table(data)
35
+
36
+ end
37
+
38
+ def student_info student
39
+ raise NotImplementedError, "Метод должен быть реализован в наследниках"
40
+ end
41
+
42
+ def create_data_table data
43
+ raise NotImplementedError, "Метод должен быть реализован в наследниках"
44
+ end
45
+
46
+ def clear_selected
47
+ @selected.clear
48
+ end
49
+
50
+ protected
51
+
52
+ def validate_index(index)
53
+ if index < 0 || index >= @elements.length
54
+ raise IndexError, "Некорректный индекс: #{index}"
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,89 @@
1
+ require_relative 'data_list'
2
+ require_relative 'student_short'
3
+ require_relative 'data_table'
4
+
5
+ class DataListStudentShort < DataList
6
+ def get_names
7
+ ["№ по порядку", "ФИО", "Контакт", "Git"]
8
+ end
9
+
10
+ def student_info student
11
+ [student.last_name_initials, student.contact || "нет", student.git || "нет"]
12
+ end
13
+
14
+ def create_data_table data
15
+ DataTable.new(data)
16
+ end
17
+ end
18
+
19
+ '''
20
+ students = [
21
+ StudentShort.new(
22
+ id: 1,
23
+ last_name_initials: "Иванов И.И.",
24
+ contact: "phone - +79181112233",
25
+ git: "https://github.com/qwerty"
26
+ ),
27
+ StudentShort.new(
28
+ id: 2,
29
+ last_name_initials: "Сидоров С.С.",
30
+ contact: "email - allakk@mail.ru",
31
+ git: "https://github.com/asdfg"
32
+ ),
33
+ StudentShort.new(
34
+ id: 3,
35
+ last_name_initials: "Петров П.П.",
36
+ contact: "telegram - @popopo",
37
+ git: nil
38
+ )
39
+ ]
40
+
41
+ data_list = DataListStudentShort.new(students)
42
+
43
+ data_table = data_list.get_data
44
+ puts "Кол-во студентов: #{data_table.rows_count}"
45
+ puts "Кол-во столббцов: #{data_table.columns_count}"
46
+
47
+ header = data_list.get_names
48
+ puts header.join("\t\t")
49
+
50
+ (0...data_table.rows_count).each do |row|
51
+ row_data = (0...data_table.columns_count).map { |column| data_table.get_element(row, column) }
52
+ puts row_data.join(" | ")
53
+ end
54
+
55
+ puts "\nРабота c select:"
56
+ data_list.select(0)
57
+ data_list.select(2)
58
+
59
+ puts "Получим, кого выделили (id): #{data_list.get_selected}"
60
+ data_list.clear_selected
61
+ puts "Очистили: #{data_list.get_selected}"
62
+
63
+ puts "Контакт в строке 1: #{data_table.get_element(0, 2)}"
64
+ puts "Студент в строке 2: #{data_table.get_element(1, 1)}"
65
+ puts "Git в строке 3: #{data_table.get_element(2, 3)}"
66
+ '''
67
+
68
+ '''
69
+ #можно заменять через сеттер для 5 задания
70
+ students1 = [
71
+ StudentShort.new(id: 1, last_name_initials: "Иванов И.И.", contact: "phone1", git: "git1"),
72
+ StudentShort.new(id: 2, last_name_initials: "Петров П.П.", contact: "phone2", git: "git2")
73
+ ]
74
+
75
+ data_list = DataListStudentShort.new(students1)
76
+ data_list.select(0)
77
+ puts "Выделенные после первого набора: #{data_list.get_selected}"
78
+
79
+ students2 = [
80
+ StudentShort.new(id: 3, last_name_initials: "Сидоров С.С.", contact: "phone3", git: "git3"),
81
+ StudentShort.new(id: 4, last_name_initials: "Козлов К.К.", contact: "phone4", git: "git4"),
82
+ StudentShort.new(id: 5, last_name_initials: "Новиков Н.Н.", contact: "phone5", git: "git5")
83
+ ]
84
+
85
+ data_list.elements = students2
86
+ puts "Выделенные после замены массива: #{data_list.get_selected}"
87
+
88
+ puts "Заголовки столбцов: #{data_list.get_names}"
89
+ '''
data/lib/data_table.rb ADDED
@@ -0,0 +1,119 @@
1
+ class DataTable
2
+ def initialize(data)
3
+ validate_data(data)
4
+ @data = data.dup
5
+ end
6
+
7
+ def get_element(row, col)
8
+ validate_indices(row, col)
9
+ safe_return(@data[row][col])
10
+ end
11
+
12
+ def columns_count
13
+ return 0 if @data.empty?
14
+ @data.map{|element| element.length}.max
15
+ end
16
+
17
+ def rows_count
18
+ @data.length
19
+ end
20
+
21
+ private
22
+
23
+ def validate_data(data)
24
+ unless data.is_a?(Array) && data.all? { |row| row.is_a?(Array) }
25
+ raise ArgumentError, "Данные д/б двумерным массивом"
26
+ end
27
+ widths = data.map{|element| element.length}.uniq
28
+ return if widths.length <= 1
29
+ raise ArgumentError, "Все строки должны иметь одинаковое количество атрибутов"
30
+ end
31
+
32
+ def validate_indices(row, col)
33
+ if row < 0 || row >= rows_count
34
+ raise IndexError, " индекс строки: #{row}"
35
+ end
36
+ if col < 0 || col >= @data[row].length
37
+ raise IndexError, "Некорректный индекс столбца: #{col}"
38
+ end
39
+ end
40
+
41
+ def safe_return(value)
42
+ copy = deep_copy(value)
43
+ end
44
+
45
+ def deep_copy(obj)
46
+ case obj
47
+ when Array
48
+ obj.map { |v| deep_copy(v) }
49
+ when Hash
50
+ obj.transform_values { |v| deep_copy(v) }
51
+ when String
52
+ obj.dup
53
+ else
54
+ obj
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ '''
61
+ employee_data = [
62
+ [1, "Иван Иванов", 25, [50000,20], false],
63
+ [2, "Мария Петрова", 30, 60000, false],
64
+ [3, "Петр Сидоров", 35, 70000, true],
65
+ [4, "Анна Козлова", 28, 55000, true]
66
+ ]
67
+
68
+ employee_table = DataTable.new(employee_data)
69
+
70
+ puts "Таблица сотрудников:"
71
+ puts "Количество строк: #{employee_table.rows_count}"
72
+ puts "Количество столбцов: #{employee_table.columns_count}"
73
+
74
+ puts "\nКонкретный элемент до попытки записи: #{employee_table.get_element(0,3)}"
75
+ puts "table element id: #{employee_table.get_element(0,3).object_id}"
76
+
77
+ element= employee_table.get_element(0,3)<<4
78
+ puts "element: #{element}"
79
+ puts "element id: #{element.object_id}"
80
+
81
+ puts "Конкретный элемент после попытки записи: #{employee_table.get_element(0,3)}"
82
+
83
+
84
+ puts "\nДанные:"
85
+ (0...employee_table.rows_count).each do |row|
86
+ (0...employee_table.columns_count).each do |col|
87
+ print "#{employee_table.get_element(row, col)}\t"
88
+ end
89
+ puts
90
+ end
91
+
92
+ # с dup не добавляется, работает как надо.
93
+ # puts "Проверка на добавление в исходный массив, поменяется ли наш экземпляр?"
94
+ # employee_data << [1, "Иван Иванов", 25, [50000,20], true]
95
+ # puts "\nДанные:"
96
+ # (0...employee_table.rows_count).each do |row|
97
+ # (0...employee_table.columns_count).each do |col|
98
+ # print "#{employee_table.get_element(row, col)}\t"
99
+ # end
100
+ # puts
101
+ # end
102
+
103
+ puts "Полученный элемент нельзя редактировать:"
104
+ data = [[1, "test"], [2, "hello"]]
105
+ table = DataTable.new(data)
106
+ element = table.get_element(0, 1)
107
+ element << " modified"
108
+ puts "#{table.get_element(0, 1)}"
109
+
110
+ #не работает, так и д/б. нет доступа
111
+ # table.get_element(0, 1) = 1111
112
+ # puts "#{table.get_element(0, 1)}"
113
+
114
+ #Тоже не работает кек, так и должно быть. нет доступа
115
+ #puts "#{employee_table.@data}"
116
+ #puts "#{employee_table.data}"
117
+ '''
118
+
119
+
data/lib/module.rb ADDED
@@ -0,0 +1,14 @@
1
+ module ValidatedAttributes
2
+
3
+ def attr_validate_writer(attribute, field_name: nil, required: true, with:)
4
+ define_method("#{attribute}=") do |value|
5
+ if value.nil? && !required
6
+ instance_variable_set("@#{attribute}", value)
7
+ elsif value && self.class.send(with, value)
8
+ instance_variable_set("@#{attribute}", value)
9
+ else
10
+ raise ArgumentError, "Ошибка валидации #{attribute}."
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/student.rb ADDED
@@ -0,0 +1,124 @@
1
+ require_relative 'student_base.rb'
2
+ require_relative 'module.rb'
3
+
4
+ class Student < StudentBase
5
+ include Comparable
6
+ extend ValidatedAttributes
7
+ attr_reader :last_name, :first_name, :patronymic
8
+
9
+ NAME_REGEX = /\A[A-ZА-ЯЁ][a-zа-яё]+\z/
10
+ PHONE_REGEX = /^(\+7|8)?[\s\-\(]?(\d{3})[\s\-\)]?(\d{3})[\s\-]?(\d{2})[\s\-]?(\d{2})$/
11
+ TELEGRAM_REGEX = /^@[a-zA-Z0-9_]{5,32}$/
12
+ EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
13
+ GIT_REGEX = /^https:\/\/(github|gitlab)\.com\/[a-zA-Z0-9_-]+\/?$/
14
+
15
+
16
+ attr_validate_writer :last_name, field_name: "фамилии", required: true, with: :valid_name?
17
+ attr_validate_writer :first_name, field_name: "имени", required: true, with: :valid_name?
18
+ attr_validate_writer :patronymic, field_name: "отчества", required: false, with: :valid_name?
19
+ attr_validate_writer :git, field_name: "Гита", required: false, with: :valid_git?
20
+ attr_validate_writer :phone, field_name: "телефона", required: false, with: :valid_phone?
21
+ attr_validate_writer :telegram, field_name: "телеграмма", required: false, with: :valid_telegram?
22
+ attr_validate_writer :email, field_name: "почты", required: false, with: :valid_email?
23
+
24
+ def initialize(last_name:, first_name:, id: nil, patronymic: nil, phone: nil, telegram: nil, email: nil, git: nil)
25
+ raise ArgumentError, "Ошибка валидации гита" if !git.nil? && !self.class.valid_git?(git)
26
+ super(id: id, git: git)
27
+ self.last_name = last_name
28
+ self.first_name = first_name
29
+ self.patronymic = patronymic
30
+
31
+ set_contact_values(phone: phone, telegram: telegram, email: email)
32
+ end
33
+
34
+ def contact
35
+ contact_data = find_primary_contact
36
+ return nil unless contact_data
37
+
38
+ "#{contact_data[:type]} - #{contact_data[:value]}"
39
+ end
40
+
41
+ def contact=(hash)
42
+ raise ArgumentError, "Неверный формат (не Hash)." unless hash.is_a?(Hash)
43
+ raise ArgumentError, "Не введено 3 контакта." if hash.length != 3
44
+
45
+ valid_keys = [:phone, :telegram, :email]
46
+ invalid_keys = hash.keys - valid_keys
47
+
48
+ raise ArgumentError, "Неизвестный тип контакта: #{invalid_keys.first}." if !invalid_keys.empty?
49
+
50
+ set_contact_values(phone: hash[:phone], telegram: hash[:telegram], email: hash[:email])
51
+ end
52
+
53
+ def last_name_initials
54
+ initials = "#{first_name[0]}."
55
+ initials += " #{patronymic[0]}." if patronymic
56
+ "#{last_name} #{initials}"
57
+ end
58
+
59
+ def <=>(other)
60
+ return nil unless other.is_a?(Student)
61
+
62
+ [@last_name, @first_name, @patronymic || ''] <=>
63
+ [other.last_name, other.first_name, other.patronymic || '']
64
+ end
65
+
66
+ def to_s
67
+ result = "#{id}"
68
+ result += "\nФИО: #{last_name} #{first_name} #{patronymic || 'нет'} "
69
+ result += "\nТелефон: #{@phone}" if @phone && !@phone.empty?
70
+ result += "\nТелеграм: #{@telegram}" if @telegram && !@telegram.empty?
71
+ result += "\nПочта: #{@email}" if @email && !@email.empty?
72
+ result += "Git: #{git}" if has_git?
73
+ end
74
+
75
+ class << self
76
+ def valid_name?(name)
77
+ name.is_a?(String) && name.length >= 2 && name.match?(NAME_REGEX)
78
+ end
79
+
80
+ def valid_phone?(phone)
81
+ validate_field(phone) { |v| v.match?(PHONE_REGEX) }
82
+ end
83
+
84
+ def valid_telegram?(telegram)
85
+ validate_field(telegram) { |v| v.match?(TELEGRAM_REGEX) }
86
+ end
87
+
88
+ def valid_email?(email)
89
+ validate_field(email) { |v| v.match?(EMAIL_REGEX) }
90
+ end
91
+
92
+ def valid_git?(git)
93
+ validate_field(git) { |v| v.match?(GIT_REGEX) }
94
+ end
95
+
96
+ private
97
+
98
+ def validate_field(value)
99
+ return true if value.nil? || value.empty?
100
+ yield(value)
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def find_primary_contact
107
+ [
108
+ { type: 'telegram', value: @telegram },
109
+ { type: 'email', value: @email },
110
+ { type: 'phone', value: @phone }
111
+ ].find { |contact| contact_present?(contact[:value]) }
112
+ end
113
+
114
+ def contact_present?(value)
115
+ value && !value.empty?
116
+ end
117
+
118
+ def set_contact_values(phone:, telegram:, email:)
119
+ self.phone = phone
120
+ self.telegram = telegram
121
+ self.email = email
122
+ end
123
+
124
+ end
@@ -0,0 +1,36 @@
1
+ class StudentBase
2
+ attr_reader :id, :git
3
+
4
+ def initialize(id: nil, git: nil)
5
+ @id = id
6
+ @git = git
7
+ end
8
+
9
+ def to_s
10
+ raise NotImplementedError, "Метод to_s должен быть реализован в подклассе"
11
+ end
12
+
13
+ def contact
14
+ raise NotImplementedError, "Метод contact должен быть реализован в подклассе"
15
+ end
16
+
17
+ def last_name_initials
18
+ raise NotImplementedError, "Метод last_name_initials должен быть реализован в подклассе"
19
+ end
20
+
21
+ def has_contact?
22
+ !contact.nil?
23
+ end
24
+
25
+ def has_git?
26
+ !git.nil?
27
+ end
28
+
29
+ def short_info
30
+ info = "ID: #{id}, ФИО: #{last_name_initials}"
31
+ info += ", Контакт: #{contact}" if has_contact?
32
+ info += ", Git: #{git}" if has_git?
33
+ info
34
+ end
35
+
36
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'student.rb'
2
+ require_relative 'student_base.rb'
3
+
4
+ class StudentShort < StudentBase
5
+ attr_reader :last_name_initials, :contact
6
+
7
+ def initialize(id:, last_name_initials:, contact: nil, git: nil)
8
+ super(id: id, git: git)
9
+ @last_name_initials = last_name_initials
10
+ @contact = contact
11
+ end
12
+
13
+ def self.from_student(student)
14
+ raise ArgumentError, 'Указан не экземпляр класса Student' unless student.is_a?(Student)
15
+
16
+ new( id: student.id, last_name_initials: student.last_name_initials, contact: student.contact, git: student.git)
17
+ end
18
+
19
+ def to_s
20
+ <<~TEXT
21
+ ID: #{id}
22
+ ФИО: #{last_name_initials}
23
+ Контакт: #{contact}
24
+ Гит: #{git}
25
+ TEXT
26
+ end
27
+
28
+ end
@@ -0,0 +1,237 @@
1
+ require_relative 'student'
2
+ require_relative 'student_short'
3
+ require_relative 'data_list_student_short'
4
+
5
+ # Abstract base class for managing student data persistence
6
+ #
7
+ # This class provides a common interface for reading and writing student data
8
+ # to different file formats. It implements the Template Method pattern,
9
+ # defining the overall algorithm while delegating format-specific operations
10
+ # to concrete subclasses.
11
+ #
12
+ # @abstract Subclasses must implement {#concrete_realization_read} and {#concrete_realization_write}
13
+ # @example
14
+ # # This class should not be instantiated directly
15
+ # # Use StudentsListJSON or StudentsListYAML instead
16
+ # json_list = StudentsListJSON.new('students.json')
17
+ # students = json_list.read_all
18
+ class StudentsListCommon
19
+ # Initialize a new StudentsListCommon instance
20
+ #
21
+ # @param file_path [String] Path to the file where student data will be stored
22
+ # @raise [NotImplementedError] if the file doesn't exist and concrete implementation
23
+ # doesn't provide write method
24
+ def initialize file_path
25
+ @file_path = file_path
26
+ ensure_file_exists
27
+ end
28
+
29
+ # Read all student data from the file
30
+ #
31
+ # @return [Array<Hash>] Array of student data as hashes
32
+ # @raise [ArgumentError] if the parsed data is not an array
33
+ # @raise [NotImplementedError] if concrete_realization_read is not implemented
34
+ def read_all
35
+ raw = File.exist?(@file_path) ? File.read(@file_path) : ''
36
+ return [] if raw.strip.empty?
37
+ data = concrete_realization_read(raw)
38
+ raise ArgumentError, "Должен содержать массив" unless data.is_a?(Array)
39
+ data
40
+ end
41
+
42
+ # Write all student data to the file
43
+ #
44
+ # @param array_of_hashes [Array<Hash>] Array of student data as hashes to write
45
+ # @raise [ArgumentError] if array_of_hashes is not an array
46
+ # @raise [NotImplementedError] if concrete_realization_write is not implemented
47
+ def write_all array_of_hashes
48
+ raise ArgumentError, "Ожидается массив" unless array_of_hashes.is_a?(Array)
49
+ File.write(@file_path, concrete_realization_write(array_of_hashes))
50
+ end
51
+
52
+ # Find a student by their ID
53
+ #
54
+ # @param student_id [Integer] The ID of the student to find
55
+ # @return [Student, nil] The student object if found, nil otherwise
56
+ def get_student_by_id student_id
57
+ h = read_all.find { |element| element['id'] == student_id }
58
+ return nil unless h
59
+ hash_to_student(h)
60
+ end
61
+
62
+ # Get a paginated list of student short objects
63
+ #
64
+ # @param k [Integer] Number of students to return (page size)
65
+ # @param n [Integer] Starting index (offset)
66
+ # @param existing_data_list [DataListStudentShort, nil] Optional existing data list to populate
67
+ # @return [DataListStudentShort] Data list containing student short objects
68
+ # @raise [ArgumentError] if k or n are not non-negative integers
69
+ def get_k_n_student_short_list(k, n, existing_data_list=nil)
70
+ raise ArgumentError, "k и n должны быть неотрицательными" unless k.is_a?(Integer) && n.is_a?(Integer) && k >= 0 && n >= 0
71
+ slice = read_all[n, k] || []
72
+ shorts = slice.map { |h| StudentShort.from_student(hash_to_student(h)) }
73
+ if existing_data_list
74
+ existing_data_list.elements = shorts
75
+ existing_data_list
76
+ else
77
+ DataListStudentShort.new(shorts)
78
+ end
79
+ end
80
+
81
+ # Sort all students by full name and save to file
82
+ #
83
+ # Sorts students by last_name, then first_name, then patronymic
84
+ # @return [void]
85
+ def sort_by_full_name
86
+ all = read_all
87
+ all.sort_by! { |h| [h['last_name'] || '', h['first_name'] || '', h['patronymic'] || ''] }
88
+ write_all(all)
89
+ end
90
+
91
+ # Add a new student to the list
92
+ #
93
+ # @param student [Student] The student object to add
94
+ # @return [Integer] The ID assigned to the new student
95
+ # @raise [ArgumentError] if student is not a Student object
96
+ def add_student student
97
+ raise ArgumentError, "Ожидается объект класса Student" unless student.is_a?(Student)
98
+ all = read_all
99
+ new_id = generate_new_id(all)
100
+ student_with_id = Student.new(
101
+ id: new_id,
102
+ last_name: student.last_name,
103
+ first_name: student.first_name,
104
+ patronymic: student.instance_variable_get(:@patronymic),
105
+ phone: student.instance_variable_get(:@phone),
106
+ telegram: student.instance_variable_get(:@telegram),
107
+ email: student.instance_variable_get(:@email),
108
+ git: student.git
109
+ )
110
+ all << student_to_hash(student_with_id)
111
+ write_all(all)
112
+ new_id
113
+ end
114
+
115
+ # Replace a student with the given ID
116
+ #
117
+ # @param student_id [Integer] The ID of the student to replace
118
+ # @param new_student [Student] The new student object
119
+ # @return [Boolean] true if replacement was successful
120
+ # @raise [ArgumentError] if new_student is not a Student object or student with ID not found
121
+ def replace_student_by_id(student_id, new_student)
122
+ raise ArgumentError, "Ожидается объект класса Student" unless new_student.is_a?(Student)
123
+ all = read_all
124
+ index = all.index { |h| h['id'] == student_id }
125
+ raise ArgumentError, "Студент с таким id не найден" unless index
126
+ replaced = Student.new(
127
+ id: student_id,
128
+ last_name: new_student.last_name,
129
+ first_name: new_student.first_name,
130
+ patronymic: new_student.instance_variable_get(:@patronymic),
131
+ phone: new_student.instance_variable_get(:@phone),
132
+ telegram: new_student.instance_variable_get(:@telegram),
133
+ email: new_student.instance_variable_get(:@email),
134
+ git: new_student.git
135
+ )
136
+ all[index] = student_to_hash(replaced)
137
+ write_all(all)
138
+ true
139
+ end
140
+
141
+ # Delete a student by their ID
142
+ #
143
+ # @param student_id [Integer] The ID of the student to delete
144
+ # @return [Boolean] true if student was deleted, false if not found
145
+ def delete_student_by_id student_id
146
+ all = read_all
147
+ before = all.length
148
+ all.reject! { |h| h['id'] == student_id }
149
+ deleted = all.length < before
150
+ write_all(all) if deleted
151
+ deleted
152
+ end
153
+
154
+ # Get the total count of students
155
+ #
156
+ # @return [Integer] Number of students in the list
157
+ def get_student_short_count
158
+ read_all.length
159
+ end
160
+
161
+ protected
162
+
163
+ # Parse raw file content into an array of hashes
164
+ #
165
+ # This method must be implemented by concrete subclasses to handle
166
+ # format-specific parsing (JSON, YAML, etc.)
167
+ #
168
+ # @param raw [String] Raw content from the file
169
+ # @return [Array<Hash>] Parsed array of student data
170
+ # @raise [NotImplementedError] if not implemented in subclass
171
+ def concrete_realization_read raw
172
+ raise NotImplementedError, "Метод concrete_realization должен быть реализован в подклассах"
173
+ end
174
+
175
+ # Convert array of hashes to format-specific string
176
+ #
177
+ # This method must be implemented by concrete subclasses to handle
178
+ # format-specific serialization (JSON, YAML, etc.)
179
+ #
180
+ # @param array_of_hashes [Array<Hash>] Array of student data to serialize
181
+ # @return [String] Serialized data ready for writing to file
182
+ # @raise [NotImplementedError] if not implemented in subclass
183
+ def concrete_realization_write array_of_hashes
184
+ raise NotImplementedError, "Метод concrete_realization_write должен быть реализован в подклассах"
185
+ end
186
+
187
+ # Ensure the file exists, create it with empty array if it doesn't
188
+ #
189
+ # @return [void]
190
+ def ensure_file_exists
191
+ return if File.exist?(@file_path)
192
+ File.write(@file_path, concrete_realization_write([]))
193
+ end
194
+
195
+ # Generate a new unique ID for a student
196
+ #
197
+ # @param all [Array<Hash>] Array of all existing student data
198
+ # @return [Integer] New unique ID (max existing ID + 1)
199
+ def generate_new_id all
200
+ max_id = all.map { |h| h['id'] }.compact.max || 0
201
+ max_id + 1
202
+ end
203
+
204
+ # Convert a Student object to a hash
205
+ #
206
+ # @param student [Student] Student object to convert
207
+ # @return [Hash] Hash representation of the student
208
+ def student_to_hash student
209
+ {
210
+ 'id' => student.id,
211
+ 'last_name' => student.last_name,
212
+ 'first_name' => student.first_name,
213
+ 'patronymic' => student.patronymic,
214
+ 'phone' => student.instance_variable_get(:@phone),
215
+ 'telegram' => student.instance_variable_get(:@telegram),
216
+ 'email' => student.instance_variable_get(:@email),
217
+ 'git' => student.git
218
+ }
219
+ end
220
+
221
+ # Convert a hash to a Student object
222
+ #
223
+ # @param h [Hash] Hash containing student data
224
+ # @return [Student] Student object created from hash
225
+ def hash_to_student h
226
+ Student.new(
227
+ id: h['id'],
228
+ last_name: h['last_name'],
229
+ first_name: h['first_name'],
230
+ patronymic: h['patronymic'],
231
+ phone: h['phone'],
232
+ telegram: h['telegram'],
233
+ email: h['email'],
234
+ git: h['git']
235
+ )
236
+ end
237
+ end
@@ -0,0 +1,40 @@
1
+ require 'yaml'
2
+ require 'date'
3
+ require_relative 'students_list_common'
4
+
5
+ # Concrete implementation of StudentsListCommon for YAML format
6
+ #
7
+ # This class provides YAML-specific serialization and deserialization
8
+ # for student data persistence. It extends the base StudentsListCommon
9
+ # class with YAML parsing and generation capabilities, supporting
10
+ # Date and Time objects with safe loading.
11
+ #
12
+ # @example
13
+ # yaml_list = StudentsListYAML.new('students.yaml')
14
+ # students = yaml_list.read_all
15
+ # yaml_list.add_student(Student.new(last_name: 'Smith', first_name: 'John'))
16
+ class StudentsListYAML < StudentsListCommon
17
+
18
+ protected
19
+
20
+ # Parse YAML string into an array of hashes
21
+ #
22
+ # Uses safe loading with permitted Date and Time classes to prevent
23
+ # code execution vulnerabilities while supporting common data types.
24
+ #
25
+ # @param raw [String] Raw YAML content from the file
26
+ # @return [Array<Hash>] Parsed array of student data
27
+ # @raise [Psych::SyntaxError] if YAML is malformed
28
+ def concrete_realization_read raw
29
+ YAML.safe_load(raw, permitted_classes: [Date, Time], aliases: true)
30
+ end
31
+
32
+ # Convert array of hashes to YAML string
33
+ #
34
+ # @param array_of_hashes [Array<Hash>] Array of student data to serialize
35
+ # @return [String] YAML string ready for writing to file
36
+ def concrete_realization_write array_of_hashes
37
+ YAML.dump(array_of_hashes)
38
+ end
39
+ #
40
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: students_list_yaml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - akhit
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Provides YAML serialization for student data storage
13
+ email:
14
+ - annakhit02@mail.ru
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/data_list.rb
20
+ - lib/data_list_student_short.rb
21
+ - lib/data_table.rb
22
+ - lib/module.rb
23
+ - lib/student.rb
24
+ - lib/student_base.rb
25
+ - lib/student_short.rb
26
+ - lib/students_list_common.rb
27
+ - lib/students_list_yaml.rb
28
+ homepage: https://example.com
29
+ licenses:
30
+ - MIT
31
+ metadata: {}
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubygems_version: 3.6.9
47
+ specification_version: 4
48
+ summary: YAML-based student list management
49
+ test_files: []