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,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
|