papaSquidLib 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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/Gemfile.lock +69 -0
  4. data/lib/controllers/tab_students_controller.rb +43 -0
  5. data/lib/data_sources/db_client.rb +35 -0
  6. data/lib/db_config/config.yaml +5 -0
  7. data/lib/db_config/library_config.yaml +5 -0
  8. data/lib/db_config/migrations/create_db.sql +3 -0
  9. data/lib/db_config/migrations/create_tables.sql +29 -0
  10. data/lib/db_config/mock_data/mock_data.sql +72 -0
  11. data/lib/logger.rb +27 -0
  12. data/lib/main.rb +6 -0
  13. data/lib/manager/controllers/manager_input_form_controller_create.rb +44 -0
  14. data/lib/manager/controllers/manager_input_form_controller_edit.rb +54 -0
  15. data/lib/manager/controllers/manager_list_controller.rb +99 -0
  16. data/lib/manager/manager_db_data_source.rb +63 -0
  17. data/lib/manager/ui/manager_input_form.rb +69 -0
  18. data/lib/manager/ui/manager_list_view.rb +168 -0
  19. data/lib/models/manager.rb +41 -0
  20. data/lib/models/student.rb +102 -0
  21. data/lib/models/student_base.rb +100 -0
  22. data/lib/models/student_short.rb +50 -0
  23. data/lib/models/task.rb +49 -0
  24. data/lib/models/user.rb +32 -0
  25. data/lib/papaSquidLib/version.rb +5 -0
  26. data/lib/papa_squid_lib.rb +6 -0
  27. data/lib/repositories/adapters/db_source_adapter.rb +54 -0
  28. data/lib/repositories/adapters/file_source_adapter.rb +37 -0
  29. data/lib/repositories/containers/data_list.rb +74 -0
  30. data/lib/repositories/containers/data_list_student_short.rb +18 -0
  31. data/lib/repositories/containers/data_table.rb +35 -0
  32. data/lib/repositories/data_sources/db_data_source.rb +32 -0
  33. data/lib/repositories/data_sources/file_data_source.rb +75 -0
  34. data/lib/repositories/data_sources/transformers/data_transformer_base.rb +15 -0
  35. data/lib/repositories/data_sources/transformers/data_transformer_json.rb +16 -0
  36. data/lib/repositories/data_sources/transformers/data_transformer_yaml.rb +16 -0
  37. data/lib/repositories/student_repository.rb +32 -0
  38. data/lib/state_holders/list_state_notifier.rb +60 -0
  39. data/lib/task/controllers/task_input_form_controller_create.rb +43 -0
  40. data/lib/task/controllers/task_input_form_controller_edit.rb +57 -0
  41. data/lib/task/controllers/task_list_controller.rb +93 -0
  42. data/lib/task/task_db_data_source.rb +85 -0
  43. data/lib/task/ui/task_input_form.rb +67 -0
  44. data/lib/task/ui/task_input_form_factory.rb +26 -0
  45. data/lib/task/ui/task_list_view.rb +163 -0
  46. data/lib/user/controllers/user_input_form_controller_create.rb +42 -0
  47. data/lib/user/controllers/user_input_form_controller_edit.rb +53 -0
  48. data/lib/user/controllers/user_list_controller.rb +99 -0
  49. data/lib/user/ui/user_input_form.rb +69 -0
  50. data/lib/user/ui/user_list_view.rb +170 -0
  51. data/lib/user/user_db_data_source.rb +71 -0
  52. data/lib/views/main_window.rb +32 -0
  53. data/lib/views/tab_students.rb +148 -0
  54. data/papaSquidLib.gemspec +15 -0
  55. data/test/manager_test.rb +27 -0
  56. data/test/state_notifier_test.rb +82 -0
  57. data/test/task_test.rb +51 -0
  58. data/test/user_test.rb +39 -0
  59. metadata +113 -0
@@ -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
@@ -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,43 @@
1
+ # frozen_string_literal: true
2
+ require 'win32api'
3
+
4
+ class TaskInputFormControllerCreate
5
+ def initialize(parent_controller)
6
+ @parent_controller = parent_controller
7
+ @task_rep = TaskDbDataSource.new
8
+ end
9
+
10
+ def set_view(view)
11
+ @view = view
12
+ end
13
+
14
+ def on_view_created
15
+ # begin
16
+ # @student_rep = StudentRepository.new(DBSourceAdapter.new)
17
+ # rescue Mysql2::Error::ConnectionError
18
+ # on_db_conn_error
19
+ # end
20
+ end
21
+
22
+ def process_fields(fields)
23
+ begin
24
+ values = fields.values
25
+ values.insert(-2, Time.now.strftime("%Y-%m-%d"))
26
+ item = Task.new(-1, *values)
27
+ item = @task_rep.add(item)
28
+ @parent_controller.state_notifier.add(item)
29
+ @view.close
30
+ rescue ArgumentError => e
31
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
32
+ api.call(0, e.message, 'Error', 0)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def on_db_conn_error
39
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
40
+ api.call(0, "No connection to DB", "Error", 0)
41
+ @view.close
42
+ end
43
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TaskInputFormControllerEdit
4
+ def initialize(parent_controller, item)
5
+ @parent_controller = parent_controller
6
+ @item = item
7
+ @task_rep = TaskDbDataSource.new
8
+ end
9
+
10
+ def set_view(view)
11
+ @view = view
12
+ end
13
+
14
+ def on_view_created
15
+ # begin
16
+ # @student_rep = StudentRepository.new(DBSourceAdapter.new)
17
+ # rescue Mysql2::Error::ConnectionError
18
+ # on_db_conn_error
19
+ # end
20
+
21
+ # @item = @author_rep.get(@item_id)
22
+ # @view.make_readonly(:git, :telegram, :email, :phone)
23
+ populate_fields(@item)
24
+ end
25
+
26
+ def populate_fields(item)
27
+ @view.set_value(:description, item.description)
28
+ @view.set_value(:completed, item.completed)
29
+ end
30
+
31
+ def process_fields(fields)
32
+
33
+ begin
34
+ print "\n"
35
+ print"ITEM ID=#{@item.task_id}"
36
+ print "\n"
37
+ item = Task.new(@item.task_id, @item.user_id, @item.manager_id, @item.date, *fields.values)
38
+ print "\n"
39
+ print"NEW ITEM ID=#{item.task_id}"
40
+ print "\n"
41
+ item = @task_rep.change(item)
42
+ @parent_controller.state_notifier.replace(@item, item)
43
+ @view.close
44
+ rescue ArgumentError => e
45
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
46
+ api.call(0, e.message, 'Error', 0)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def on_db_conn_error
53
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
54
+ api.call(0, "No connection to DB", "Error", 0)
55
+ @view.close
56
+ end
57
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+ require './lib/state_holders/list_state_notifier'
3
+ require_relative '../ui/task_input_form'
4
+ require_relative 'task_input_form_controller_create.rb'
5
+ require_relative 'task_input_form_controller_edit'
6
+ require_relative '../task_db_data_source'
7
+ require_relative '../ui/task_input_form_factory'
8
+ require 'win32api'
9
+
10
+ class TaskListController
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
+ @task_rep = TaskDbDataSource.new
18
+
19
+ @sort_columns = %w[TaskID UserID ManagerID Date Description Completed]
20
+ @sort_by = @sort_columns.first
21
+
22
+ @completed_filter_columns = [nil, 'Done', 'Undone']
23
+ @completed_filter = @completed_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 = TaskInputFormControllerCreate.new(self)
42
+ view = TaskInputFormFactory.create_create_form(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 = @state_notifier.get(selected_row)
49
+ controller = TaskInputFormControllerEdit.new(self, item)
50
+ view = TaskInputFormFactory.create_edit_form(controller, item)
51
+ controller.set_view(view)
52
+ view.create.show
53
+ end
54
+
55
+ def delete_selected(current_page, per_page, selected_row)
56
+ begin
57
+ item = @state_notifier.get(selected_row)
58
+ @task_rep.delete(item.task_id)
59
+ @state_notifier.delete(item)
60
+ rescue
61
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
62
+ api.call(0, "You cannot delete the user because he is associated with some user or manager", "Error", 0)
63
+ end
64
+ end
65
+
66
+ def refresh_data(page, per_page)
67
+ print "\n"
68
+ print "completed filter:#{@completed_filter}"
69
+ print "\n"
70
+ items = @task_rep.get_list(per_page, page, @sort_by, 'ASC', @completed_filter)
71
+ @state_notifier.set_all(items)
72
+ @view.update_student_count(@task_rep.count)
73
+ end
74
+
75
+ def sort(page, per_page, sort_index)
76
+ @sort_by = @sort_columns[sort_index]
77
+ refresh_data(page, per_page)
78
+ end
79
+
80
+ def filter_completed(page, per_page, filter_index)
81
+ @completed_filter = @completed_filter_columns[filter_index]
82
+ refresh_data(page, per_page)
83
+ end
84
+
85
+
86
+ private
87
+
88
+ def on_db_conn_error
89
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
90
+ api.call(0, "No connection to DB", "Error", 0)
91
+ exit(false)
92
+ end
93
+ end
@@ -0,0 +1,85 @@
1
+ require 'mysql2'
2
+ require_relative '../data_sources/db_client'
3
+ class TaskDbDataSource
4
+ def initialize
5
+ @client = DBClient.instance
6
+ end
7
+ def add(task)
8
+ user_exists = user_exists?(task.user_id)
9
+ manager_exists = manager_exists?(task.manager_id)
10
+ query = "INSERT INTO Task (UserID, ManagerID, Date, Description, Completed) VALUES (#{task.user_id}, #{task.manager_id}, '#{task.date}', '#{task.description}', '#{task.completed}')"
11
+ @client.query(query)
12
+ task_id = @client.last_id
13
+ get(task_id)
14
+ end
15
+
16
+ def change(task)
17
+ user_exists = user_exists?(task.user_id)
18
+ manager_exists = manager_exists?(task.manager_id)
19
+ query = "UPDATE Task SET Completed='#{task.completed}', Description='#{task.description}' WHERE TaskID=#{task.task_id}"
20
+ @client.query(query)
21
+ get(task.task_id)
22
+ end
23
+
24
+
25
+ def delete(id)
26
+ query = "DELETE FROM Task WHERE TaskID=#{id}"
27
+ @client.query(query)
28
+ end
29
+
30
+ def get(id)
31
+ query = "SELECT * FROM Task WHERE TaskID=#{id}"
32
+ result = @client.query(query).first
33
+ if result
34
+ Task.new(result[:'TaskID'], result[:'UserID'], result[:'ManagerID'], result[:'Date'], result[:'Description'], result[:'Completed'])
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ def get_list(page_size, page_num, sort_field, sort_direction, completed = nil)
41
+ offset = (page_num - 1) * page_size
42
+ query = "SELECT * FROM Task"
43
+ print "\n"
44
+ print "completed:#{completed}"
45
+ print "\n"
46
+ if completed == 'Done'
47
+ query += " WHERE Completed='Done'"
48
+ elsif completed == 'Undone'
49
+ query += " WHERE Completed='Undone'"
50
+ end
51
+
52
+ query += " ORDER BY #{sort_field} #{sort_direction} LIMIT #{page_size} OFFSET #{offset}"
53
+ results = @client.query(query)
54
+
55
+ tasks = []
56
+ results.each do |result|
57
+ tasks << Task.new(result[:'TaskID'], result[:'UserID'], result[:'ManagerID'], result[:'Date'], result[:'Description'], result[:'Completed'])
58
+ end
59
+
60
+ tasks
61
+ end
62
+
63
+ def count
64
+ query = "SELECT COUNT(*) FROM Task"
65
+ result = @client.query(query).first
66
+ result[:'COUNT(*)']
67
+ end
68
+
69
+ def user_exists?(user_id)
70
+ query = "SELECT COUNT(*) FROM User WHERE UserID=#{user_id}"
71
+ result = @client.query(query).first
72
+ unless result[:'COUNT(*)'] > 0
73
+ raise ArgumentError, "User with ID=#{user_id} doesn't exist"
74
+ end
75
+ end
76
+
77
+ def manager_exists?(manager_id)
78
+ query = "SELECT COUNT(*) FROM Manager WHERE ManagerID=#{manager_id}"
79
+ result = @client.query(query).first
80
+ unless result[:'COUNT(*)'] > 0
81
+ raise ArgumentError, "Manager with ID=#{manager_id} doesn't exist"
82
+ end
83
+ end
84
+
85
+ end