starproxima_library 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 +9 -0
- data/Gemfile.lock +69 -0
- data/diagrams/add_uthor_seq.png +0 -0
- data/diagrams/add_uthor_seq.puml +42 -0
- data/diagrams/author.png +0 -0
- data/diagrams/author.puml +118 -0
- data/diagrams/core.png +0 -0
- data/diagrams/core.puml +103 -0
- data/diagrams/delete_author.png +0 -0
- data/diagrams/delete_author.puml +25 -0
- data/diagrams/edit_author.png +0 -0
- data/diagrams/edit_author.puml +48 -0
- data/diagrams/er.png +0 -0
- data/diagrams/er.puml +30 -0
- data/diagrams/publisher.png +0 -0
- data/diagrams/publisher.puml +118 -0
- data/diagrams/requirements.docx +0 -0
- data/diagrams/start.png +0 -0
- data/diagrams/start.puml +74 -0
- data/lib/author/author_db_data_source.rb +90 -0
- data/lib/author/controllers/author_controller.rb +57 -0
- data/lib/author/controllers/author_input_form_controller_create.rb +44 -0
- data/lib/author/controllers/author_input_form_controller_edit.rb +53 -0
- data/lib/author/controllers/author_list_controller.rb +107 -0
- data/lib/author/ui/author_input_form.rb +69 -0
- data/lib/author/ui/author_list_view.rb +170 -0
- data/lib/controllers/tab_students_controller.rb +43 -0
- data/lib/data_sources/book_db_data_source.rb +43 -0
- data/lib/data_sources/db_client.rb +34 -0
- data/lib/db_config/config.yaml +5 -0
- data/lib/db_config/library_config.yaml +5 -0
- data/lib/db_config/migrations/create_db.sql +3 -0
- data/lib/db_config/migrations/create_tables.sql +27 -0
- data/lib/db_config/mock_data/mock_data.sql +49 -0
- data/lib/logger.rb +27 -0
- data/lib/main.rb +6 -0
- data/lib/models/author.rb +32 -0
- data/lib/models/book.rb +31 -0
- data/lib/models/publisher.rb +37 -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/publisher/controllers/publisher_input_form_controller_create.rb +44 -0
- data/lib/publisher/controllers/publisher_input_form_controller_edit.rb +52 -0
- data/lib/publisher/controllers/publisher_list_controller.rb +99 -0
- data/lib/publisher/publisher_db_data_source.rb +63 -0
- data/lib/publisher/ui/publisher_input_form.rb +69 -0
- data/lib/publisher/ui/publisher_list_view.rb +168 -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 +60 -0
- data/lib/views/main_window.rb +32 -0
- data/lib/views/tab_students.rb +148 -0
- data/starproxima_library.gemspec +15 -0
- data/test/author_test.rb +51 -0
- data/test/book_test.rb +33 -0
- data/test/publisher_test.rb +39 -0
- data/test/state_notifier_test.rb +80 -0
- metadata +123 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/state_holders/list_state_notifier'
|
4
|
+
require_relative '../ui/publisher_input_form'
|
5
|
+
require_relative 'publisher_input_form_controller_create.rb'
|
6
|
+
require_relative 'publisher_input_form_controller_edit'
|
7
|
+
require_relative '../publisher_db_data_source'
|
8
|
+
require 'win32api'
|
9
|
+
|
10
|
+
class PublisherListController
|
11
|
+
|
12
|
+
attr_reader :state_notifier
|
13
|
+
def initialize(view)
|
14
|
+
@view = view
|
15
|
+
@state_notifier = ListStateNotifier.new
|
16
|
+
@state_notifier.add_listener(@view)
|
17
|
+
@publisher_rep = PublisherDBDataSource.new
|
18
|
+
|
19
|
+
@sort_columns = %w[PublisherID Name Email]
|
20
|
+
@sort_by = @sort_columns.first
|
21
|
+
|
22
|
+
@email_filter_columns = [nil, true, false]
|
23
|
+
@email_filter = @email_filter_columns.first
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
def on_view_created
|
29
|
+
# begin
|
30
|
+
# @student_rep = StudentRepository.new(DBSourceAdapter.new)
|
31
|
+
# rescue Mysql2::Error::ConnectionError
|
32
|
+
# on_db_conn_error
|
33
|
+
# end
|
34
|
+
end
|
35
|
+
|
36
|
+
def show_view
|
37
|
+
@view.create.show
|
38
|
+
end
|
39
|
+
|
40
|
+
def show_modal_add
|
41
|
+
controller = PublisherInputFormControllerCreate.new(self)
|
42
|
+
view = PublisherInputForm.new(controller)
|
43
|
+
controller.set_view(view)
|
44
|
+
view.create.show
|
45
|
+
end
|
46
|
+
|
47
|
+
def show_modal_edit(current_page, per_page, selected_row)
|
48
|
+
# item_num = (current_page - 1) * per_page + selected_row
|
49
|
+
|
50
|
+
item = @state_notifier.get(selected_row)
|
51
|
+
|
52
|
+
controller = PublisherInputFormControllerEdit.new(self, item)
|
53
|
+
view = PublisherInputForm.new(controller)
|
54
|
+
controller.set_view(view)
|
55
|
+
view.create.show
|
56
|
+
end
|
57
|
+
|
58
|
+
def delete_selected(current_page, per_page, selected_row)
|
59
|
+
begin
|
60
|
+
item = @state_notifier.get(selected_row)
|
61
|
+
@publisher_rep.delete(item.publisher_id)
|
62
|
+
@state_notifier.delete(item)
|
63
|
+
rescue
|
64
|
+
api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
|
65
|
+
api.call(0, "You cannot delete the author because he is associated with some book", "Error", 0)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def refresh_data(page, per_page)
|
70
|
+
# begin
|
71
|
+
# @data_list = @student_rep.paginated_short_students(page, per_page, @data_list)
|
72
|
+
# @view.update_student_count(@student_rep.student_count)
|
73
|
+
# rescue
|
74
|
+
# on_db_conn_error
|
75
|
+
# end
|
76
|
+
items = @publisher_rep.get_list(per_page, page, @sort_by, 'ASC', @email_filter)
|
77
|
+
@state_notifier.set_all(items)
|
78
|
+
@view.update_student_count(@publisher_rep.count)
|
79
|
+
end
|
80
|
+
|
81
|
+
def sort(page, per_page, sort_index)
|
82
|
+
@sort_by = @sort_columns[sort_index]
|
83
|
+
refresh_data(page, per_page)
|
84
|
+
end
|
85
|
+
|
86
|
+
def filter_email(page, per_page, filter_index)
|
87
|
+
@email_filter = @email_filter_columns[filter_index]
|
88
|
+
refresh_data(page, per_page)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def on_db_conn_error
|
95
|
+
api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
|
96
|
+
api.call(0, "No connection to DB", "Error", 0)
|
97
|
+
exit(false)
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'mysql2'
|
2
|
+
require_relative '../data_sources/db_client'
|
3
|
+
require_relative '../models/publisher'
|
4
|
+
|
5
|
+
class PublisherDBDataSource
|
6
|
+
def initialize
|
7
|
+
@client = DBClient.instance
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(publisher)
|
11
|
+
query = "INSERT INTO Publisher (Name, Email) VALUES ('#{publisher.name}', #{publisher.email.nil? ? 'NULL' : "'#{publisher.email}'"})"
|
12
|
+
@client.query(query)
|
13
|
+
publisher_id = @client.last_id
|
14
|
+
get(publisher_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def change(publisher)
|
18
|
+
query = "UPDATE Publisher SET Name='#{publisher.name}', Email=#{publisher.email.nil? ? 'NULL' : "'#{publisher.email}'"} WHERE PublisherID=#{publisher.publisher_id}"
|
19
|
+
@client.query(query)
|
20
|
+
get(publisher.publisher_id)
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(id)
|
24
|
+
query = "DELETE FROM Publisher WHERE PublisherID=#{id}"
|
25
|
+
@client.query(query)
|
26
|
+
end
|
27
|
+
|
28
|
+
def get(id)
|
29
|
+
query = "SELECT * FROM Publisher WHERE PublisherID=#{id}"
|
30
|
+
result = @client.query(query).first
|
31
|
+
if result
|
32
|
+
Publisher.new(result[:'PublisherID'], result[:'Name'], result[:'Email'])
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_list(page_size, page_num, sort_field, sort_direction, has_email = nil)
|
39
|
+
offset = (page_num - 1) * page_size
|
40
|
+
query = "SELECT * FROM Publisher"
|
41
|
+
|
42
|
+
if has_email == true
|
43
|
+
query += " WHERE Email IS NOT NULL"
|
44
|
+
elsif has_email == false
|
45
|
+
query += " WHERE Email IS NULL"
|
46
|
+
end
|
47
|
+
|
48
|
+
query += " ORDER BY #{sort_field} #{sort_direction} LIMIT #{page_size} OFFSET #{offset}"
|
49
|
+
|
50
|
+
results = @client.query(query)
|
51
|
+
publishers = []
|
52
|
+
results.each do |result|
|
53
|
+
publishers << Publisher.new(result[:'PublisherID'], result[:'Name'], result[:'Email'])
|
54
|
+
end
|
55
|
+
publishers
|
56
|
+
end
|
57
|
+
|
58
|
+
def count
|
59
|
+
query = "SELECT COUNT(*) FROM Publisher"
|
60
|
+
result = @client.query(query).first
|
61
|
+
result[:'COUNT(*)']
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'glimmer-dsl-libui'
|
4
|
+
require_relative '../controllers/publisher_input_form_controller_create.rb'
|
5
|
+
require './lib/models/author'
|
6
|
+
require 'win32api'
|
7
|
+
|
8
|
+
class PublisherInputForm
|
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 = [[:name, 'Название'], [:email, 'Почта']]
|
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,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'glimmer-dsl-libui'
|
4
|
+
require_relative '../controllers/publisher_list_controller'
|
5
|
+
require_relative 'publisher_input_form'
|
6
|
+
|
7
|
+
class PublisherListView
|
8
|
+
include Glimmer
|
9
|
+
|
10
|
+
PAGE_SIZE = 20
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@controller = PublisherListController.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(publishers)
|
33
|
+
@items = []
|
34
|
+
|
35
|
+
i = 0
|
36
|
+
item_num = 0
|
37
|
+
publishers.each do |publisher|
|
38
|
+
i += 1
|
39
|
+
item_num = ((@current_page - 1) * PAGE_SIZE) + i
|
40
|
+
@items << Struct.new(:№, :id, :название, :почта).new(item_num, publisher.publisher_id, publisher.name, publisher.email)
|
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_student_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_email(@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
|
+
},
|
102
|
+
per_page: PAGE_SIZE,
|
103
|
+
|
104
|
+
)
|
105
|
+
|
106
|
+
@pages = horizontal_box {
|
107
|
+
stretchy false
|
108
|
+
|
109
|
+
button("<") {
|
110
|
+
stretchy true
|
111
|
+
|
112
|
+
on_clicked do
|
113
|
+
@current_page = [@current_page - 1, 1].max
|
114
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
115
|
+
end
|
116
|
+
|
117
|
+
}
|
118
|
+
@page_label = label("...") { stretchy false }
|
119
|
+
button(">") {
|
120
|
+
stretchy true
|
121
|
+
|
122
|
+
on_clicked do
|
123
|
+
@current_page = [@current_page + 1, (@total_count / PAGE_SIZE.to_f).ceil].min
|
124
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
125
|
+
end
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
# Секция 3
|
131
|
+
vertical_box {
|
132
|
+
stretchy false
|
133
|
+
|
134
|
+
button('Добавить') {
|
135
|
+
stretchy false
|
136
|
+
|
137
|
+
on_clicked {
|
138
|
+
@controller.show_modal_add
|
139
|
+
}
|
140
|
+
}
|
141
|
+
button('Изменить') {
|
142
|
+
stretchy false
|
143
|
+
|
144
|
+
on_clicked {
|
145
|
+
@controller.show_modal_edit(@current_page, PAGE_SIZE, @table.selection) unless @table.selection.nil?
|
146
|
+
}
|
147
|
+
}
|
148
|
+
button('Удалить') {
|
149
|
+
stretchy false
|
150
|
+
|
151
|
+
on_clicked {
|
152
|
+
@controller.delete_selected(@current_page, PAGE_SIZE, @table.selection) unless @table.selection.nil?
|
153
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
154
|
+
}
|
155
|
+
}
|
156
|
+
button('Обновить') {
|
157
|
+
stretchy false
|
158
|
+
|
159
|
+
on_clicked {
|
160
|
+
@controller.refresh_data(@current_page, PAGE_SIZE)
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
165
|
+
on_create
|
166
|
+
root_container
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/repositories/data_sources/db_data_source'
|
4
|
+
require './lib/models/student'
|
5
|
+
require './lib/models/student_short'
|
6
|
+
require './lib/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 './lib/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('./lib/db_config/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
|