students_list_yaml_silerhofe 0.2.5

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: fbbd30ecddec6dddd28cc35d5005c180a19a09a6b10c328231a2ddfae8e71bd2
4
+ data.tar.gz: 6b60b60f2d94c3093146bb20deb6e8a46af7937e47030595d0296f8e08395244
5
+ SHA512:
6
+ metadata.gz: 63fa7c3053e504d20c3a3eaae366b472fdeb19ca02fbfce6fe392987b7eb97d87f9210cafe38f4196e8fd904b4cb84654b00b62dfaf9fb243b52c8f46fb17b53
7
+ data.tar.gz: '09e799ae055223e1f7d6a66442a36c927ff9b65a6b4dc009ade13adae873e48002a7fc4ec42c927058c3f09217a668ea5eb55feeb70027fa94c2dd5ca061a454'
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # StudentsListYAML Gem
2
+
3
+ Простой gem для работы со списками студентов в формате YAML.
4
+
5
+ ## Установка
6
+
7
+ ```bash
8
+ # Собрать gem
9
+ gem build students_list_yaml.gemspec
10
+
11
+ # Установить
12
+ gem install students_list_yaml-0.1.2.gem
13
+ ```
14
+
15
+ ## Использование
16
+
17
+ ```ruby
18
+ require 'students_list_yaml'
19
+
20
+ # Создаем экземпляр
21
+ list = StudentsListYAML::StudentsListYAML.new('test_students.yaml')
22
+
23
+ # Записываем в файл
24
+ list.write_to_file(students_data)
25
+ puts "Данные записаны в test_students.yaml"
26
+
27
+ # Читаем из файла
28
+ loaded_data = list.read_from_file()
29
+ puts "Прочитано #{list.count} студентов:"
30
+ loaded_data.each { |s| puts " - #{s[:name]} (#{s[:group]})" }
31
+
32
+ # Добавляем нового студента
33
+ list.add_student({ id: 3, name: "Сидорова А.В.", group: "ИВТ-303" })
34
+ list.write_to_file()
35
+
36
+ ```
37
+
38
+ ## API
39
+
40
+ ### StudentsListYAML::StudentsListYAML
41
+
42
+ Класс для управления списком студентов в YAML формате.
43
+
44
+ #### Методы
45
+
46
+ - `read_from_file(file_path)` - чтение из YAML файла
47
+ - `write_to_file(file_path, data = @students)` - запись в YAML файл
48
+ - `count` - получение количества студентов
49
+ - `add_student(student_data)` - добавление студента
50
+ - `find_by_id(id)` - поиск студента по ID
51
+ - `filter_by_group(group)` - фильтрация студентов по группе
52
+
53
+ ## Лицензия
54
+
55
+ MIT
@@ -0,0 +1,56 @@
1
+ require_relative 'Data_table'
2
+
3
+ class Data_list
4
+
5
+ def initialize(list)
6
+ @list = list
7
+ @selected =[]
8
+ end
9
+
10
+ def select(number)
11
+ check_bounds(number)
12
+ @selected << number if !@selected.include?(number)
13
+ end
14
+
15
+ def get_selected
16
+ @selected.map{ |number| @list[number].id}
17
+ end
18
+
19
+ def get_names
20
+ headers =["№ по порядку"]
21
+ headers.concat(get_fields || [])
22
+ end
23
+
24
+ def get_data
25
+ list_clone = @list.dup
26
+ table = []
27
+ list_clone.each_with_index.map do |entity, index|
28
+ row = [index+1] + get_values(entity)
29
+ table << row
30
+ end
31
+ Data_table.new(table)
32
+ end
33
+
34
+ def clear_selected
35
+ @selected.clear
36
+ end
37
+
38
+ private
39
+
40
+ def list=(value)
41
+ @list = value
42
+ end
43
+
44
+ def get_values(entity)
45
+ raise NotImplementedError, "Метод должен быть реализован в подклассе"
46
+ end
47
+
48
+ def get_fields
49
+ raise NotImplementedError, "Метод должен быть реализован в подклассе"
50
+ end
51
+
52
+ def check_bounds(number)
53
+ raise IndexError, "Выход за границы строки" if number<0 || number>=@list.size
54
+ end
55
+
56
+ end
@@ -0,0 +1,36 @@
1
+ class Data_table
2
+ def initialize(matrix)
3
+ self.matrix = matrix
4
+ end
5
+
6
+ def countRow
7
+ @matrix.size
8
+ end
9
+ def countColumn
10
+ @matrix.empty? ? 0 : @matrix[0].size
11
+ end
12
+
13
+ def [](row, column = nil)
14
+ return @matrix[row] if column.nil?
15
+ @matrix[row,column]
16
+ end
17
+
18
+ def checkIndex(row,column)
19
+ raise IndexError, "Выход за границы строки" if row<0 || row>=countRow
20
+ raise IndexError, "Выход за границы столбца" if column<0 || column>=countColumn
21
+ end
22
+ private
23
+ def matrix=(value)
24
+ unless value.is_a?(Array) && value.all? { |row| row.is_a?(Array)}
25
+ raise ArgumentError, "Значение должно быть двумерным массивом"
26
+ end
27
+ unless value.empty?
28
+ length = value[0].length
29
+ unless value.all? { |row| row.length == length}
30
+ raise ArgumentError, "Строки должны быть одного размера"
31
+ end
32
+ end
33
+ @matrix = Marshal.load(Marshal.dump(value))
34
+ end
35
+
36
+ end
@@ -0,0 +1,58 @@
1
+ require_relative 'validated_setter'
2
+
3
+ class ContactInfo
4
+ extend ValidatedSetter
5
+
6
+ validated_attr_writer :phone, validatedMethod: :valid_phone?, must_nil: true
7
+ validated_attr_writer :email, validatedMethod: :valid_email?, must_nil: true
8
+ validated_attr_writer :telegram, validatedMethod: :valid_telegram?, must_nil: true
9
+
10
+ private :phone=, :telegram=, :email=
11
+
12
+ def initialize(phone: nil, telegram: nil, email: nil)
13
+ self.phone = phone
14
+ self.telegram = telegram
15
+ self.email = email
16
+ end
17
+
18
+ def contact=(hash)
19
+ self.phone = hash[:phone]
20
+ self.telegram = hash[:telegram]
21
+ self.email = hash[:email]
22
+ end
23
+
24
+ def contact
25
+ return "telegram - #{@telegram}" if @telegram
26
+ return "email - #{@email}" if @email
27
+ return "phone - #{@phone}" if @phone
28
+ end
29
+
30
+ def has_contact?
31
+ !contact.nil?
32
+ end
33
+
34
+ def ==(other)
35
+ fields = [:@phone, :@telegram, :@email]
36
+ fields.any? do |field|
37
+ value1 = instance_variable_get(field)
38
+ value2 = other.instance_variable_get(field)
39
+ value1 && value2 && value1 == value2
40
+ end
41
+ end
42
+
43
+ def to_s
44
+ "phone: #{@phone || "нет"} telegram: #{@telegram || "нет"} email: #{@email || "нет"}"
45
+ end
46
+
47
+ def self.valid_phone?(phone)
48
+ phone.nil? || phone.match?(/^(\+7|8)?[\s\-]?\(?\d{3}\)?[\s\-]?\d{3}[\s\-]?\d{2}[\s\-]?\d{2}$/)
49
+ end
50
+
51
+ def self.valid_telegram?(telegram)
52
+ telegram.nil? || telegram.match?(/^@[A-Za-z0-9_]{5,32}$/)
53
+ end
54
+
55
+ def self.valid_email?(email)
56
+ email.nil? || email.match?(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/)
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'Data_table'
2
+ require_relative 'Data_list'
3
+ require_relative 'student_short'
4
+
5
+ class Data_list_student_short < Data_list
6
+ def get_fields
7
+ ["Фамилия И. О.", "Контакт", "Git"]
8
+ end
9
+
10
+ def get_values(student_short)
11
+ [
12
+ student_short.last_name_initials || "Null",
13
+ student_short.contact || "Null",
14
+ student_short.git || "Null"
15
+ ]
16
+ end
17
+ end
@@ -0,0 +1,110 @@
1
+ require_relative 'super_student'
2
+ require_relative 'validated_setter'
3
+ require_relative 'contact'
4
+
5
+ class Student < SuperStudent
6
+ extend ValidatedSetter
7
+ include Comparable
8
+ attr_reader :last_name, :first_name, :patronymic
9
+ validated_attr_writer :first_name, validatedMethod: :valid_name?
10
+ validated_attr_writer :last_name, validatedMethod: :valid_name?
11
+ validated_attr_writer :patronymic, validatedMethod: :valid_name?, must_nil: true
12
+ validated_attr_writer :git, validatedMethod: :valid_git?, must_nil: true
13
+
14
+ # Основной конструктор
15
+ def initialize(first_name:, last_name:, id: nil, patronymic: nil, git: nil, phone: nil, email: nil,telegram: nil)
16
+ if last_name.nil? or first_name.nil? then
17
+ raise ArgumentError, "Имя и Фамилия обезательны"
18
+ end
19
+
20
+ super(id: id, git: git)
21
+ self.last_name = last_name
22
+ self.first_name = first_name
23
+ self.patronymic = patronymic
24
+ @contact_info = ContactInfo.new(phone: phone, telegram: telegram, email: email)
25
+ end
26
+
27
+ # Альтернативный конструктор, принимающий хеш
28
+ def self.from_hash(hash)
29
+ new(
30
+ id: hash[:id],
31
+ first_name: hash[:first_name],
32
+ last_name: hash[:last_name],
33
+ patronymic: hash[:patronymic],
34
+ git: hash[:git],
35
+ phone: hash[:phone],
36
+ email: hash[:email],
37
+ telegram: hash[:telegram]
38
+ )
39
+ end
40
+
41
+ def <=>(other)
42
+ [last_name, first_name, patronymic] <=> [other.last_name, other.first_name, other.patronymic]
43
+ end
44
+
45
+ def ==(other)
46
+ (@git && other.git && @git == other.git) || @contact_info == other.instance_variable_get(:@contact_info)
47
+ end
48
+
49
+ def contact=(hash)
50
+ @contact_info.contact = hash
51
+ end
52
+
53
+ def self.valid_name?(name)
54
+ !name.nil? && name.match?(/^[A-ZА-Я]+[a-zа-яё]+$/)
55
+ end
56
+
57
+ def self.valid_phone?(phone)
58
+ ContactInfo.valid_phone?(phone)
59
+ end
60
+
61
+ def self.valid_telegram?(telegram)
62
+ ContactInfo.valid_telegram?(telegram)
63
+ end
64
+
65
+ def self.valid_email?(email)
66
+ ContactInfo.valid_email?(email)
67
+ end
68
+
69
+ def self.valid_git?(git)
70
+ git.match?(%r{^https://(github|gitlab)\.com/[A-Za-z0-9_\-]+$})
71
+ end
72
+
73
+ def contact
74
+ @contact_info.contact
75
+ end
76
+
77
+ def last_name_initials()
78
+ if patronymic then
79
+ result = "#{@last_name} " + "#{@first_name[0]}. " + "#{@patronymic[0]}."
80
+ else
81
+ result = "#{@last_name} " + "#{@first_name[0]}."
82
+ end
83
+ result
84
+ end
85
+
86
+ def to_s
87
+ result = "
88
+ id: #{format_field(@id)},
89
+ Имя: #{@first_name},
90
+ Фамилия: #{@last_name},
91
+ Отчество: #{format_field(@patronymic)},
92
+ #{@contact_info.to_s},
93
+ Git: #{format_field(@git)}
94
+ "
95
+ result
96
+ end
97
+
98
+ def to_hash()
99
+ {
100
+ id: self.id,
101
+ last_name: self.last_name,
102
+ first_name: self.first_name,
103
+ patronymic: self.patronymic,
104
+ git: self.git,
105
+ phone: @contact_info.instance_variable_get(:@phone),
106
+ email: @contact_info.instance_variable_get(:@email),
107
+ telegram: @contact_info.instance_variable_get(:@telegram)
108
+ }
109
+ end
110
+ end
@@ -0,0 +1,122 @@
1
+ require_relative 'student'
2
+ require_relative 'student_short'
3
+ require_relative 'data_list_student_short'
4
+
5
+ class StudentsListBase
6
+ def initialize(file_path)
7
+ self.file_path = file_path
8
+ read_from_file
9
+ end
10
+
11
+ def read_from_file
12
+ content = File.read(@file_path)
13
+ return [] if content.strip.empty?
14
+ @array_list = load(content).map { |element| Student.new(**element) }
15
+ end
16
+
17
+ def write_to_file()
18
+ new_data = @array_list.map { |element| element.to_hash}
19
+ write_in(new_data)
20
+ end
21
+
22
+
23
+ def get_student_by_id(id)
24
+ @array_list.find { |e| e.id == id }
25
+ end
26
+
27
+ def get_k_n_student_short_list(k, n, data_list = nil)
28
+ start_index = (k - 1) * n
29
+ end_index = start_index + n - 1
30
+ range = @array_list[start_index..end_index] || []
31
+
32
+ student_shorts = range.map { |student| StudentShort.from_student(student)}
33
+
34
+ if data_list
35
+ data_list.list = student_shorts
36
+ data_list
37
+ else
38
+ Data_list_student_short.new(student_shorts)
39
+ end
40
+ end
41
+
42
+ def sort_by_fio
43
+ @array_list.sort_by! { |student| student.last_name_initials}
44
+ end
45
+
46
+
47
+ def add_student(student)
48
+ if duplicate_exist?(student)
49
+ raise ArgumentError, "Студент с такими контактными данными (git, phone, telegram, email) уже существует"
50
+ end
51
+
52
+ new_id = @array_list.empty? ? 1 : @array_list.map { |student| student.id}.max + 1
53
+ student.instance_variable_set(:@id, new_id)
54
+ @array_list << student
55
+ new_id
56
+ end
57
+
58
+
59
+ def replace_student_by_id(id, new_student)
60
+ index = @array_list.index { |student| student.id == id}
61
+
62
+ if duplicate_exist?(new_student, exclude_id: id)
63
+ raise ArgumentError, "Студент с такими контактными данными (git, phone, telegram, email) уже существует"
64
+ end
65
+
66
+ @array_list[index] = new_student.instance_variable_set(:@id, id)
67
+ end
68
+
69
+
70
+ def delete_student_by_id(id)
71
+ @array_list.reject! { |student| student.id == id}
72
+ end
73
+
74
+ def get_student_short_count
75
+ @array_list.length
76
+ end
77
+
78
+ protected
79
+
80
+ def parse_student_data(data)
81
+ Student.new(
82
+ id: data['id'] || data[:id],
83
+ last_name: data['last_name'] || data[:last_name],
84
+ first_name: data['first_name'] || data[:first_name],
85
+ patronymic: data['patronymic'] || data[:patronymic],
86
+ git: data['git'] || data[:git],
87
+ phone: data['phone'] || data[:phone],
88
+ email: data['email'] || data[:email],
89
+ telegram: data['telegram'] || data[:telegram]
90
+ )
91
+ end
92
+
93
+ private
94
+
95
+ def duplicate_exist?(student, exclude_id: nil)
96
+ @array_list.any? do |other|
97
+ next if exclude_id && other.id == exclude_id
98
+ student == other
99
+ end
100
+ end
101
+
102
+ def load(content)
103
+ raise ArgumentError, "Метод должен быть определен в потомках"
104
+ end
105
+
106
+ def write_in(extended_data)
107
+ raise ArgumentError, "Метод должен быть определен в потомках"
108
+ end
109
+
110
+ def file_path=(path)
111
+ normalized_path = File.expand_path(path.to_s)
112
+
113
+ unless File.exist?(normalized_path) || File.file?(normalized_path)
114
+ raise ArgumentError, "Файл: #{normalized_path} не существует лиюбо неправильно указан путь"
115
+ end
116
+ @file_path = path
117
+ end
118
+
119
+ def clear
120
+ File.write(@file_path, '')
121
+ end
122
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'super_student'
2
+ class StudentShort < SuperStudent
3
+ attr_reader :last_name_initials, :contact, :git
4
+
5
+ def initialize(id:, git: nil, last_name_initials:, contact: nil)
6
+ if id.nil? then
7
+ raise ArgumentError, "ID обязательный"
8
+ end
9
+ if last_name_initials.nil? then
10
+ raise ArgumentError, "Фамилия и инициалы обязательны"
11
+ end
12
+ super(id:id, git:git)
13
+ @last_name_initials = last_name_initials
14
+ @contact = contact
15
+ end
16
+
17
+ def self.from_student(student)
18
+ new(
19
+ id: student.id,
20
+ last_name_initials: student.last_name_initials,
21
+ contact: student.contact,
22
+ git: student.git
23
+ )
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ class SuperStudent
2
+
3
+ attr_reader :id, :git
4
+ def initialize(id: nil, git: nil)
5
+ @id = id
6
+ @git = git
7
+ end
8
+
9
+ def has_git?
10
+ not_empty?(@git)
11
+ end
12
+
13
+ def has_contact?
14
+ !contact.nil?
15
+ end
16
+
17
+ def last_name_initials
18
+ raise NotImplementedError, "Метод должен быть реализован в подклассе"
19
+ end
20
+
21
+ def contact
22
+ raise NotImplementedError, "Метод должен быть реализован в подклассе"
23
+ end
24
+
25
+ def short_info
26
+ result ="
27
+ ID: #{id}
28
+ Фамилия Инициалы: #{last_name_initials}
29
+ Гит: #{format_field(@git)}
30
+ Контакт: #{format_field(contact)}
31
+ "
32
+ result
33
+ end
34
+
35
+ def to_s
36
+ short_info
37
+ end
38
+
39
+ private
40
+ def not_empty?(value)
41
+ !value.nil? && !value.to_s.empty?
42
+ end
43
+
44
+ def format_field(value)
45
+ value || 'Отсутствует'
46
+ end
47
+
48
+ end
@@ -0,0 +1,20 @@
1
+ module ValidatedSetter
2
+ private
3
+ def validated_attr_writer(attribute, validatedMethod: nil, must_nil:false)
4
+ define_method("#{attribute}=") do |value|
5
+ if value.nil?
6
+ if must_nil
7
+ instance_variable_set("@#{attribute}", value)
8
+ return
9
+ end
10
+ raise ArgumentError, "Поле #{attribute} обязательно"
11
+ end
12
+ unless validatedMethod.nil?
13
+ unless self.class.send(validatedMethod,value)
14
+ raise ArgumentError, "Некорректное значение #{attribute}"
15
+ end
16
+ end
17
+ instance_variable_set("@#{attribute}",value)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module StudentsListYAML
2
+ VERSION = "0.2.5"
3
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'students_list_yaml/version'
2
+ require 'yaml'
3
+ require_relative 'students_list_yaml/student_base'
4
+
5
+ module StudentsListYAML
6
+ class StudentsList < StudentsListBase
7
+
8
+ def load(content)
9
+ YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true) || []
10
+ end
11
+
12
+ def write_in(extended_data)
13
+ File.write(@file_path, YAML.dump(extended_data))
14
+ end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: students_list_yaml_silerhofe
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.5
5
+ platform: ruby
6
+ authors:
7
+ - SilerHofe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '13.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '13.0'
27
+ description: Реализация паттерна Repository для работы с данными студентов
28
+ email:
29
+ - Siler.Hofe@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - lib/students_list_yaml.rb
36
+ - lib/students_list_yaml/Data_list.rb
37
+ - lib/students_list_yaml/Data_table.rb
38
+ - lib/students_list_yaml/contact.rb
39
+ - lib/students_list_yaml/data_list_student_short.rb
40
+ - lib/students_list_yaml/student.rb
41
+ - lib/students_list_yaml/student_base.rb
42
+ - lib/students_list_yaml/student_short.rb
43
+ - lib/students_list_yaml/super_student.rb
44
+ - lib/students_list_yaml/validated_setter.rb
45
+ - lib/students_list_yaml/version.rb
46
+ homepage: https://zhuk.k-lab.su/SilerHofe/design-patterns-template/students_list_yaml
47
+ licenses:
48
+ - MIT
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 2.7.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.4.20
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Класс для работы со списками студентов в формате YAML
69
+ test_files: []