starproxima_library 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/models/student'
|
4
|
+
require './lib/models/student_short'
|
5
|
+
require './lib/repositories/containers/data_list_student_short'
|
6
|
+
|
7
|
+
class FileDataSource
|
8
|
+
attr_writer :data_transformer
|
9
|
+
|
10
|
+
def initialize(data_transformer)
|
11
|
+
self.students = []
|
12
|
+
self.seq_id = 1
|
13
|
+
self.data_transformer = data_transformer
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_from_file(file_path)
|
17
|
+
hash_list = data_transformer.str_to_hash_list(File.read(file_path))
|
18
|
+
self.students = hash_list.map { |h| Student.from_hash(h) }
|
19
|
+
update_seq_id
|
20
|
+
end
|
21
|
+
|
22
|
+
def save_to_file(file_path)
|
23
|
+
hash_list = students.map(&:to_hash)
|
24
|
+
File.write(file_path, data_transformer.hash_list_to_str(hash_list))
|
25
|
+
end
|
26
|
+
|
27
|
+
def student_by_id(student_id)
|
28
|
+
students.detect { |s| s.id == student_id }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Получить page по счету count элементов (страница начинается с 1)
|
32
|
+
def paginated_short_students(page, count, existing_data_list = nil)
|
33
|
+
offset = (page - 1) * count
|
34
|
+
slice = students[offset, count].map { |s| StudentShort.from_student(s) }
|
35
|
+
|
36
|
+
return DataListStudentShort.new(slice) if existing_data_list.nil?
|
37
|
+
|
38
|
+
existing_data_list.replace_objects(slice)
|
39
|
+
existing_data_list
|
40
|
+
end
|
41
|
+
|
42
|
+
def sorted
|
43
|
+
students.sort_by(&:last_name_and_initials)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_student(student)
|
47
|
+
student.id = seq_id
|
48
|
+
students << student
|
49
|
+
self.seq_id += 1
|
50
|
+
student.id
|
51
|
+
end
|
52
|
+
|
53
|
+
def replace_student(student_id, student)
|
54
|
+
idx = students.find_index { |s| s.id == student_id }
|
55
|
+
students[idx] = student
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove_student(student_id)
|
59
|
+
students.reject! { |s| s.id == student_id }
|
60
|
+
end
|
61
|
+
|
62
|
+
def student_count
|
63
|
+
students.count
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# Метод для актуализации seq_id
|
69
|
+
def update_seq_id
|
70
|
+
self.seq_id = students.max_by(&:id).id + 1
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_reader :data_transformer
|
74
|
+
attr_accessor :students, :seq_id
|
75
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DataTransformerBase
|
4
|
+
private_class_method :new
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def str_to_hash_list(str)
|
9
|
+
raise NotImplementedError('Should be implemented in child')
|
10
|
+
end
|
11
|
+
|
12
|
+
def hash_list_to_str(hash_list)
|
13
|
+
raise NotImplementedError('Should be implemented in child')
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'data_transformer_base'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
class DataTransformerJSON < DataTransformerBase
|
7
|
+
public_class_method :new
|
8
|
+
|
9
|
+
def str_to_hash_list(str)
|
10
|
+
JSON.parse(str, { symbolize_names: true })
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash_list_to_str(hash_list)
|
14
|
+
JSON.pretty_generate(hash_list)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'data_transformer_base'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
class DataTransformerYAML < DataTransformerBase
|
7
|
+
public_class_method :new
|
8
|
+
|
9
|
+
def str_to_hash_list(str)
|
10
|
+
YAML.safe_load(str).map { |h| h.transform_keys(&:to_sym) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash_list_to_str(hash_list)
|
14
|
+
hash_list.map { |h| h.transform_keys(&:to_s) }.to_yaml
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class StudentRepository
|
4
|
+
def initialize(data_source_adapter)
|
5
|
+
@data_source_adapter = data_source_adapter
|
6
|
+
end
|
7
|
+
|
8
|
+
def student_by_id(student_id)
|
9
|
+
@data_source_adapter.student_by_id(student_id)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Получить page по счету count элементов (страница начинается с 1)
|
13
|
+
def paginated_short_students(page, count, existing_data_list = nil)
|
14
|
+
@data_source_adapter.paginated_short_students(page, count, existing_data_list)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_student(student)
|
18
|
+
@data_source_adapter.add_student(student)
|
19
|
+
end
|
20
|
+
|
21
|
+
def replace_student(student_id, student)
|
22
|
+
@data_source_adapter.replace_student(student_id, student)
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove_student(student_id)
|
26
|
+
@data_source_adapter.remove_student(student_id)
|
27
|
+
end
|
28
|
+
|
29
|
+
def student_count
|
30
|
+
@data_source_adapter.student_count
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class ListStateNotifier
|
2
|
+
attr_reader :items
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@items = []
|
6
|
+
@listeners = []
|
7
|
+
end
|
8
|
+
|
9
|
+
# устанавливает новое значение для items и уведомляет всех слушателей.
|
10
|
+
def set_all(objects)
|
11
|
+
LoggerHolder.instance.debug('ListStateNotifier: set_all')
|
12
|
+
@items = objects
|
13
|
+
notify_listeners
|
14
|
+
end
|
15
|
+
|
16
|
+
# добавляет объект в массив items и уведомляет всех слушателей.
|
17
|
+
def add(object)
|
18
|
+
LoggerHolder.instance.debug('ListStateNotifier: add')
|
19
|
+
@items << object
|
20
|
+
notify_listeners
|
21
|
+
end
|
22
|
+
# возвращает объект из массива items по индексу.
|
23
|
+
def get(number)
|
24
|
+
LoggerHolder.instance.debug('ListStateNotifier: get')
|
25
|
+
@items[number]
|
26
|
+
end
|
27
|
+
|
28
|
+
# удаляет объект из массива items и уведомляет всех слушателей.
|
29
|
+
def delete(object)
|
30
|
+
LoggerHolder.instance.debug('ListStateNotifier: delete')
|
31
|
+
@items.delete(object)
|
32
|
+
notify_listeners
|
33
|
+
end
|
34
|
+
|
35
|
+
# заменяет объект в массиве items на новый объект и уведомляет всех слушателей.
|
36
|
+
def replace(object, new_object)
|
37
|
+
LoggerHolder.instance.debug('ListStateNotifier: replace')
|
38
|
+
index = @items.index(object)
|
39
|
+
@items[index] = new_object
|
40
|
+
notify_listeners
|
41
|
+
end
|
42
|
+
# добавляет нового слушателя в массив listeners.
|
43
|
+
def add_listener(listener)
|
44
|
+
LoggerHolder.instance.debug('ListStateNotifier: add_listener')
|
45
|
+
@listeners << listener
|
46
|
+
end
|
47
|
+
# удаляет слушателя из массива listeners.
|
48
|
+
def delete_listener(listener)
|
49
|
+
LoggerHolder.instance.debug('ListStateNotifier: delete_listener')
|
50
|
+
@listeners.delete(listener)
|
51
|
+
end
|
52
|
+
|
53
|
+
# уведомляет всех слушателей о изменении массива items.
|
54
|
+
def notify_listeners
|
55
|
+
LoggerHolder.instance.debug('notify_listeners')
|
56
|
+
@listeners.each do |listener|
|
57
|
+
listener.update(@items)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'glimmer-dsl-libui'
|
4
|
+
require_relative 'tab_students'
|
5
|
+
require './lib/author/ui/author_list_view'
|
6
|
+
require './lib/publisher/ui/publisher_list_view'
|
7
|
+
|
8
|
+
class MainWindow
|
9
|
+
include Glimmer
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@view_tab_students = TabStudentsView.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
window('Библиотека', 1000, 600) {
|
17
|
+
tab {
|
18
|
+
tab_item('Авторы') {
|
19
|
+
AuthorListView.new.create
|
20
|
+
}
|
21
|
+
tab_item('Издатели') {
|
22
|
+
PublisherListView.new.create
|
23
|
+
}
|
24
|
+
|
25
|
+
|
26
|
+
# tab_item('Студенты') {
|
27
|
+
# @view_tab_students.create
|
28
|
+
# }
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'glimmer-dsl-libui'
|
4
|
+
require './lib/controllers/tab_students_controller'
|
5
|
+
|
6
|
+
class TabStudentsView
|
7
|
+
include Glimmer
|
8
|
+
|
9
|
+
ITEMS_PER_PAGE = 20
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@controller = TabStudentsController.new(self)
|
13
|
+
@current_page = 1
|
14
|
+
@total_count = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_create
|
18
|
+
@controller.on_view_created
|
19
|
+
@controller.refresh_data(@current_page, ITEMS_PER_PAGE)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Метод наблюдателя datalist
|
23
|
+
def on_datalist_changed(new_table)
|
24
|
+
arr = new_table.to_2d_array
|
25
|
+
arr.map { |row| row[3] = [row[3][:value], contact_color(row[3][:type])] }
|
26
|
+
@table.model_array = arr
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_student_count(new_cnt)
|
30
|
+
@total_count = new_cnt
|
31
|
+
@page_label.text = "#{@current_page} / #{(@total_count / ITEMS_PER_PAGE.to_f).ceil}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def contact_color(type)
|
35
|
+
case type
|
36
|
+
when 'telegram'
|
37
|
+
'#00ADB5'
|
38
|
+
when 'email'
|
39
|
+
'#F08A5D'
|
40
|
+
when 'phone'
|
41
|
+
'#B83B5E'
|
42
|
+
else
|
43
|
+
'#000000'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create
|
48
|
+
root_container = horizontal_box {
|
49
|
+
# Секция 1
|
50
|
+
vertical_box {
|
51
|
+
stretchy false
|
52
|
+
|
53
|
+
form {
|
54
|
+
stretchy false
|
55
|
+
|
56
|
+
@filter_last_name_initials = entry {
|
57
|
+
label 'Фамилия И. О.'
|
58
|
+
}
|
59
|
+
|
60
|
+
@filters = {}
|
61
|
+
fields = [[:git, 'Гит'], [:email, 'Почта'], [:phone, 'Телефон'], [:telegram, 'Телеграм']]
|
62
|
+
|
63
|
+
fields.each do |field|
|
64
|
+
@filters[field[0]] = {}
|
65
|
+
|
66
|
+
@filters[field[0]][:combobox] = combobox {
|
67
|
+
label "#{field[1]} имеется?"
|
68
|
+
items ['Не важно', 'Есть', 'Нет']
|
69
|
+
selected 0
|
70
|
+
|
71
|
+
on_selected do
|
72
|
+
if @filters[field[0]][:combobox].selected == 1
|
73
|
+
@filters[field[0]][:entry].read_only = false
|
74
|
+
else
|
75
|
+
@filters[field[0]][:entry].text = ''
|
76
|
+
@filters[field[0]][:entry].read_only = true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
}
|
80
|
+
|
81
|
+
@filters[field[0]][:entry] = entry {
|
82
|
+
label field[1]
|
83
|
+
read_only true
|
84
|
+
}
|
85
|
+
end
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
# Секция 2
|
90
|
+
vertical_box {
|
91
|
+
@table = refined_table(
|
92
|
+
table_editable: false,
|
93
|
+
filter: lambda do |row_hash, query|
|
94
|
+
utf8_query = query.force_encoding("utf-8")
|
95
|
+
row_hash['Фамилия И. О'].include?(utf8_query)
|
96
|
+
end,
|
97
|
+
table_columns: {
|
98
|
+
'#' => :text,
|
99
|
+
'Фамилия И. О' => :text,
|
100
|
+
'Гит' => :text,
|
101
|
+
'Контакт' => :text_color
|
102
|
+
}
|
103
|
+
)
|
104
|
+
|
105
|
+
@pages = horizontal_box {
|
106
|
+
stretchy false
|
107
|
+
|
108
|
+
button("<") {
|
109
|
+
stretchy true
|
110
|
+
|
111
|
+
on_clicked do
|
112
|
+
@current_page = [@current_page - 1, 1].max
|
113
|
+
@controller.refresh_data(@current_page, ITEMS_PER_PAGE)
|
114
|
+
end
|
115
|
+
|
116
|
+
}
|
117
|
+
@page_label = label("...") { stretchy false }
|
118
|
+
button(">") {
|
119
|
+
stretchy true
|
120
|
+
|
121
|
+
on_clicked do
|
122
|
+
@current_page = [@current_page + 1, (@total_count / ITEMS_PER_PAGE.to_f).ceil].min
|
123
|
+
@controller.refresh_data(@current_page, ITEMS_PER_PAGE)
|
124
|
+
end
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
# Секция 3
|
130
|
+
vertical_box {
|
131
|
+
stretchy false
|
132
|
+
|
133
|
+
button('Добавить') { stretchy false }
|
134
|
+
button('Изменить') { stretchy false }
|
135
|
+
button('Удалить') { stretchy false }
|
136
|
+
button('Обновить') {
|
137
|
+
stretchy false
|
138
|
+
|
139
|
+
on_clicked {
|
140
|
+
@controller.refresh_data(@current_page, ITEMS_PER_PAGE)
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
}
|
145
|
+
on_create
|
146
|
+
root_container
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = 'starproxima_library'
|
3
|
+
spec.version = '0.1.0'
|
4
|
+
spec.authors = ['StarProxima']
|
5
|
+
spec.email = 'starproxima@yandex.ru'
|
6
|
+
spec.summary = 'Starproxima library app'
|
7
|
+
spec.description = 'Description Starproxima library app'
|
8
|
+
spec.homepage = 'https://github.com/StarProxima/library_system'
|
9
|
+
spec.license = 'MIT'
|
10
|
+
|
11
|
+
spec.files = Dir.glob("**/*")
|
12
|
+
spec.require_paths = ['lib']
|
13
|
+
|
14
|
+
spec.add_dependency 'win32api'
|
15
|
+
end
|
data/test/author_test.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require './lib/models/author'
|
3
|
+
class TestAuthor < Minitest::Test
|
4
|
+
def test_initialize_sets_attributes
|
5
|
+
author = Author.new(1, 'John', 'Doe', 'Smith')
|
6
|
+
|
7
|
+
assert_equal 1, author.author_id
|
8
|
+
assert_equal 'John', author.first_name
|
9
|
+
assert_equal 'Doe', author.last_name
|
10
|
+
assert_equal 'Smith', author.father_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_initialize_raises_error_if_author_id_is_nil
|
14
|
+
assert_raises ArgumentError do
|
15
|
+
Author.new(nil, 'John', 'Doe')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_initialize_raises_error_if_first_name_is_nil
|
20
|
+
assert_raises ArgumentError do
|
21
|
+
Author.new(1, nil, 'Doe')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_initialize_raises_error_if_last_name_is_nil
|
26
|
+
assert_raises ArgumentError do
|
27
|
+
Author.new(1, 'John', nil)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_initialize_raises_error_if_first_name_exceeds_50_characters
|
32
|
+
long_name = 'a' * 51
|
33
|
+
assert_raises ArgumentError do
|
34
|
+
Author.new(1, long_name, 'Doe')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_initialize_raises_error_if_last_name_exceeds_50_characters
|
39
|
+
long_name = 'a' * 51
|
40
|
+
assert_raises ArgumentError do
|
41
|
+
Author.new(1, 'John', long_name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_initialize_raises_error_if_father_name_exceeds_50_characters
|
46
|
+
long_name = 'a' * 51
|
47
|
+
assert_raises ArgumentError do
|
48
|
+
Author.new(1, 'John', 'Doe', long_name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/test/book_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require './lib/models/book'
|
3
|
+
class TestBook < Minitest::Test
|
4
|
+
def test_initialize_raises_error_with_null_book_id
|
5
|
+
assert_raises(ArgumentError) { Book.new(nil, 'title', 1, 1) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_initialize_raises_error_with_null_title
|
9
|
+
assert_raises(ArgumentError) { Book.new(1, nil, 1, 1) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_initialize_raises_error_with_null_author_id
|
13
|
+
assert_raises(ArgumentError) { Book.new(1, 'title', nil, 1) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_initialize_raises_error_with_null_publisher_id
|
17
|
+
assert_raises(ArgumentError) { Book.new(1, 'title', 1, nil) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_initialize_raises_error_with_long_title
|
21
|
+
long_title = 'a' * 256
|
22
|
+
assert_raises(ArgumentError) { Book.new(1, long_title, 1, 1) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_book_attributes_are_set
|
26
|
+
book = Book.new(1, 'title', 2, 3)
|
27
|
+
|
28
|
+
assert_equal 1, book.book_id
|
29
|
+
assert_equal 'title', book.title
|
30
|
+
assert_equal 2, book.author_id
|
31
|
+
assert_equal 3, book.publisher_id
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require './lib/models/publisher'
|
3
|
+
class TestPublisher < Minitest::Test
|
4
|
+
def test_initialize_with_valid_arguments
|
5
|
+
publisher = Publisher.new(1, "Test Publisher", "test@example.com")
|
6
|
+
assert_equal 1, publisher.publisher_id
|
7
|
+
assert_equal "Test Publisher", publisher.name
|
8
|
+
assert_equal "test@example.com", publisher.email
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_initialize_with_null_publisher_id
|
12
|
+
error = assert_raises(ArgumentError) do
|
13
|
+
Publisher.new(nil, "Test Publisher", "test@example.com")
|
14
|
+
end
|
15
|
+
assert_equal "Argument 'publisher_id' cannot be null", error.message
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_initialize_with_null_name
|
19
|
+
error = assert_raises(ArgumentError) do
|
20
|
+
Publisher.new(1, nil, "test@example.com")
|
21
|
+
end
|
22
|
+
assert_equal "Argument 'name' cannot be null", error.message
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_initialize_with_name_length_exceeding_limit
|
26
|
+
long_name = "a" * 101
|
27
|
+
error = assert_raises(ArgumentError) do
|
28
|
+
Publisher.new(1, long_name, "test@example.com")
|
29
|
+
end
|
30
|
+
assert_equal "Name exceeds 100 characters limit: #{long_name}", error.message
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_initialize_with_invalid_email_format
|
34
|
+
error = assert_raises(ArgumentError) do
|
35
|
+
Publisher.new(1, "Test Publisher", "invalid-email")
|
36
|
+
end
|
37
|
+
assert_equal "Invalid email format: invalid-email", error.message
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require './lib/state_holders/list_state_notifier'
|
3
|
+
class TestListStateNotifier < Minitest::Test
|
4
|
+
def setup
|
5
|
+
@notifier = ListStateNotifier.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_items_initialized_as_empty_array
|
9
|
+
assert_equal [], @notifier.items
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_set_all_sets_items_and_notifies_listeners
|
13
|
+
listener = MiniTest::Mock.new
|
14
|
+
listener.expect(:update, nil, [[1, 2, 3]])
|
15
|
+
@notifier.add_listener(listener)
|
16
|
+
|
17
|
+
@notifier.set_all([1, 2, 3])
|
18
|
+
|
19
|
+
assert_equal [1, 2, 3], @notifier.items
|
20
|
+
listener.verify
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_add_adds_item_and_notifies_listeners
|
24
|
+
listener = MiniTest::Mock.new
|
25
|
+
listener.expect(:update, nil, [@notifier.items])
|
26
|
+
@notifier.add_listener(listener)
|
27
|
+
|
28
|
+
@notifier.add(4)
|
29
|
+
|
30
|
+
assert_equal [4], @notifier.items
|
31
|
+
listener.verify
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_get_returns_correct_item
|
35
|
+
@notifier.set_all([1, 2, 3])
|
36
|
+
|
37
|
+
assert_equal 2, @notifier.get(1)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_delete_removes_item_and_notifies_listeners
|
41
|
+
@notifier.set_all([1, 2, 3])
|
42
|
+
listener = MiniTest::Mock.new
|
43
|
+
listener.expect(:update, nil, [@notifier.items])
|
44
|
+
@notifier.add_listener(listener)
|
45
|
+
|
46
|
+
@notifier.delete(2)
|
47
|
+
|
48
|
+
assert_equal [1, 3], @notifier.items
|
49
|
+
listener.verify
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_replace_replaces_item_and_notifies_listeners
|
53
|
+
@notifier.set_all([1, 2, 3])
|
54
|
+
listener = MiniTest::Mock.new
|
55
|
+
listener.expect(:update, nil, [@notifier.items])
|
56
|
+
@notifier.add_listener(listener)
|
57
|
+
|
58
|
+
@notifier.replace(2, 4)
|
59
|
+
|
60
|
+
assert_equal [1, 4, 3], @notifier.items
|
61
|
+
listener.verify
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_add_listener_adds_listener
|
65
|
+
listener = MiniTest::Mock.new
|
66
|
+
|
67
|
+
@notifier.add_listener(listener)
|
68
|
+
|
69
|
+
assert_includes @notifier.instance_variable_get(:@listeners), listener
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_delete_listener_removes_listener
|
73
|
+
listener = MiniTest::Mock.new
|
74
|
+
@notifier.add_listener(listener)
|
75
|
+
|
76
|
+
@notifier.delete_listener(listener)
|
77
|
+
|
78
|
+
refute_includes @notifier.instance_variable_get(:@listeners), listener
|
79
|
+
end
|
80
|
+
end
|