my_students 1.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.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/lib/my_students/version.rb +5 -0
  3. data/lib/my_students.rb +10 -0
  4. data/lib/source/controllers/student_contact_form_controller.rb +40 -0
  5. data/lib/source/controllers/student_create_form_controller.rb +48 -0
  6. data/lib/source/controllers/student_edit_form_controller.rb +45 -0
  7. data/lib/source/controllers/student_git_form_controller.rb +40 -0
  8. data/lib/source/controllers/student_list_controller.rb +112 -0
  9. data/lib/source/gui/logic_window.rb +187 -0
  10. data/lib/source/gui/main_window.rb +26 -0
  11. data/lib/source/gui/student_create_form.rb +70 -0
  12. data/lib/source/gui/test.rb +32 -0
  13. data/lib/source/logger/logger_holder.rb +23 -0
  14. data/lib/source/models/student.rb +163 -0
  15. data/lib/source/models/student_short.rb +56 -0
  16. data/lib/source/repositories/containers/data_list.rb +67 -0
  17. data/lib/source/repositories/containers/data_list_student_short.rb +18 -0
  18. data/lib/source/repositories/containers/data_table.rb +38 -0
  19. data/lib/source/repositories/data_sources/strategy/student_list_json.rb +16 -0
  20. data/lib/source/repositories/data_sources/strategy/student_list_txt.rb +39 -0
  21. data/lib/source/repositories/data_sources/strategy/student_list_yaml.rb +17 -0
  22. data/lib/source/repositories/data_sources/student_list_base.rb +91 -0
  23. data/lib/source/repositories/data_sources/student_list_strategy.rb +12 -0
  24. data/lib/source/repositories/db_university.rb +28 -0
  25. data/lib/source/repositories/student_list.rb +40 -0
  26. data/lib/source/repositories/student_list_db_adapter.rb +75 -0
  27. data/lib/source/repositories/student_list_file_adapter.rb +42 -0
  28. data/my_students.gemspec +17 -0
  29. data/sig/my_students.rbs +4 -0
  30. metadata +85 -0
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'student_short'
5
+
6
+ class Student < StudentShort
7
+ # стандартные геттеры и сеттеры для класса
8
+ attr_writer :id
9
+ attr_reader :phone, :telegram, :email, :first_name, :paternal_name
10
+
11
+ # валидаТОР номера телефона
12
+ def self.valid_phone?(phone)
13
+ phone.match(/^\+?[7,8] ?\(?\d{3}\)?-?\d{3}-?\d{2}-?\d{2}$/)
14
+ end
15
+
16
+ # валидаТОР имени
17
+ def self.valid_name?(name)
18
+ name.match(/^[А-Я][а-я]+$/)
19
+ end
20
+
21
+ # валидаТОР профиля
22
+ def self.valid_account?(account)
23
+ account.match(/^@[A-Za-z0-9\-_]+$/)
24
+ end
25
+
26
+ # валидаТОР почты
27
+ def self.valid_email?(email)
28
+ email.match(/^[A-Za-z0-9\-_]+@[A-Za-z]+\.([A-Za-z]+\.)*[A-Za-z]+$/)
29
+ end
30
+
31
+ # стандартный конструктор
32
+ def initialize(last_name, first_name, paternal_name, id: nil, git: nil, phone: nil, email: nil, telegram: nil)
33
+ self.last_name = last_name
34
+ self.first_name = first_name
35
+ self.paternal_name = paternal_name
36
+ self.id = id
37
+ self.git = git
38
+ set_contacts(phone: phone, email: email, telegram: telegram)
39
+ end
40
+
41
+
42
+ # конструктор принимающий на вход хээээш
43
+ def self.from_hash(hash)
44
+ hash = hash.dup
45
+ raise ArgumentError, 'Missing fields: last_name, first_name, paternal_name' unless hash.key?(:first_name) && hash.key?(:last_name) && hash.key?(:paternal_name)
46
+
47
+ first_name = hash.delete(:first_name)
48
+ last_name = hash.delete(:last_name)
49
+ paternal_name = hash.delete(:paternal_name)
50
+
51
+ Student.new(last_name, first_name, paternal_name, **hash)
52
+ end
53
+
54
+ def to_hash
55
+ attrs = {}
56
+ %i[last_name first_name paternal_name id phone telegram email git].each do |attr|
57
+ attr_val = send(attr)
58
+ attrs[attr] = attr_val unless attr_val.nil?
59
+ end
60
+ attrs
61
+ end
62
+
63
+ # конструктор из json-строки
64
+ def self.init_from_json(str)
65
+ params = JSON.parse(str, { symbolize_names: true })
66
+ from_hash(params)
67
+ end
68
+
69
+ #сеттеры
70
+ def phone=(phone)
71
+ raise ArgumentError, "Incorrect value: phone=#{phone}!" if !phone.nil? && !Student.valid_phone?(phone)
72
+
73
+ @phone = phone
74
+ end
75
+
76
+ def first_name=(first_name)
77
+ raise ArgumentError, "Incorrect value: first_name=#{first_name}!" if !first_name.nil? && !Student.valid_name?(first_name)
78
+
79
+ @first_name = first_name
80
+ end
81
+
82
+ def last_name=(last_name)
83
+ raise ArgumentError, "Incorrect value: last_name=#{last_name}" if !last_name.nil? && !Student.valid_name?(last_name)
84
+
85
+ @last_name = last_name
86
+ end
87
+
88
+ def paternal_name=(paternal_name)
89
+ raise ArgumentError, "Incorrect value: paternal_name=#{paternal_name}!" if !paternal_name.nil? && !Student.valid_name?(paternal_name)
90
+
91
+ @paternal_name = paternal_name
92
+ end
93
+
94
+ def git=(git)
95
+ raise ArgumentError, "Incorrect value: git=#{git}!" if !git.nil? && !Student.valid_account?(git)
96
+
97
+ @git = git
98
+ end
99
+
100
+ def telegram=(telegram)
101
+ raise ArgumentError, "Incorrect value: telegram=#{telegram}!" if !telegram.nil? && !Student.valid_account?(telegram)
102
+
103
+ @telegram = telegram
104
+ end
105
+
106
+ def email=(email)
107
+ raise ArgumentError, "Incorrect value: email=#{email}!" if !email.nil? && !Student.valid_email?(email)
108
+
109
+ @email = email
110
+ end
111
+
112
+ # метод возвращающий фамилию и инициалы у объекта
113
+ def last_name_and_initials
114
+ "#{last_name} #{first_name[0]}. #{paternal_name[0]}."
115
+ end
116
+
117
+ # метод возвращающий краткую инф-ю об объекте
118
+ def short_info
119
+ info = {}
120
+ info[:last_name_and_initials] = last_name_and_initials
121
+ info[:contact] = contact
122
+ info[:git] = git
123
+ JSON.generate(info)
124
+ end
125
+
126
+
127
+ def set_contacts(phone: nil, telegram: nil, email: nil)
128
+ self.phone = phone if phone
129
+ self.telegram = telegram if telegram
130
+ self.email = email if email
131
+ @contact = contact
132
+ end
133
+
134
+ # метод возвращающий представление объекта в виде строки
135
+ def to_s
136
+ result = "#{last_name} #{first_name} #{paternal_name}"
137
+ result += " id=#{id}" unless id.nil?
138
+ result += " phone=#{phone}" unless phone.nil?
139
+ result += " git=#{git}" unless git.nil?
140
+ result += " telegram=#{telegram}" unless telegram.nil?
141
+ result += " email=#{email}" unless email.nil?
142
+ result
143
+ end
144
+
145
+ private
146
+
147
+ # метод устанавливающий контакт
148
+ def contact
149
+ # return @contact = {phone: phone} unless phone.nil?
150
+ # return @contact = {telegram: telegram} unless telegram.nil?
151
+ # return @contact = {email: email} unless email.nil?
152
+ #
153
+ # nil
154
+
155
+ if phone
156
+ @contact = {type: :phone, value: phone}
157
+ elsif telegram
158
+ @contact = {type: :telegram, value: telegram}
159
+ elsif email
160
+ @contact = {type: :email, value: email}
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+
4
+ class StudentShort
5
+ # стандартные геттеры для класса
6
+ attr_reader :id, :git, :contact, :last_name, :initials
7
+
8
+ # стандартный конструктор, принимающий аргументов экземпляр класса student
9
+ def initialize(student)
10
+ @id = student.id
11
+
12
+ @last_name = student.last_name
13
+ @initials = "#{student.first_name[0]}. #{student.paternal_name[0]}."
14
+ @git = student.git unless student.git.nil?
15
+ @contact = student.set_contacts(phone: student.phone, email: student.email, telegram: student.telegram)
16
+ end
17
+
18
+ # кастомный конструктор, принимающий на вход id и строку, которая содержит всю остальную инф-ю
19
+ def self.from_str(id, str)
20
+ result = JSON.parse(str)
21
+ raise ArgumentError, 'Missing fields: last_name, first_name, paternal_name' unless result.key?('first_name') &&
22
+ result.key?('last_name') && result.key?('paternal_name')
23
+
24
+ StudentShort.new(Student.new(result['last_name'],result['first_name'],result['paternal_name'],id: id,
25
+ phone: result['phone'], git: result['git'],
26
+ email: result['email'],telegram: result['telegram']))
27
+ end
28
+
29
+ # метод возвращающий фамилию и инициалы у объекта
30
+ def last_name_and_initials
31
+ "#{@last_name} #{@initials}"
32
+ end
33
+
34
+ # метод возвращающий представление объекта в виде строки
35
+ def to_s
36
+ result = last_name_and_initials
37
+ result += " id= #{id} " unless id.nil?
38
+ result += contact unless contact.nil?
39
+ result
40
+ end
41
+
42
+ # метод проверяющий наличие гита
43
+ def git?
44
+ !git.nil?
45
+ end
46
+
47
+ # метод проверяющий наличие контакта
48
+ def contact?
49
+ !contact.nil?
50
+ end
51
+
52
+ def validate
53
+ git? && contact?
54
+ end
55
+
56
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'data_table'
3
+
4
+ class DataList
5
+
6
+ private_class_method :new
7
+
8
+ attr_writer :objects_list
9
+
10
+ def initialize(objects)
11
+ self.objects_list = objects
12
+ @observers = []
13
+ end
14
+
15
+ def add_observer(observer)
16
+ @observers.append(observer)
17
+ end
18
+
19
+ def remove_observer(observer)
20
+ @observers.delete(observer)
21
+ end
22
+ def notify
23
+ @observers.each { |observer| observer.on_datalist_changed(get_data) }
24
+ end
25
+
26
+ # добавить айди в выборку
27
+ def select_elem(number)
28
+ self.selected_object = number
29
+ end
30
+
31
+ def selected_id
32
+ objects_list[selected_object].id
33
+ end
34
+
35
+ # шаблон
36
+ def get_data
37
+ index_id=0
38
+ dt = objects_list.inject([]) do |res, object|
39
+ row=[index_id]
40
+ row.append(*table_fields(object))
41
+ index_id+=1
42
+ res<<row
43
+ end
44
+ DataTable.new(dt)
45
+ end
46
+
47
+
48
+ def replace_objects(objects)
49
+ self.objects_list = objects.dup
50
+ notify
51
+ end
52
+
53
+ protected
54
+
55
+ # должен быть реализован в детях
56
+ def get_names; end
57
+
58
+ # теперь этот метод необходимо переопределять у наследников(если я правильно понял принцип паттерна)
59
+ def table_fields(object)
60
+ []
61
+ end
62
+
63
+ private
64
+
65
+ attr_reader :objects_list
66
+ attr_accessor :selected_object
67
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'data_list'
4
+ class DataListStudentShort < DataList
5
+ public_class_method :new
6
+
7
+ # переопределенный метод из предка
8
+ def get_names
9
+ ["last_name_and_initials", "git", "contact"]
10
+ end
11
+
12
+ protected
13
+
14
+ # паблон шаттерн
15
+ def table_fields(object)
16
+ [object.last_name_and_initials, object.git, object.contact]
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DataTable
4
+
5
+ attr_reader :rows_count, :cols_count
6
+
7
+ def initialize(table)
8
+
9
+ self.rows_count = table.length
10
+ max_cols = 0
11
+ table.each do |row|
12
+ max_cols = row.length if row.size > max_cols
13
+ end
14
+ self.cols_count = max_cols
15
+ self.table = table
16
+ end
17
+
18
+ def get_item(row, col)
19
+ return nil if row >= rows_count || row.negative?
20
+ return nil if col >= cols_count || col.negative?
21
+
22
+ table[row][col]
23
+ end
24
+
25
+ def to_my_array
26
+ table.dup
27
+ end
28
+
29
+ def to_s
30
+ table.map { |row| "[#{row.join(', ')}]" }.join("\n")
31
+ end
32
+
33
+ private
34
+
35
+ attr_accessor :table
36
+ attr_writer :rows_count, :cols_count
37
+
38
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../student_list_strategy'
4
+ require 'json'
5
+ class StudentListJSON < StudentListStrategy
6
+ public_class_method :new
7
+
8
+
9
+ def str_to_list(str)
10
+ JSON.parse(str, { symbolize_names: true })
11
+ end
12
+
13
+ def list_to_str(list)
14
+ JSON.generate(list)
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../student_list_strategy'
4
+ require 'json'
5
+ class StudentListTxt < StudentListStrategy
6
+ public_class_method :new
7
+
8
+
9
+ def str_to_list(str)
10
+ transform_to_hashes(str.split("\n").map(&:chomp))
11
+ end
12
+
13
+ def list_to_str(list)
14
+ transform_to_string(list)
15
+ end
16
+
17
+ private
18
+
19
+ def transform_to_hashes(list_of_strings)
20
+ list_of_hashes = []
21
+ list_of_strings.each do |string|
22
+ string = string.gsub('"', "")
23
+ hash = {}
24
+ string.split(',').each do |attribute|
25
+ key, value = attribute.split(':').map(&:strip)
26
+ hash[key.to_sym] = value
27
+ end
28
+ list_of_hashes << hash
29
+ end
30
+ list_of_hashes
31
+ end
32
+
33
+ def transform_to_string(hashes)
34
+ lines = hashes.map do |hash|
35
+ hash.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
36
+ end
37
+ lines.join("\n")
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../student_list_strategy'
4
+ require 'yaml'
5
+
6
+ class StudentListYaml<StudentListStrategy
7
+ public_class_method :new
8
+
9
+
10
+ def str_to_list(str)
11
+ YAML.safe_load(str).map { |h| h.transform_keys(&:to_sym) }
12
+ end
13
+
14
+ def list_to_str(hash_list)
15
+ hash_list.map { |h| h.transform_keys(&:to_s) }.to_yaml
16
+ end
17
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StudentListBase
4
+
5
+
6
+ attr_writer :data_type
7
+
8
+ # конструктор
9
+ def initialize(data_type)
10
+ self.students = []
11
+ self.cur_id = 1
12
+ self.data_type = data_type
13
+ end
14
+
15
+ # загрузка из файла
16
+ def load_from_file(file_path)
17
+ list = data_type.str_to_list(File.read(file_path))
18
+ self.students = list.map { |h| Student.from_hash(h) }
19
+ update_cur_id
20
+ end
21
+
22
+ # выгрузка в файл
23
+ def save_to_file(file_path)
24
+ list = students.map(&:to_hash)
25
+ File.write(file_path, data_type.list_to_str(list))
26
+ end
27
+
28
+ # найти студента по айди
29
+ def student_by_id(student_id)
30
+ students.detect { |s| s.id == student_id }
31
+ end
32
+
33
+ # найти студента по фамилии и инциалам
34
+ def student_by_name(student_name)
35
+ students.filter { |s| s.last_name_and_initials == student_name }
36
+ end
37
+
38
+ # Получить page по счету count элементов (страница начинается с 1)
39
+ def k_n_student_short_list(page, n, data_list)
40
+ page_list = students[(page-1)*n, n].map{|st| StudentShort.new(st)}
41
+ return DataListStudentShort.new(page_list) if data_list.nil?
42
+
43
+ data_list.replace_objects(page_list)
44
+ data_list
45
+ end
46
+
47
+ # сортировка
48
+ def sorted
49
+ students.sort_by(&:last_name_and_initials)
50
+ end
51
+
52
+ # добавление студента
53
+ def add_student(student)
54
+ student.id = cur_id
55
+ students << student
56
+ self.cur_id += 1
57
+ end
58
+
59
+ # замена студента
60
+ def replace_student(student_id, student)
61
+ idx = students.find_index { |s| s.id == student_id }
62
+ students[idx] = student
63
+ end
64
+
65
+ # отчисление студента))
66
+ def remove_student(student_id)
67
+ students.reject! { |s| s.id == student_id }
68
+ end
69
+
70
+ # число студентов
71
+ def student_count
72
+ students.size
73
+ end
74
+
75
+
76
+ protected
77
+
78
+ attr_accessor :students, :cur_id
79
+
80
+ private
81
+
82
+ attr_reader :data_type
83
+
84
+ # Метод для обновлении информации в cur_id
85
+ def update_cur_id
86
+ self.cur_id = students.max_by(&:id).id.to_i + 1
87
+ end
88
+
89
+ # чтобы никто мне ничего не трогал в списке студентов
90
+
91
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StudentListStrategy
4
+ private_class_method :new
5
+
6
+ protected
7
+
8
+ # Паблон шаттерн
9
+ def str_to_list(str); end
10
+
11
+ def list_to_str(list); end
12
+ end
@@ -0,0 +1,28 @@
1
+ class DBUniversity
2
+ private_class_method :new
3
+ @instance_mutex = Mutex.new
4
+
5
+ def initialize
6
+ @client = SQLite3::Database.open '/Users/kirilltitov/RubymineProjects/ruby_labs/university.sqlite'
7
+ @client.results_as_hash = true
8
+ end
9
+
10
+ def self.instance
11
+ return @instance if @instance
12
+
13
+ @instance_mutex.synchronize do
14
+ @instance ||= new
15
+ end
16
+ @instance
17
+ end
18
+
19
+ def prepare_exec(statement, *params)
20
+ @client.prepare(statement).execute(*params)
21
+ end
22
+
23
+ def query(statement)
24
+ @client.query(statement)
25
+ end
26
+
27
+
28
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StudentList
4
+
5
+ # конструктор
6
+ def initialize(data_adapter)
7
+ @data_adapter = data_adapter
8
+ end
9
+
10
+ # получить студента по id
11
+ def student_by_id(student_id)
12
+ @data_adapter.student_by_id(student_id)
13
+ end
14
+
15
+
16
+ #добавление студента
17
+ def add_student(student)
18
+ @data_adapter.add_student(student)
19
+ end
20
+
21
+ #отчисление студента
22
+ def remove_student(student_id)
23
+ @data_adapter.remove_student(student_id)
24
+ end
25
+
26
+ #замена студента
27
+ def replace_student(student_id, student)
28
+ @data_adapter.replace_student(student_id, student)
29
+ end
30
+
31
+ #подсчет количества студентов
32
+ def student_count
33
+ @data_adapter.student_count
34
+ end
35
+
36
+ #получение n элементов k страницы
37
+ def k_n_student_short_list(k,n, data_list)
38
+ @data_adapter.k_n_student_short_list(k,n,data_list)
39
+ end
40
+ end
@@ -0,0 +1,75 @@
1
+ require_relative 'db_university'
2
+ require 'json'
3
+ require 'sqlite3'
4
+ class StudentListDbAdapter
5
+
6
+ # новый конструктор
7
+ def initialize
8
+ self.client = DBUniversity.instance
9
+ end
10
+
11
+ def from_array_to_hash(arr)
12
+ attrs = {}
13
+ i=0
14
+ %i[id last_name first_name paternal_name phone telegram email git].each do |attr|
15
+ attrs[attr] = arr[i] unless arr[i].nil?
16
+ i=i+1
17
+ end
18
+ attrs
19
+ end
20
+
21
+ # вернуть студента по ид
22
+ def student_by_id(student_id)
23
+ hash = client.prepare_exec('SELECT * FROM students WHERE id = ?',student_id).first
24
+ hash = from_array_to_hash(hash)
25
+ return nil if hash.nil?
26
+
27
+ Student.from_hash(hash)
28
+
29
+ end
30
+
31
+ def k_n_student_short_list(k,n, data_list=nil)
32
+
33
+ offset = (k - 1) * n
34
+ students = client.prepare_exec('SELECT * FROM students LIMIT ?, ?', offset, n)
35
+
36
+ slice = students.map { |h|
37
+ h = h.transform_keys(&:to_sym)
38
+ StudentShort.new(Student.from_hash(h))
39
+ }
40
+ return DataListStudentShort.new(slice) if data_list.nil?
41
+
42
+ data_list.replace_objects(slice)
43
+ data_list
44
+ end
45
+
46
+ # добавление студента
47
+ def add_student(student)
48
+ stmt = client.prepare_exec('insert into students (first_name, last_name, paternal_name, phone, telegram, email, git) VALUES (?, ?, ?, ?, ?, ?, ?)', *student_fields(student))
49
+ end
50
+
51
+ # отчисление студента
52
+ def remove_student(student_id)
53
+ client.prepare_exec('DELETE FROM students WHERE id = ?', student_id)
54
+ end
55
+
56
+ # обновление студента
57
+ def replace_student(student_id, student)
58
+ template = 'UPDATE students SET first_name=?, last_name=?, paternal_name=?, phone=?, telegram=?, email=?, git=? WHERE id=?'
59
+ client.prepare_exec(template, *student_fields(student), student_id)
60
+
61
+ end
62
+
63
+ def student_count
64
+ client.query('SELECT COUNT(id) FROM students').next[0]
65
+ end
66
+
67
+
68
+ private
69
+
70
+ attr_accessor :client
71
+
72
+ def student_fields(student)
73
+ [student.first_name, student.last_name, student.paternal_name, student.phone, student.telegram, student.email, student.git]
74
+ end
75
+ end