shnaider_carproj 0.1.0
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 +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +71 -0
- data/diagrams/add_owner.jpg +0 -0
- data/diagrams/delete_owner.jpg +0 -0
- data/diagrams/er.png +0 -0
- data/diagrams/owner.jpg +0 -0
- data/diagrams/owner.sai2 +0 -0
- data/diagrams/start.jpg +0 -0
- data/lib/car/controllers/publisher_input_form_controller_create.rb +44 -0
- data/lib/car/controllers/publisher_input_form_controller_edit.rb +52 -0
- data/lib/car/controllers/publisher_list_controller.rb +99 -0
- data/lib/car/tenant_db_data_source.rb +63 -0
- data/lib/car/ui/tenant_input_form.rb +69 -0
- data/lib/car/ui/tenant_list_view.rb +168 -0
- data/lib/controllers/tab_students_controller.rb +43 -0
- data/lib/data_sources/car_db_data_source.rb +43 -0
- data/lib/data_sources/db_client.rb +34 -0
- data/lib/db_config/carshering_config.yaml +5 -0
- data/lib/db_config/config.yaml +5 -0
- data/lib/db_config/migrations/create_db.sql +3 -0
- data/lib/db_config/migrations/create_tables.sql +29 -0
- data/lib/db_config/mock_data/mock_data.sql +55 -0
- data/lib/models/car.rb +31 -0
- data/lib/models/owner.rb +32 -0
- data/lib/models/student.rb +102 -0
- data/lib/models/student_base.rb +100 -0
- data/lib/models/student_short.rb +50 -0
- data/lib/models/tenant.rb +40 -0
- data/lib/owner/controllers/owner_input_form_controller_create.rb +44 -0
- data/lib/owner/controllers/owner_input_form_controller_edit.rb +53 -0
- data/lib/owner/controllers/owner_list_controller.rb +107 -0
- data/lib/owner/owner_db_data_source.rb +70 -0
- data/lib/owner/ui/owner_input_form.rb +69 -0
- data/lib/owner/ui/owner_list_view.rb +169 -0
- data/lib/repositories/adapters/db_source_adapter.rb +54 -0
- data/lib/repositories/adapters/file_source_adapter.rb +37 -0
- data/lib/repositories/containers/data_list.rb +74 -0
- data/lib/repositories/containers/data_list_student_short.rb +18 -0
- data/lib/repositories/containers/data_table.rb +35 -0
- data/lib/repositories/data_sources/db_data_source.rb +32 -0
- data/lib/repositories/data_sources/file_data_source.rb +75 -0
- data/lib/repositories/data_sources/transformers/data_transformer_base.rb +15 -0
- data/lib/repositories/data_sources/transformers/data_transformer_json.rb +16 -0
- data/lib/repositories/data_sources/transformers/data_transformer_yaml.rb +16 -0
- data/lib/repositories/student_repository.rb +32 -0
- data/lib/state_holders/list_state_notifier.rb +62 -0
- data/lib/tenant/controllers/tenant_input_form_controller_create.rb +44 -0
- data/lib/tenant/controllers/tenant_input_form_controller_edit.rb +53 -0
- data/lib/tenant/controllers/tenant_list_controller.rb +107 -0
- data/lib/tenant/tenant_db_data_source.rb +90 -0
- data/lib/tenant/ui/tenant_input_form.rb +69 -0
- data/lib/tenant/ui/tenant_list_view.rb +169 -0
- data/lib/views/main_window.rb +25 -0
- data/lib/views/tab_students.rb +148 -0
- data/requirements.docx +0 -0
- data/shnaider_carproj.gemspec +15 -0
- data/test/car_test.rb +33 -0
- data/test/logger.rb +27 -0
- data/test/main.rb +5 -0
- data/test/owner_test.rb +48 -0
- data/test/state_notifier_test.rb +80 -0
- data/test/tenant_test.rb +48 -0
- metadata +118 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './state_holders/list_state_notifier'
|
4
|
+
require_relative '../ui/owner_input_form'
|
5
|
+
require_relative 'owner_input_form_controller_create'
|
6
|
+
require_relative 'owner_input_form_controller_edit'
|
7
|
+
require_relative '../owner_db_data_source'
|
8
|
+
require 'win32api'
|
9
|
+
|
10
|
+
# Класс TenantListController отвечает за управление списком
|
11
|
+
# авторов, используя различные методы для обновления данных и
|
12
|
+
# взаимодействия с пользовательским интерфейсом.
|
13
|
+
class OwnerListController
|
14
|
+
|
15
|
+
attr_reader :state_notifier
|
16
|
+
|
17
|
+
def initialize(view)
|
18
|
+
LoggerHolder.instance.debug('OwnerListController: initialize')
|
19
|
+
@view = view
|
20
|
+
@state_notifier = ListStateNotifier.new
|
21
|
+
@state_notifier.add_listener(@view)
|
22
|
+
@owner_rep = OwnerDBDataSource.new
|
23
|
+
|
24
|
+
@sort_columns = %w[OwnerID FirstName LastName FatherName]
|
25
|
+
@sort_by = @sort_columns.first
|
26
|
+
|
27
|
+
@father_name_filter_columns = [nil, true, false]
|
28
|
+
@father_name_filter = @father_name_filter_columns.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_view_created
|
32
|
+
# begin
|
33
|
+
# @student_rep = StudentRepository.new(DBSourceAdapter.new)
|
34
|
+
# rescue Mysql2::Error::ConnectionError
|
35
|
+
# on_db_conn_error
|
36
|
+
# end
|
37
|
+
end
|
38
|
+
|
39
|
+
def show_view
|
40
|
+
@view.create.show
|
41
|
+
end
|
42
|
+
|
43
|
+
def show_modal_add
|
44
|
+
LoggerHolder.instance.debug('OwnerListController: show_modal_add')
|
45
|
+
controller = OwnerInputFormControllerCreate.new(self)
|
46
|
+
view = OwnerInputForm.new(controller)
|
47
|
+
controller.set_view(view)
|
48
|
+
view.create.show
|
49
|
+
end
|
50
|
+
|
51
|
+
# метод, который создает контроллер TenantInputFormControllerEdit, представление OwnerInputForm, устанавливает связи между ними и показывает модальное окно.
|
52
|
+
def show_modal_edit(current_page, per_page, selected_row)
|
53
|
+
LoggerHolder.instance.debug('OwnerListController: show_modal_edit')
|
54
|
+
# item_num = (current_page - 1) * per_page + selected_row
|
55
|
+
|
56
|
+
item = @state_notifier.get(selected_row)
|
57
|
+
|
58
|
+
controller = OwnerInputFormControllerEdit.new(self, item)
|
59
|
+
view = OwnerInputForm.new(controller)
|
60
|
+
controller.set_view(view)
|
61
|
+
view.create.show
|
62
|
+
end
|
63
|
+
|
64
|
+
# метод, который получает выбранный элемент из state_notifier, удаляет его из базы данных и из state_notifier
|
65
|
+
def delete_selected(current_page, per_page, selected_row)
|
66
|
+
LoggerHolder.instance.debug('OwnerListController: delete_selected')
|
67
|
+
begin
|
68
|
+
item = @state_notifier.get(selected_row)
|
69
|
+
@owner_rep.delete(item.owner_id)
|
70
|
+
@state_notifier.delete(item)
|
71
|
+
rescue
|
72
|
+
api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
|
73
|
+
api.call(0, "You cannot delete the owner because he is associated with some car", "Error", 0)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# етод, который получает список авторов из базы данных, устанавливает их в state_notifier и обновляет пользовательский интерфейс
|
78
|
+
def refresh_data(page, per_page)
|
79
|
+
# begin
|
80
|
+
# @data_list = @student_rep.paginated_short_students(page, per_page, @data_list)
|
81
|
+
# @view.update_student_count(@student_rep.student_count)
|
82
|
+
# rescue
|
83
|
+
# on_db_conn_error
|
84
|
+
# end
|
85
|
+
items = @owner_rep.get_list(per_page, page, @sort_by, 'ASC', @father_name_filter)
|
86
|
+
@state_notifier.set_all(items)
|
87
|
+
@view.update_count(@owner_rep.count)
|
88
|
+
end
|
89
|
+
|
90
|
+
def sort(page, per_page, sort_index)
|
91
|
+
@sort_by = @sort_columns[sort_index]
|
92
|
+
refresh_data(page, per_page)
|
93
|
+
end
|
94
|
+
|
95
|
+
def filter_father_name(page, per_page, filter_index)
|
96
|
+
@father_name_filter = @father_name_filter_columns[filter_index]
|
97
|
+
refresh_data(page, per_page)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def on_db_conn_error
|
103
|
+
api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
|
104
|
+
api.call(0, "No connection to DB", "Error", 0)
|
105
|
+
exit(false)
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'mysql2'
|
2
|
+
require_relative '../lib/data_sources/db_client'
|
3
|
+
|
4
|
+
class OwnerDBDataSource
|
5
|
+
def initialize
|
6
|
+
@client = DBClient.instance
|
7
|
+
end
|
8
|
+
|
9
|
+
# добавляет нового владельца в базу данных, возвращает созданную запись.
|
10
|
+
def add(owner)
|
11
|
+
query = "INSERT INTO Owner (FirstName, LastName, FatherName) VALUES ('#{owner.first_name}', '#{owner.last_name}', #{owner.father_name.nil? ? 'NULL' : "'#{owner.father_name}'"})"
|
12
|
+
@client.query(query)
|
13
|
+
owner_id = @client.last_id
|
14
|
+
get(owner_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
# изменяет данные об владельце в базе данных, возвращает измененную запись.
|
18
|
+
def change(owner)
|
19
|
+
query = "UPDATE Owner SET FirstName='#{owner.first_name}', LastName='#{owner.last_name}', FatherName=#{owner.father_name.nil? ? 'NULL' : "'#{owner.father_name}'"} WHERE OwnerID=#{owner.owner_id}"
|
20
|
+
@client.query(query)
|
21
|
+
get(owner.owner_id)
|
22
|
+
end
|
23
|
+
|
24
|
+
# удаляет запись о владельце из базы данных.
|
25
|
+
def delete(id)
|
26
|
+
query = "DELETE FROM Owner WHERE OwnerID=#{id}"
|
27
|
+
@client.query(query)
|
28
|
+
end
|
29
|
+
|
30
|
+
# возвращает запись об владельце по заданному id.
|
31
|
+
def get(id)
|
32
|
+
query = "SELECT * FROM Owner WHERE OwnerID=#{id}"
|
33
|
+
result = @client.query(query).first
|
34
|
+
if result
|
35
|
+
Owner.new(result[:'OwnerID'], result[:'FirstName'], result[:'LastName'], result[:'FatherName'])
|
36
|
+
else
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# возвращает список владельцов с учетом фильтра по наличию отчества и сортировки, позволяет задавать количество элементов на странице и номер страницы.
|
42
|
+
def get_list(page_size, page_num, sort_field, sort_direction, has_father_name = nil)
|
43
|
+
offset = (page_num - 1) * page_size
|
44
|
+
query = "SELECT * FROM Owner"
|
45
|
+
|
46
|
+
if has_father_name == true
|
47
|
+
query += " WHERE FatherName IS NOT NULL"
|
48
|
+
elsif has_father_name == false
|
49
|
+
query += " WHERE FatherName IS NULL"
|
50
|
+
end
|
51
|
+
|
52
|
+
query += " ORDER BY #{sort_field} #{sort_direction} LIMIT #{page_size} OFFSET #{offset}"
|
53
|
+
results = @client.query(query)
|
54
|
+
|
55
|
+
owners = []
|
56
|
+
results.each do |result|
|
57
|
+
owners << Owner.new(result[:'OwnerID'], result[:'FirstName'], result[:'LastName'], result[:'FatherName'])
|
58
|
+
end
|
59
|
+
|
60
|
+
owners
|
61
|
+
end
|
62
|
+
|
63
|
+
# возвращает количество записей об владельцах в базе данных.
|
64
|
+
def count
|
65
|
+
query = "SELECT COUNT(*) FROM Owner"
|
66
|
+
result = @client.query(query).first
|
67
|
+
|
68
|
+
result[:'COUNT(*)']
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'glimmer-dsl-libui'
|
4
|
+
require_relative '../controllers/owner_input_form_controller_create'
|
5
|
+
require './models/owner'
|
6
|
+
require 'win32api'
|
7
|
+
|
8
|
+
class OwnerInputForm
|
9
|
+
include Glimmer
|
10
|
+
|
11
|
+
def initialize(controller, existing_student = nil)
|
12
|
+
@item = existing_student.to_hash unless existing_student.nil?
|
13
|
+
@controller = controller
|
14
|
+
@entries = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_create
|
18
|
+
@controller.on_view_created
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
@root_container = window('Владелец', 300, 70) {
|
23
|
+
resizable false
|
24
|
+
|
25
|
+
vertical_box {
|
26
|
+
@student_form = form {
|
27
|
+
stretchy false
|
28
|
+
|
29
|
+
fields = [[:first_name, 'Имя владельца'], [:last_name, 'Фамилия владельца'], [:father_name, 'Отчество владельца']]
|
30
|
+
|
31
|
+
fields.each do |field|
|
32
|
+
@entries[field[0]] = entry {
|
33
|
+
label field[1]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
}
|
37
|
+
|
38
|
+
button('Сохранить') {
|
39
|
+
stretchy false
|
40
|
+
|
41
|
+
on_clicked {
|
42
|
+
values = @entries.transform_values { |v| v.text.force_encoding("utf-8").strip }
|
43
|
+
values.transform_values! { |v| v.empty? ? nil : v}
|
44
|
+
|
45
|
+
@controller.process_fields(values)
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
on_create
|
51
|
+
@root_container
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_value(field, value)
|
55
|
+
return unless @entries.include?(field)
|
56
|
+
|
57
|
+
@entries[field].text = value
|
58
|
+
end
|
59
|
+
|
60
|
+
def make_readonly(*fields)
|
61
|
+
fields.each do |field|
|
62
|
+
@entries[field].read_only = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def close
|
67
|
+
@root_container.destroy
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'glimmer-dsl-libui'
|
4
|
+
require_relative '../controllers/owner_list_controller'
|
5
|
+
require_relative 'owner_input_form'
|
6
|
+
|
7
|
+
class OwnerListView
|
8
|
+
include Glimmer
|
9
|
+
|
10
|
+
PAGE_SIZE = 20
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@controller = OwnerListController.new(self)
|
14
|
+
@current_page = 1
|
15
|
+
@total_count = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_create
|
19
|
+
@controller.on_view_created
|
20
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Метод наблюдателя datalist
|
24
|
+
# def on_datalist_changed(new_table)
|
25
|
+
# arr = new_table.to_2d_array
|
26
|
+
# arr.map do |row|
|
27
|
+
# row[3] = [row[3][:value], contact_color(row[3][:type])] unless row[3].nil?
|
28
|
+
# end
|
29
|
+
# @table.model_array = arr
|
30
|
+
# end
|
31
|
+
|
32
|
+
def update(owners)
|
33
|
+
@items = []
|
34
|
+
|
35
|
+
i = 0
|
36
|
+
item_num = 0
|
37
|
+
owners.each do |owner|
|
38
|
+
i += 1
|
39
|
+
item_num = ((@current_page - 1) * PAGE_SIZE) + i
|
40
|
+
@items << Struct.new(:№, :id, :имя_владельца, :фамилия_владельца, :отчество_владельца).new(item_num, owner.owner_id, owner.first_name, owner.last_name, owner.father_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
@table.model_array = @items
|
44
|
+
@page_label.text = "#{@current_page} / #{(@total_count / PAGE_SIZE.to_f).ceil}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_count(new_cnt)
|
48
|
+
@total_count = new_cnt
|
49
|
+
@page_label.text = "#{@current_page} / #{(@total_count / PAGE_SIZE.to_f).ceil}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def create
|
53
|
+
|
54
|
+
root_container = horizontal_box {
|
55
|
+
# Секция 1
|
56
|
+
vertical_box {
|
57
|
+
stretchy false
|
58
|
+
|
59
|
+
vertical_box {
|
60
|
+
stretchy false
|
61
|
+
|
62
|
+
label {
|
63
|
+
text 'Отчество'
|
64
|
+
}
|
65
|
+
combobox { |c|
|
66
|
+
items ['Не важно','Есть','Нет']
|
67
|
+
selected 0
|
68
|
+
on_selected do
|
69
|
+
@controller.filter_father_name(@current_page, PAGE_SIZE, c.selected)
|
70
|
+
end
|
71
|
+
}
|
72
|
+
|
73
|
+
label {
|
74
|
+
text 'Сортировка'
|
75
|
+
}
|
76
|
+
combobox { |c|
|
77
|
+
items ['ID','Имя владельца','Фамилия владельца', 'Отчество владельца']
|
78
|
+
selected 0
|
79
|
+
on_selected do
|
80
|
+
@controller.sort(@current_page, PAGE_SIZE, c.selected)
|
81
|
+
end
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
|
86
|
+
}
|
87
|
+
|
88
|
+
# Секция 2
|
89
|
+
vertical_box {
|
90
|
+
@table = refined_table(
|
91
|
+
table_editable: false,
|
92
|
+
filter: lambda do |row_hash, query|
|
93
|
+
utf8_query = query.force_encoding("utf-8")
|
94
|
+
row_hash['Имя владельца'].include?(utf8_query)
|
95
|
+
end,
|
96
|
+
table_columns: {
|
97
|
+
'№' => :text,
|
98
|
+
'ID' => :text,
|
99
|
+
'Имя владельца' => :text,
|
100
|
+
'Фамилия владельца' => :text,
|
101
|
+
'Отчество владельца' => :text,
|
102
|
+
},
|
103
|
+
per_page: PAGE_SIZE,
|
104
|
+
|
105
|
+
)
|
106
|
+
|
107
|
+
@pages = horizontal_box {
|
108
|
+
stretchy false
|
109
|
+
|
110
|
+
button("<") {
|
111
|
+
stretchy true
|
112
|
+
|
113
|
+
on_clicked do
|
114
|
+
@current_page = [@current_page - 1, 1].max
|
115
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
116
|
+
end
|
117
|
+
|
118
|
+
}
|
119
|
+
@page_label = label("...") { stretchy false }
|
120
|
+
button(">") {
|
121
|
+
stretchy true
|
122
|
+
|
123
|
+
on_clicked do
|
124
|
+
@current_page = [@current_page + 1, (@total_count / PAGE_SIZE.to_f).ceil].min
|
125
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
126
|
+
end
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
# Секция 3
|
132
|
+
vertical_box {
|
133
|
+
stretchy false
|
134
|
+
|
135
|
+
button('Добавить') {
|
136
|
+
stretchy false
|
137
|
+
|
138
|
+
on_clicked {
|
139
|
+
@controller.show_modal_add
|
140
|
+
}
|
141
|
+
}
|
142
|
+
button('Изменить') {
|
143
|
+
stretchy false
|
144
|
+
|
145
|
+
on_clicked {
|
146
|
+
@controller.show_modal_edit(@current_page, PAGE_SIZE, @table.selection) unless @table.selection.nil?
|
147
|
+
}
|
148
|
+
}
|
149
|
+
button('Удалить') {
|
150
|
+
stretchy false
|
151
|
+
|
152
|
+
on_clicked {
|
153
|
+
@controller.delete_selected(@current_page, PAGE_SIZE, @table.selection) unless @table.selection.nil?
|
154
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
155
|
+
}
|
156
|
+
}
|
157
|
+
button('Обновить') {
|
158
|
+
stretchy false
|
159
|
+
|
160
|
+
on_clicked {
|
161
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
165
|
+
}
|
166
|
+
on_create
|
167
|
+
root_container
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './repositories/data_sources/db_data_source'
|
4
|
+
require './models/student'
|
5
|
+
require './models/student_short'
|
6
|
+
require './repositories/containers/data_list_student_short'
|
7
|
+
|
8
|
+
class DBSourceAdapter
|
9
|
+
def initialize
|
10
|
+
@db = DBDataSource.instance
|
11
|
+
end
|
12
|
+
|
13
|
+
def student_by_id(student_id)
|
14
|
+
hash = @db.prepare_exec('SELECT * FROM student WHERE id = ?', student_id).first
|
15
|
+
return nil if hash.nil?
|
16
|
+
|
17
|
+
Student.from_hash(hash)
|
18
|
+
end
|
19
|
+
|
20
|
+
def paginated_short_students(page, count, existing_data_list = nil)
|
21
|
+
offset = (page - 1) * count
|
22
|
+
students = @db.prepare_exec('SELECT * FROM student LIMIT ?, ?', offset, count)
|
23
|
+
slice = students.map { |h| StudentShort.from_student(Student.from_hash(h)) }
|
24
|
+
return DataListStudentShort.new(slice) if existing_data_list.nil?
|
25
|
+
|
26
|
+
existing_data_list.replace_objects(slice)
|
27
|
+
existing_data_list
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_student(student)
|
31
|
+
template = 'INSERT INTO student(last_name, first_name, father_name, phone, telegram, email, git) VALUES (?, ?, ?, ?, ?, ?, ?)'
|
32
|
+
@db.prepare_exec(template, *student_fields(student))
|
33
|
+
@db.query('SELECT LAST_INSERT_ID()').first.first[1]
|
34
|
+
end
|
35
|
+
|
36
|
+
def replace_student(student_id, student)
|
37
|
+
template = 'UPDATE student SET last_name=?, first_name=?, father_name=?, phone=?, telegram=?, email=?, git=? WHERE id=?'
|
38
|
+
@db.prepare_exec(template, *student_fields(student), student_id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove_student(student_id)
|
42
|
+
@db.prepare_exec('DELETE FROM student WHERE id = ?', student_id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def student_count
|
46
|
+
@db.query('SELECT COUNT(id) FROM student').first.first[1]
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def student_fields(student)
|
52
|
+
[student.last_name, student.first_name, student.father_name, student.phone, student.telegram, student.email, student.git]
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FileSourceAdapter
|
4
|
+
def initialize(data_transformer, file_path)
|
5
|
+
@file_path = file_path
|
6
|
+
@file_source = FileDataSource.new(data_transformer)
|
7
|
+
@file_source.load_from_file(file_path)
|
8
|
+
end
|
9
|
+
|
10
|
+
def student_by_id(student_id)
|
11
|
+
@file_source.student_by_id(student_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def paginated_short_students(page, count, existing_data_list = nil)
|
15
|
+
@file_source.paginated_short_students(page, count, existing_data_list)
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_student(student)
|
19
|
+
added_id = @file_source.add_student(student)
|
20
|
+
@file_source.save_to_file(@file_path)
|
21
|
+
added_id
|
22
|
+
end
|
23
|
+
|
24
|
+
def replace_student(student_id, student)
|
25
|
+
@file_source.replace_student(student_id, student)
|
26
|
+
@file_source.save_to_file(@file_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_student(student_id)
|
30
|
+
@file_source.remove_student(student_id)
|
31
|
+
@file_source.save_to_file(@file_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def student_count
|
35
|
+
@file_source.student_count
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './repositories/containers/data_table'
|
4
|
+
|
5
|
+
class DataList
|
6
|
+
# Это "абстрактный" класс
|
7
|
+
private_class_method :new
|
8
|
+
|
9
|
+
attr_writer :objects
|
10
|
+
|
11
|
+
# Конструктор, принимает массив любых объектов
|
12
|
+
def initialize(objects)
|
13
|
+
self.objects = objects
|
14
|
+
@listeners = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_listener(listener)
|
18
|
+
@listeners << listener
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove_listener(listener)
|
22
|
+
@listeners.delete(listener)
|
23
|
+
end
|
24
|
+
|
25
|
+
def notify
|
26
|
+
@listeners.each { |lst| lst.on_datalist_changed(data_table) }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Выбрать элемент по номеру
|
30
|
+
def select_element(number)
|
31
|
+
self.selected_num = number < objects.size ? number : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def selected_id
|
35
|
+
objects[selected_num].id
|
36
|
+
end
|
37
|
+
|
38
|
+
# Получить DataTable со всеми элементами.
|
39
|
+
def data_table
|
40
|
+
result = []
|
41
|
+
counter = 0
|
42
|
+
objects.each do |obj|
|
43
|
+
row = []
|
44
|
+
row << counter
|
45
|
+
row.push(*table_fields(obj))
|
46
|
+
result << row
|
47
|
+
counter += 1
|
48
|
+
end
|
49
|
+
DataTable.new(result)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Добавить элементы в конец списка
|
53
|
+
def replace_objects(objects)
|
54
|
+
self.objects = objects.dup
|
55
|
+
notify
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
# Список значений полей для DataTable. Переопределить в наследниках
|
61
|
+
def table_fields(_obj)
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Имена атрибутов объектов по порядку. Переопределить в наследниках
|
66
|
+
def column_names
|
67
|
+
[]
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
attr_reader :objects
|
73
|
+
attr_accessor :selected_num
|
74
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'data_list'
|
4
|
+
|
5
|
+
class DataListStudentShort < DataList
|
6
|
+
# Делаем приватный new предка публичным
|
7
|
+
public_class_method :new
|
8
|
+
|
9
|
+
def column_names
|
10
|
+
['Фамилия И. О.', 'Гит', 'Контакт']
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def table_fields(obj)
|
16
|
+
[obj.last_name_and_initials, obj.git, obj.contact]
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DataTable
|
4
|
+
attr_reader :rows_count, :cols_count
|
5
|
+
|
6
|
+
# Конструктор, принимает 2D Array
|
7
|
+
def initialize(table)
|
8
|
+
self.rows_count = table.size
|
9
|
+
max_cols = 0
|
10
|
+
table.each { |row| max_cols = row.size if row.size > max_cols }
|
11
|
+
self.cols_count = max_cols
|
12
|
+
self.table = table
|
13
|
+
end
|
14
|
+
|
15
|
+
# Получить значение в ячейке [row, col]
|
16
|
+
def get_item(row, col)
|
17
|
+
return nil if row >= rows_count
|
18
|
+
return nil if col >= cols_count
|
19
|
+
|
20
|
+
table[row][col].dup
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_2d_array
|
24
|
+
table.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"DataTable (#{rows_count}x#{cols_count})"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_accessor :table
|
34
|
+
attr_writer :rows_count, :cols_count
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mysql2'
|
4
|
+
|
5
|
+
class DBDataSource
|
6
|
+
private_class_method :new
|
7
|
+
@instance_mutex = Mutex.new
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
db_config = YAML.load_file('./db_config/carshering_config.yaml').transform_keys(&:to_sym)
|
11
|
+
@client = Mysql2::Client.new(db_config)
|
12
|
+
@client.query_options.merge!(symbolize_keys: true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.instance
|
16
|
+
return @instance if @instance
|
17
|
+
|
18
|
+
@instance_mutex.synchronize do
|
19
|
+
@instance ||= new
|
20
|
+
end
|
21
|
+
|
22
|
+
@instance
|
23
|
+
end
|
24
|
+
|
25
|
+
def prepare_exec(statement, *params)
|
26
|
+
@client.prepare(statement).execute(*params)
|
27
|
+
end
|
28
|
+
|
29
|
+
def query(statement)
|
30
|
+
@client.query(statement)
|
31
|
+
end
|
32
|
+
end
|