student_gem 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a702216837e16e05dd2c9d7db66ce027a78c5551c98ee81905045b0d57db3d29
4
- data.tar.gz: 11ffbcf1a9f0d46a012942e6137b04d8cfe898c4097fcd895201602ee02befee
3
+ metadata.gz: ccd7efc7cc0b3db277b399f2f3d29200f933364df6ef62fae21c8ef261ccec33
4
+ data.tar.gz: 79ac98a8f24c136eb12d33f4b269303388f5841efa88050900588a813abb796d
5
5
  SHA512:
6
- metadata.gz: 5d8e7cfeefc00b6fc1ec5c90171b82ef7a5791fbffbccafcd70e829dbb34aed351a8962085b38499dbd32696c836c7b1a616f207a9c368758094e0a2ea0fe38c
7
- data.tar.gz: c90e74c115102570d70be9ff4760c68877acefb76db5c184d2399b2d95c2d62997c0f97190d93ac565210842d4bb62d7411aed6ec2edd572e5c44512002e5f87
6
+ metadata.gz: 945de714158f048bd8efa84014b359587f592b0192c41449c7c47679dc226ab659405f8ead5e3f50f3567322216bd0e16e1ad7ba415d3c32f215c33f031ec8f2
7
+ data.tar.gz: d2d62790cc68de1110b65b96b39356656dfdb86efa9bb2d31cabad5e3413370e0349c8633236f2b6820e9c231794ca9d42fb817f579d6a3d7b0ee76d4decf42e
data/lib/data_list.rb CHANGED
@@ -1,4 +1,4 @@
1
- require_relative '../data_table.rb'
1
+ require_relative 'data_table.rb'
2
2
 
3
3
  # класс работы с массивом объектов определенного класса
4
4
  class Data_list
@@ -48,12 +48,12 @@ class Data_list
48
48
  end
49
49
 
50
50
  # двумерный массив (экземпляр Data Table) объектов и их атрибутов
51
- def get_data
51
+ def get_data(k, n)
52
52
  unless self.class.private_method_defined?(:get_object_data)
53
53
  raise(NotImplementedError, "#{self.class} должен реализовать метод #get_object_data")
54
54
  end
55
55
  table = []
56
- @selected.each{|number| table << [number + 1].concat(get_object_data(number))}
56
+ @selected.each{|number| table << [(k * n) + number + 1].concat(get_object_data(number))}
57
57
  Data_table.new(table)
58
58
  end
59
59
 
@@ -21,9 +21,9 @@ class Data_list_student_short < Data_list
21
21
  end
22
22
 
23
23
  # уведомление об изменении состояния подписчикам
24
- def notify(n)
24
+ def notify(k, n)
25
25
  (0...n).each {|number| select(number)}
26
- data_table = get_data
26
+ data_table = get_data(k, n)
27
27
  clear_selected
28
28
 
29
29
  @subscribers.each do |view|
@@ -0,0 +1,117 @@
1
+ require_relative 'student_list.rb'
2
+ require_relative 'modal_controller.rb'
3
+
4
+ # контроллер основного окна
5
+ class Student_list_controller
6
+
7
+ # константа числа строк на странице
8
+ ROWS_ON_PAGE = 20
9
+
10
+ # инициализация номера страницы
11
+ def initialize(view)
12
+ @view = view
13
+ @student_list = Student_list.new
14
+ @page = 0
15
+
16
+ @data_list_student_short = Data_list_student_short.new(nil)
17
+ @data_list_student_short.subscribe(@view)
18
+ end
19
+
20
+
21
+ # реакция на выделение ячеек таблицы
22
+ def table_rows_onselect(row)
23
+ @data_list_student_short.select(row)
24
+ end
25
+
26
+ # реакция на снятие выделения
27
+ def table_rows_ondeselect(row)
28
+ @data_list_student_short.reject(row)
29
+ end
30
+
31
+
32
+ # реакция на нажатие кнопки вставки
33
+ def insert_button_onclick
34
+ modal_controller = Insertion_controller.new(@view)
35
+ result = modal_controller.execute
36
+ if result[:status] == :ok
37
+ begin
38
+ @student_list.insert(result[:student])
39
+ rescue => e
40
+ show_error(e)
41
+ end
42
+ refresh_data(@page)
43
+ end
44
+ end
45
+
46
+ # реакция на нажатие кнопки замены
47
+ def update_button_onclick
48
+ modal_controller = Updation_controller.new(@view)
49
+ id = @data_list_student_short.get_selected.first
50
+ student = @student_list.get_student(id)
51
+ result = modal_controller.execute(old_student: student)
52
+ if result[:status] == :ok
53
+ begin
54
+ @student_list.update(id, result[:student])
55
+ rescue => e
56
+ show_error(e)
57
+ end
58
+ refresh_data(@page)
59
+ end
60
+
61
+ end
62
+
63
+ # реакция на нажатие кнопки удаления
64
+ def delete_button_onclick
65
+ ids = @data_list_student_short.get_selected
66
+ begin
67
+ ids.each {|id| @student_list.delete(id)}
68
+ rescue => e
69
+ show_error(e)
70
+ end
71
+ refresh_data(@page)
72
+ end
73
+
74
+
75
+ # реакция на нажатие кнопки назад
76
+ def previous_button_onclick
77
+ @page -= 1
78
+ refresh_data(@page)
79
+ end
80
+
81
+ # реакция на нажатие кнопки вперед
82
+ def next_button_onclick
83
+ @page += 1
84
+ refresh_data(@page)
85
+ end
86
+
87
+
88
+ # обновление данных на странице
89
+ def refresh_data(k)
90
+ @page = max_page - 1 if @page >= max_page
91
+ begin
92
+ @student_list.get_k_n_student_short_list(k, ROWS_ON_PAGE, @data_list_student_short)
93
+ max_pages = max_page
94
+ rescue => e
95
+ show_error(e)
96
+ end
97
+ @data_list_student_short.notify(k, ROWS_ON_PAGE)
98
+
99
+ @view.page_count.text = "Страница: #{@page + 1} / #{max_pages}"
100
+ @page == 0 ? @view.previous_button.disable : @view.previous_button.enable
101
+ @page == max_pages - 1 ? @view.next_button.disable : @view.next_button.enable
102
+ end
103
+
104
+
105
+ def show_error(e)
106
+ error_box = FXMessageBox.new(@view, "Ошибка", e.to_s, opts: MBOX_OK)
107
+ error_box.execute
108
+ end
109
+
110
+
111
+ private
112
+
113
+ # вычисление максимального числа страниц
114
+ def max_page
115
+ (@student_list.count.to_i + ROWS_ON_PAGE - 1) / ROWS_ON_PAGE
116
+ end
117
+ end
@@ -1,6 +1,6 @@
1
1
  require 'fox16'
2
2
  include Fox
3
- require_relative 'controller.rb'
3
+ require_relative 'main_controller.rb'
4
4
 
5
5
  # интерфейс подписчика модели
6
6
  module View
@@ -28,7 +28,7 @@ class MainWindow < FXMainWindow
28
28
  # конструктор страницы и начальное заполнение данными
29
29
  def initialize(app)
30
30
  super(app, "Таблица студентов", width: 750, height: 500)
31
- @controller = Main_window_controller.new(self)
31
+ @controller = Student_list_controller.new(self)
32
32
 
33
33
  main_frame = FXVerticalFrame.new(self, LAYOUT_FILL_X | LAYOUT_FILL_Y, padding: 10)
34
34
  @table = FXTable.new(main_frame, opts: TABLE_COL_SIZABLE | TABLE_ROW_SIZABLE | LAYOUT_FILL_X | LAYOUT_FILL_Y)
@@ -144,11 +144,18 @@ class MainWindow < FXMainWindow
144
144
 
145
145
  # установка значений в строках таблицы
146
146
  def set_table_data(data_table)
147
+ width = 195
148
+ height = 17
149
+ pixel = [255, 0, 0, 255].pack('C*')
150
+ pixels = pixel * (width * height)
151
+ icon = FXIcon.new(app, pix: nil, opts: IMAGE_KEEP, width: width, height: height)
152
+ icon.pixels = pixels
153
+ icon.create
147
154
  (0...data_table.get_rows).each do |i|
148
155
  @table.setRowText(i, data_table.get_element(i, 0).to_s)
149
156
  (1...data_table.get_columns).each do |j|
150
157
  if data_table.get_element(i, j).nil?
151
- @table.setItemText(i, j - 1, "[NULL]")
158
+ @table.setItemIcon(i, j - 1, icon)
152
159
  else
153
160
  @table.setItemText(i, j - 1, data_table.get_element(i, j).to_s)
154
161
  end
@@ -166,74 +173,10 @@ class MainWindow < FXMainWindow
166
173
  end
167
174
 
168
175
 
169
- # класс модельного окна для добавления и изменения записей
170
- class ModalWindow < FXDialogBox
171
-
172
- # геттеры для обращения к текстовым полям и к кнопке "Сохранить"
173
- attr_reader :last_name, :first_name, :patronymic, :telegram, :email, :phone, :git, :ok_button
174
-
175
- # конструктор модального окна
176
- def initialize(owner, controller)
177
- super(owner, "Создание студента", DECOR_TITLE | DECOR_BORDER, width: 400, height: 350)
178
- @controller = controller
179
-
180
- main_frame = FXVerticalFrame.new(self, LAYOUT_FILL_X | LAYOUT_FILL_Y, padding: 10)
181
- grid = FXMatrix.new(main_frame, 2, MATRIX_BY_COLUMNS | LAYOUT_FILL_X, hSpacing: 10, vSpacing: 8)
182
-
183
- FXLabel.new(grid, "Фамилия:", opts: LAYOUT_CENTER_Y)
184
- @last_name = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
185
- FXLabel.new(grid, "Имя:", opts: LAYOUT_CENTER_Y)
186
- @first_name = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
187
- FXLabel.new(grid, "Отчество:", opts: LAYOUT_CENTER_Y)
188
- @patronymic = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
189
- FXLabel.new(grid, "Телеграм:", opts: LAYOUT_CENTER_Y)
190
- @telegram = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
191
- FXLabel.new(grid, "Почта:", opts: LAYOUT_CENTER_Y)
192
- @email = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
193
- FXLabel.new(grid, "Телефон:", opts: LAYOUT_CENTER_Y)
194
- @phone = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
195
- FXLabel.new(grid, "Гит:", opts: LAYOUT_CENTER_Y)
196
- @git = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
197
-
198
- button_frame = FXHorizontalFrame.new(main_frame, LAYOUT_FILL_X | LAYOUT_BOTTOM, padding: 5, hSpacing: 8)
199
- FXButton.new(button_frame, "Отмена", nil, self, ID_CANCEL, opts: BUTTON_NORMAL | FRAME_RAISED)
200
- @ok_button = FXButton.new(button_frame, "Сохранить", nil, self, ID_ACCEPT, opts: BUTTON_NORMAL | FRAME_RAISED)
201
- @ok_button.disable
202
-
203
- @last_name.connect(SEL_CHANGED) do
204
- text_fields_onchange
205
- end
206
- @first_name.connect(SEL_CHANGED) do
207
- text_fields_onchange
208
- end
209
- @patronymic.connect(SEL_CHANGED) do
210
- text_fields_onchange
211
- end
212
- @telegram.connect(SEL_CHANGED) do
213
- text_fields_onchange
214
- end
215
- @email.connect(SEL_CHANGED) do
216
- text_fields_onchange
217
- end
218
- @phone.connect(SEL_CHANGED) do
219
- text_fields_onchange
220
- end
221
- @git.connect(SEL_CHANGED) do
222
- text_fields_onchange
223
- end
224
- end
225
-
226
- # делегация валидации контроллеру
227
- def text_fields_onchange
228
- @controller.text_fields_onchange
229
- end
230
-
231
- end
232
-
233
-
234
176
  # запуск приложения
177
+ script = self
235
178
  FXApp.new do |app|
236
- MainWindow.new(app)
179
+ main = MainWindow.new(app)
237
180
  app.create
238
181
  app.run
239
182
  end
@@ -0,0 +1,74 @@
1
+ require_relative 'modal_window.rb'
2
+
3
+ # интерфейс контроллера модального окна для стратегии
4
+ class Modal_window_controller
5
+
6
+ # конструктор контроллера модального окна
7
+ def initialize(view)
8
+ @modal = ModalWindow.new(view, self)
9
+ end
10
+
11
+ # основная работа контроллера
12
+ def execute(old_student: nil)
13
+ fill_text_fields(old_student)
14
+ if @modal.execute(PLACEMENT_OWNER) == 1
15
+ student = Student.from_hash(student_attributes)
16
+ {status: :ok, student: student}
17
+ else
18
+ {status: :cancel}
19
+ end
20
+ end
21
+
22
+
23
+ # общий для контроллера модального окна метод валидации
24
+ def text_fields_onchange
25
+ student = student_attributes
26
+ [
27
+ Student.valid_name?(student["last_name"]),
28
+ Student.valid_name?(student["first_name"]),
29
+ Student.valid_name?(student["patronymic"], true),
30
+ Student.valid_telegram?(student["telegram"]),
31
+ Student.valid_email?(student["email"]),
32
+ Student.valid_phone?(student["phone"]),
33
+ Student.valid_git?(student["git"])
34
+ ].all? ? @modal.ok_button.enable : @modal.ok_button.disable
35
+ end
36
+
37
+ # сбор атрибутов студента из полей ввода
38
+ def student_attributes
39
+ attributes = {}
40
+ attributes["last_name"] = @modal.last_name.text.empty? ? nil : @modal.last_name.text
41
+ attributes["first_name"] = @modal.first_name.text.empty? ? nil : @modal.first_name.text
42
+ attributes["patronymic"] = @modal.patronymic.text.empty? ? nil : @modal.patronymic.text
43
+ attributes["telegram"] = @modal.telegram.text.empty? ? nil : @modal.telegram.text
44
+ attributes["email"] = @modal.email.text.empty? ? nil : @modal.email.text
45
+ attributes["phone"] = @modal.phone.text.empty? ? nil : @modal.phone.text
46
+ attributes["git"] = @modal.git.text.empty? ? nil : @modal.git.text
47
+ attributes
48
+ end
49
+
50
+ # заполнение полей ввода контроллерами
51
+ def fill_text_fields(student)
52
+ raise(NotImplementedError, "#{self.class} должен реализовать метод #fill_text_fields")
53
+ end
54
+ end
55
+
56
+ # контроллер модального окна при вставке
57
+ class Insertion_controller < Modal_window_controller
58
+ def fill_text_fields(student)
59
+ end
60
+ end
61
+
62
+ # контроллер модального окна при изменении
63
+ class Updation_controller < Modal_window_controller
64
+ def fill_text_fields(student)
65
+ attributes = student.to_h
66
+ @modal.last_name.text = attributes["last_name"]
67
+ @modal.first_name.text = attributes["first_name"]
68
+ @modal.patronymic.text = attributes["patronymic"]
69
+ @modal.telegram.text = attributes["telegram"]
70
+ @modal.email.text = attributes["email"]
71
+ @modal.phone.text = attributes["phone"]
72
+ @modal.git.text = attributes["git"]
73
+ end
74
+ end
@@ -0,0 +1,62 @@
1
+ # класс модельного окна для добавления и изменения записей
2
+ class ModalWindow < FXDialogBox
3
+
4
+ # геттеры для обращения к текстовым полям и к кнопке "Сохранить"
5
+ attr_reader :last_name, :first_name, :patronymic, :telegram, :email, :phone, :git, :ok_button
6
+
7
+ # конструктор модального окна
8
+ def initialize(owner, controller)
9
+ super(owner, "Создание студента", DECOR_TITLE | DECOR_BORDER, width: 400, height: 350)
10
+ @controller = controller
11
+
12
+ main_frame = FXVerticalFrame.new(self, LAYOUT_FILL_X | LAYOUT_FILL_Y, padding: 10)
13
+ grid = FXMatrix.new(main_frame, 2, MATRIX_BY_COLUMNS | LAYOUT_FILL_X, hSpacing: 10, vSpacing: 8)
14
+
15
+ FXLabel.new(grid, "Фамилия:", opts: LAYOUT_CENTER_Y)
16
+ @last_name = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
17
+ FXLabel.new(grid, "Имя:", opts: LAYOUT_CENTER_Y)
18
+ @first_name = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
19
+ FXLabel.new(grid, "Отчество:", opts: LAYOUT_CENTER_Y)
20
+ @patronymic = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
21
+ FXLabel.new(grid, "Телеграм:", opts: LAYOUT_CENTER_Y)
22
+ @telegram = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
23
+ FXLabel.new(grid, "Почта:", opts: LAYOUT_CENTER_Y)
24
+ @email = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
25
+ FXLabel.new(grid, "Телефон:", opts: LAYOUT_CENTER_Y)
26
+ @phone = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
27
+ FXLabel.new(grid, "Гит:", opts: LAYOUT_CENTER_Y)
28
+ @git = FXTextField.new(grid, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_FILL_X | LAYOUT_CENTER_Y)
29
+
30
+ button_frame = FXHorizontalFrame.new(main_frame, LAYOUT_FILL_X | LAYOUT_BOTTOM, padding: 5, hSpacing: 8)
31
+ FXButton.new(button_frame, "Отмена", nil, self, ID_CANCEL, opts: BUTTON_NORMAL | FRAME_RAISED)
32
+ @ok_button = FXButton.new(button_frame, "Сохранить", nil, self, ID_ACCEPT, opts: BUTTON_NORMAL | FRAME_RAISED)
33
+ @ok_button.disable
34
+
35
+ @last_name.connect(SEL_CHANGED) do
36
+ text_fields_onchange
37
+ end
38
+ @first_name.connect(SEL_CHANGED) do
39
+ text_fields_onchange
40
+ end
41
+ @patronymic.connect(SEL_CHANGED) do
42
+ text_fields_onchange
43
+ end
44
+ @telegram.connect(SEL_CHANGED) do
45
+ text_fields_onchange
46
+ end
47
+ @email.connect(SEL_CHANGED) do
48
+ text_fields_onchange
49
+ end
50
+ @phone.connect(SEL_CHANGED) do
51
+ text_fields_onchange
52
+ end
53
+ @git.connect(SEL_CHANGED) do
54
+ text_fields_onchange
55
+ end
56
+ end
57
+
58
+ # делегация валидации контроллеру
59
+ def text_fields_onchange
60
+ @controller.text_fields_onchange
61
+ end
62
+ end
data/lib/student.rb CHANGED
@@ -1,4 +1,4 @@
1
- require_relative '../student_ancestor.rb'
1
+ require_relative 'student_ancestor.rb'
2
2
  require_relative 'checking_attr_writer.rb'
3
3
 
4
4
  # класс студента с различными методами валидации его полей
data/lib/student_list.rb CHANGED
@@ -5,11 +5,24 @@ require_relative 'data_list_student_short.rb'
5
5
 
6
6
  # класс модели для работы с базой данных с использованием PostgreSQL
7
7
  class Student_list
8
- ACTION_LOGGER = Logger.new(File.join(__dir__, 'log', 'actions.log'))
8
+ ACTION_LOGGER = Logger.new(File.join(__dir__, '..', 'log', 'actions.log'))
9
9
  ACTION_LOGGER.level = Logger::INFO
10
- ERROR_LOGGER = Logger.new(File.join(__dir__, 'log', 'errors.log'))
10
+ ERROR_LOGGER = Logger.new(File.join(__dir__, '..', 'log', 'errors.log'))
11
11
  ERROR_LOGGER.level = Logger::ERROR
12
12
 
13
+
14
+ # конструктор с созданием соединения
15
+ def initialize(dbname: 'student_db', user: 'postgres', password: 'postgres', host: '127.0.0.1', port: 5432)
16
+ @connection = PG.connect(
17
+ dbname: dbname,
18
+ user: user,
19
+ password: password,
20
+ host: host,
21
+ port: port
22
+ )
23
+ end
24
+
25
+
13
26
  # получение объекта класса Student по ID
14
27
  def get_student(id)
15
28
  result = sql("SELECT * FROM student WHERE id = #{id}")
@@ -25,33 +38,35 @@ class Student_list
25
38
 
26
39
  # вставка объекта класса в таблицу (новый ID генерируется автоматически)
27
40
  def insert(student)
41
+ attributes = student.to_h
28
42
  sql("INSERT INTO student (first_name, last_name, patronymic, phone, telegram, email, git)
29
43
  VALUES
30
- ('#{student.to_h["first_name"]}',
31
- '#{student.to_h["last_name"]}',
32
- #{student.to_h["patronymic"].nil? ? "NULL" : "'#{student.to_h["patronymic"]}'"},
33
- #{student.to_h["phone"].nil? ? "NULL" : "'#{student.to_h["phone"]}'"},
34
- #{student.to_h["telegram"].nil? ? "NULL" : "'#{student.to_h["telegram"]}'"},
35
- #{student.to_h["email"].nil? ? "NULL" : "'#{student.to_h["email"]}'"},
36
- #{student.to_h["git"].nil? ? "NULL" : "'#{student.to_h["git"]}'"})")
37
-
38
- ACTION_LOGGER.info("Создание записи студента: #{student.to_h.inspect}")
44
+ ('#{attributes["first_name"]}',
45
+ '#{attributes["last_name"]}',
46
+ #{attributes["patronymic"].nil? ? "NULL" : "'#{attributes["patronymic"]}'"},
47
+ #{attributes["phone"].nil? ? "NULL" : "'#{attributes["phone"]}'"},
48
+ #{attributes["telegram"].nil? ? "NULL" : "'#{attributes["telegram"]}'"},
49
+ #{attributes["email"].nil? ? "NULL" : "'#{attributes["email"]}'"},
50
+ #{attributes["git"].nil? ? "NULL" : "'#{attributes["git"]}'"})")
51
+
52
+ ACTION_LOGGER.info("Создание записи студента: #{attributes.inspect}")
39
53
  end
40
54
 
41
55
  # замена объекта по ID
42
56
  def update(id, student)
57
+ attributes = student.to_h
43
58
  sql("UPDATE student
44
59
  SET
45
- first_name = '#{student.to_h["first_name"]}',
46
- last_name = '#{student.to_h["last_name"]}',
47
- patronymic = #{student.to_h["patronymic"].nil? ? "NULL" : "'#{student.to_h["patronymic"]}'"},
48
- phone = #{student.to_h["phone"].nil? ? "NULL" : "'#{student.to_h["phone"]}'"},
49
- telegram = #{student.to_h["telegram"].nil? ? "NULL" : "'#{student.to_h["telegram"]}'"},
50
- email = #{student.to_h["email"].nil? ? "NULL" : "'#{student.to_h["email"]}'"},
51
- git = #{student.to_h["git"].nil? ? "NULL" : "'#{student.to_h["git"]}'"}
60
+ first_name = '#{attributes["first_name"]}',
61
+ last_name = '#{attributes["last_name"]}',
62
+ patronymic = #{attributes["patronymic"].nil? ? "NULL" : "'#{attributes["patronymic"]}'"},
63
+ phone = #{attributes["phone"].nil? ? "NULL" : "'#{attributes["phone"]}'"},
64
+ telegram = #{attributes["telegram"].nil? ? "NULL" : "'#{attributes["telegram"]}'"},
65
+ email = #{attributes["email"].nil? ? "NULL" : "'#{attributes["email"]}'"},
66
+ git = #{attributes["git"].nil? ? "NULL" : "'#{attributes["git"]}'"}
52
67
  WHERE id = #{id}")
53
68
 
54
- ACTION_LOGGER.info("Изменение данных студента по ID = #{id}: #{student.to_h.inspect}")
69
+ ACTION_LOGGER.info("Изменение данных студента по ID = #{id}: #{attributes.inspect}")
55
70
  end
56
71
 
57
72
  # удаление объекта по ID
@@ -71,17 +86,10 @@ class Student_list
71
86
  private
72
87
 
73
88
  # подключение к базе данных и выполнение запроса
74
- def sql(command, dbname: 'student_db', user: 'postgres', password: 'postgres', host: '127.0.0.1', port: 5432)
89
+ def sql(command)
75
90
  result = []
76
91
  begin
77
- connection = PG.connect(
78
- dbname: dbname,
79
- user: user,
80
- password: password,
81
- host: host,
82
- port: port
83
- )
84
- result = connection.exec(command)
92
+ result = @connection.exec(command)
85
93
  rescue PG::ConnectionBad => e
86
94
  s = "Ошибка при попытке установить соединение с базой данных:\n#{e.message}"
87
95
  ERROR_LOGGER.error(s)
@@ -98,8 +106,6 @@ class Student_list
98
106
  s = "Ошибка на стороне клиента:\n#{e.message}"
99
107
  ERROR_LOGGER.error(s)
100
108
  raise s
101
- ensure
102
- connection&.close
103
109
  end
104
110
  result.to_a
105
111
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: student_gem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - s0195920
@@ -13,58 +13,60 @@ dependencies:
13
13
  name: pg
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - ">="
16
+ - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0'
18
+ version: 1.7.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - ">="
23
+ - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0'
25
+ version: 1.7.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: fox16
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - ">="
30
+ - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '0'
32
+ version: 1.6.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: 1.6.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: logger
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0'
46
+ version: 1.7.0
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">="
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0'
53
+ version: 1.7.0
54
54
  executables: []
55
55
  extensions: []
56
56
  extra_rdoc_files: []
57
57
  files:
58
58
  - lib/checking_attr_writer.rb
59
- - lib/controller.rb
60
59
  - lib/data_list.rb
61
60
  - lib/data_list_student_short.rb
62
61
  - lib/data_table.rb
62
+ - lib/main_controller.rb
63
+ - lib/main_window.rb
64
+ - lib/modal_controller.rb
65
+ - lib/modal_window.rb
63
66
  - lib/student.rb
64
67
  - lib/student_ancestor.rb
65
68
  - lib/student_list.rb
66
69
  - lib/student_short.rb
67
- - lib/view.rb
68
70
  - sql/ddl.sql
69
71
  - sql/dml.sql
70
72
  homepage: https://fpm-gitlab.kubsu.ru/design-patterns-tests/s0195920
data/lib/controller.rb DELETED
@@ -1,182 +0,0 @@
1
- require_relative '../student_list.rb'
2
-
3
- # общий интерфейс для всех контроллеров
4
- class Student_list_controller
5
-
6
- # общий для любого контроллера главного окна фрагмент конструктора
7
- def initialize(view)
8
- @view = view
9
- @student_list = Student_list.new
10
- end
11
-
12
- end
13
-
14
-
15
- # контроллер основного окна
16
- class Main_window_controller < Student_list_controller
17
-
18
- # константа числа строк на странице
19
- ROWS_ON_PAGE = 20
20
-
21
- # инициализация номера страницы
22
- def initialize(view)
23
- super(view)
24
- @page = 0
25
-
26
- @data_list_student_short = Data_list_student_short.new(nil)
27
- @data_list_student_short.subscribe(@view)
28
- end
29
-
30
-
31
- # реакция на выделение ячеек таблицы
32
- def table_rows_onselect(row)
33
- @data_list_student_short.select(row)
34
- end
35
-
36
- # реакция на снятие выделения
37
- def table_rows_ondeselect(row)
38
- @data_list_student_short.reject(row)
39
- end
40
-
41
-
42
- # реакция на нажатие кнопки вставки
43
- def insert_button_onclick
44
- modal_controller = Insertion_controller.new(@view)
45
- dialog = modal_controller.create
46
- if dialog.execute(PLACEMENT_OWNER) == 1
47
- student = Student.from_hash(modal_controller.student_attributes)
48
- begin
49
- result = @student_list.insert(student)
50
- rescue => e
51
- show_error(e)
52
- end
53
- refresh_data(@page)
54
- end
55
- end
56
-
57
- # реакция на нажатие кнопки замены
58
- def update_button_onclick
59
- modal_controller = Updation_controller.new(@view)
60
- id = @data_list_student_short.get_selected.first
61
- dialog = modal_controller.create
62
- modal_controller.fill_text_fields(id)
63
- if dialog.execute(PLACEMENT_OWNER) == 1
64
- student = Student.from_hash(modal_controller.student_attributes)
65
- begin
66
- @student_list.update(id, student)
67
- rescue => e
68
- show_error(e)
69
- end
70
- refresh_data(@page)
71
- end
72
-
73
- end
74
-
75
- # реакция на нажатие кнопки удаления
76
- def delete_button_onclick
77
- ids = @data_list_student_short.get_selected
78
- begin
79
- ids.each {|id| @student_list.delete(id)}
80
- rescue => e
81
- show_error(e)
82
- end
83
- refresh_data(@page)
84
- end
85
-
86
-
87
- # реакция на нажатие кнопки назад
88
- def previous_button_onclick
89
- @page -= 1
90
- refresh_data(@page)
91
- end
92
-
93
- # реакция на нажатие кнопки вперед
94
- def next_button_onclick
95
- @page += 1
96
- refresh_data(@page)
97
- end
98
-
99
-
100
- # обновление данных на странице
101
- def refresh_data(k)
102
- @page = max_page - 1 if @page >= max_page
103
- begin
104
- @student_list.get_k_n_student_short_list(k, ROWS_ON_PAGE, @data_list_student_short)
105
- max_pages = max_page
106
- rescue => e
107
- show_error(e)
108
- end
109
- @data_list_student_short.notify(ROWS_ON_PAGE)
110
-
111
- @view.page_count.text = "Страница: #{@page + 1} / #{max_pages}"
112
- @page == 0 ? @view.previous_button.disable : @view.previous_button.enable
113
- @page == max_pages - 1 ? @view.next_button.disable : @view.next_button.enable
114
- end
115
-
116
-
117
- def show_error(e)
118
- error_box = FXMessageBox.new(@view, "Ошибка", e.to_s, opts: MBOX_OK)
119
- error_box.execute
120
- end
121
-
122
-
123
- private
124
-
125
- # вычисление максимального числа страниц
126
- def max_page
127
- (@student_list.count.to_i + ROWS_ON_PAGE - 1) / ROWS_ON_PAGE
128
- end
129
- end
130
-
131
-
132
- # интерфейс контроллера модального окна для стратегии
133
- class Modal_window_controller < Student_list_controller
134
-
135
- def create
136
- @modal = ModalWindow.new(@view, self)
137
- end
138
-
139
- # общий для контроллера модального окна метод валидации
140
- def text_fields_onchange
141
- student = student_attributes
142
- [
143
- Student.valid_name?(student["last_name"]),
144
- Student.valid_name?(student["first_name"]),
145
- Student.valid_name?(student["patronymic"], true),
146
- Student.valid_telegram?(student["telegram"]),
147
- Student.valid_email?(student["email"]),
148
- Student.valid_phone?(student["phone"]),
149
- Student.valid_git?(student["git"])
150
- ].all? ? @modal.ok_button.enable : @modal.ok_button.disable
151
- end
152
-
153
- def student_attributes
154
- attributes = {}
155
- attributes["last_name"] = @modal.last_name.text.empty? ? nil : @modal.last_name.text
156
- attributes["first_name"] = @modal.first_name.text.empty? ? nil : @modal.first_name.text
157
- attributes["patronymic"] = @modal.patronymic.text.empty? ? nil : @modal.patronymic.text
158
- attributes["telegram"] = @modal.telegram.text.empty? ? nil : @modal.telegram.text
159
- attributes["email"] = @modal.email.text.empty? ? nil : @modal.email.text
160
- attributes["phone"] = @modal.phone.text.empty? ? nil : @modal.phone.text
161
- attributes["git"] = @modal.git.text.empty? ? nil : @modal.git.text
162
- attributes
163
- end
164
- end
165
-
166
- # контроллер модального окна при вставке
167
- class Insertion_controller < Modal_window_controller
168
- end
169
-
170
- # контроллер модального окна при изменении
171
- class Updation_controller < Modal_window_controller
172
- def fill_text_fields(id)
173
- student = @student_list.get_student(id).to_h
174
- @modal.last_name.text = student["last_name"]
175
- @modal.first_name.text = student["first_name"]
176
- @modal.patronymic.text = student["patronymic"]
177
- @modal.telegram.text = student["telegram"]
178
- @modal.email.text = student["email"]
179
- @modal.phone.text = student["phone"]
180
- @modal.git.text = student["git"]
181
- end
182
- end