Compare commits
55 Commits
@ -0,0 +1 @@
|
||||
lab2/tests/students_test.txt
|
@ -1,16 +0,0 @@
|
||||
require_relative '../src/02_user_interface.rb'
|
||||
|
||||
RSpec.describe "Main" do
|
||||
before do
|
||||
allow(STDIN).to receive(:gets).and_return("ruby\n")
|
||||
end
|
||||
|
||||
it "greets the user and checks Ruby as language" do
|
||||
expect { main() }.to output("Hello my catgirl test_user! \nWhat is your love language?\nruby\nc++\npy\nПодлиза \n").to_stdout
|
||||
end
|
||||
|
||||
it "handles unknown language input" do
|
||||
allow(STDIN).to receive(:gets).and_return("java\n")
|
||||
expect { main() }.to output(/Неизвестный язык: java/).to_stdout
|
||||
end
|
||||
end
|
@ -1,2 +0,0 @@
|
||||
puts "Hello, world!"
|
||||
|
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
LANGUAGES = [
|
||||
"ruby",
|
||||
"c++",
|
||||
"py"
|
||||
]
|
||||
|
||||
def main()
|
||||
user_name = ARGV[0]
|
||||
puts "Hello my catgirl #{user_name}! \nWhat is your love language?"
|
||||
LANGUAGES.each { |language| puts "#{language}", " " }
|
||||
|
||||
user_lang = STDIN.gets.chomp
|
||||
|
||||
case user_lang
|
||||
when "ruby"
|
||||
puts "Подлиза \n"
|
||||
when "c++"
|
||||
puts "MATLAB скушал? \n"
|
||||
when "TS/JS"
|
||||
puts "Фронтендер, Фууу! \n"
|
||||
when "py"
|
||||
puts "Девопсер, иди ДАГИ писать \n"
|
||||
else
|
||||
puts "Неизвестный язык: #{user_lang}"
|
||||
end
|
||||
end
|
||||
|
||||
main()
|
@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
LANGUAGES = [
|
||||
"ruby",
|
||||
"c++",
|
||||
"py"
|
||||
]
|
||||
|
||||
def main()
|
||||
user_name = ARGV[0]
|
||||
puts "Hello my catgirl #{user_name}! \nWhat is your love language?"
|
||||
LANGUAGES.each { |language| puts "#{language}", " " }
|
||||
|
||||
user_lang = STDIN.gets.chomp
|
||||
case user_lang
|
||||
when "ruby"
|
||||
puts "Подлиза \n"
|
||||
when "c++"
|
||||
puts "MATLAB скушал? \n"
|
||||
when "TS/JS"
|
||||
puts "Фронтендер, Фууу! \n"
|
||||
when "py"
|
||||
puts "Девопсер, иди ДАГИ писать \n"
|
||||
end
|
||||
|
||||
puts "Введите команду на языке Ruby для выполнения:"
|
||||
ruby_command = STDIN.gets.chomp
|
||||
begin
|
||||
eval(ruby_command)
|
||||
rescue Exception => e
|
||||
puts "Ошибка выполнения команды Ruby: #{e.message}"
|
||||
end
|
||||
|
||||
puts "Введите команду операционной системы для выполнения:"
|
||||
os_command = STDIN.gets.chomp
|
||||
system(os_command)
|
||||
end
|
||||
|
||||
main()
|
@ -1,68 +0,0 @@
|
||||
|
||||
|
||||
def find_min_element_for(arr)
|
||||
min_element = arr[0]
|
||||
for element in arr
|
||||
min_element = element if element < min_element
|
||||
end
|
||||
min_element
|
||||
end
|
||||
|
||||
|
||||
def find_min_element_while(arr)
|
||||
min_element = arr[0]
|
||||
index = 0
|
||||
while index < arr.size
|
||||
min_element = arr[index] if arr[index] < min_element
|
||||
index += 1
|
||||
end
|
||||
min_element
|
||||
end
|
||||
|
||||
|
||||
def find_first_positive_index_for(arr)
|
||||
for index in 0...arr.size
|
||||
return index if arr[index] > 0
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def find_first_positive_index_while(arr)
|
||||
index = 0
|
||||
while index < arr.size
|
||||
return index if arr[index] > 0
|
||||
index += 1
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def find_first_positive_for(arr)
|
||||
for element in arr
|
||||
return element if element > 0
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def find_first_positive_while(arr)
|
||||
index = 0
|
||||
while index < arr.size
|
||||
return arr[index] if arr[index] > 0
|
||||
index += 1
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# INPUT
|
||||
array = [-10, -5, 0, 3, 5, -2]
|
||||
|
||||
puts "Минимальный элемент (for): #{find_min_element_for(array)}"
|
||||
puts "Минимальный элемент (while): #{find_min_element_while(array)}"
|
||||
|
||||
puts "Индекс первого положительного элемента (for): #{find_first_positive_index_for(array)}"
|
||||
puts "Индекс первого положительного элемента (while): #{find_first_positive_index_while(array)}"
|
||||
|
||||
puts "Первый положительный элемент (for): #{find_first_positive_for(array)}"
|
||||
puts "Первый положительный элемент (while): #{find_first_positive_while(array)}"
|
@ -1,62 +0,0 @@
|
||||
|
||||
|
||||
def find_min_element(arr)
|
||||
min_element = arr[0]
|
||||
arr.each do |element|
|
||||
min_element = element if element < min_element
|
||||
end
|
||||
min_element
|
||||
end
|
||||
|
||||
|
||||
def find_first_positive_index(arr)
|
||||
arr.each_with_index do |element, index|
|
||||
return index if element > 0
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def find_first_positive(arr)
|
||||
arr.each do |element|
|
||||
return element if element > 0
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def main
|
||||
method_name = ARGV[0]
|
||||
file_path = ARGV[1]
|
||||
|
||||
if method_name.nil? || file_path.nil?
|
||||
puts "Неправльный формат ввода. Использование: ruby program.rb <method_name> <file_path>"
|
||||
puts "method_name: 'min', 'first_positive_index' или 'first_positive'"
|
||||
exit
|
||||
end
|
||||
|
||||
begin
|
||||
array = File.read(file_path).split.map(&:to_i)
|
||||
rescue Errno::ENOENT
|
||||
puts "Файл не найден: #{file_path}"
|
||||
exit
|
||||
end
|
||||
|
||||
case method_name
|
||||
when 'min'
|
||||
result = find_min_element(array)
|
||||
puts "Минимальный элемент: #{result}"
|
||||
when 'first_positive_index'
|
||||
result = find_first_positive_index(array)
|
||||
puts "Индекс первого положительного элемента: #{result.nil? ? 'Нет положительного элемента' : result}"
|
||||
when 'first_positive'
|
||||
result = find_first_positive(array)
|
||||
puts "Первый положительный элемент: #{result.nil? ? 'Нет положительного элемента' : result}"
|
||||
else
|
||||
puts "Неизвестный метод: #{method_name}"
|
||||
puts "Доступные методы: 'min', 'first_positive_index', 'first_positive'"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
main if __FILE__ == $0
|
@ -1 +0,0 @@
|
||||
-10 -5 0 3 5 -2
|
@ -1,4 +1,163 @@
|
||||
# Lab 2
|
||||
|
||||
> "И тут я обнаружил что случайно сделал 3-5 задачи в 1-2"
|
||||
|
||||
## Диаграмма классов:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PersistenceStrategy {
|
||||
+ load(filename : String) : Raise
|
||||
+ save(filename : String, students : Students) : Raise
|
||||
}
|
||||
|
||||
class StudentsListBase {
|
||||
+ initialize(filename : String)
|
||||
+ load_from_file() : Raise
|
||||
+ save_to_file() : Raise
|
||||
+ get_student_by_id(id : String) : Student
|
||||
+ get_k_n_student_short_list(k : Int, n : Int, data_list = nil) : List<Student>
|
||||
+ sort_students() : List<Student>
|
||||
+ add_student(student : Student) : Student
|
||||
+ update_student_by_id(id : Int, new_stodent : Student) : Bool
|
||||
+ delete_student_by_id(id : Int)
|
||||
+ get_student_short_count() : Int
|
||||
}
|
||||
|
||||
class StudentsListDB {
|
||||
+ initialize()
|
||||
+ get_student_by_id(id : Int) : nil | Student
|
||||
+ get_k_n_student_short_list(k : Int, n : Int) : List<Student>
|
||||
+ add_student(student : Student) : Student
|
||||
+ update_student_by_id(id : Int, new_student : Student)
|
||||
+ delete_student_by_id(id : Int)
|
||||
+ get_student_short_count() : Int
|
||||
- row_to_student(row : Student)
|
||||
- escape(value : any)
|
||||
}
|
||||
|
||||
class JSONPersistenceStrategy {
|
||||
+ load(filename : String) : List<>
|
||||
+ save(filename : String, students : List<Student>)
|
||||
- student_to_hash(student : Student)
|
||||
- hash_to_student(hash : String)
|
||||
}
|
||||
|
||||
class TXTPersistenceStrategy {
|
||||
+ load(filename : String) : List<>
|
||||
+ save(filename : String, students : List<Student>)
|
||||
}
|
||||
|
||||
class YAMLPersistenceStrategy {
|
||||
+ load(filename : String) : List<>
|
||||
+ save(filename : String, students : List<Student>)
|
||||
- student_to_hash(student : Student)
|
||||
- hash_to_student(hash : String)
|
||||
}
|
||||
|
||||
class StudentsList {
|
||||
+ initialize(filename : String, persistence_strategy)
|
||||
+ load() : self
|
||||
+ save() : self
|
||||
+ get_student_by_id(id : Int) : Student
|
||||
+ get_k_n_student_short_list(k : Int, n : Int, data_list = nil) : DataListStudentShort
|
||||
+ sort_students() : List<Student>
|
||||
add_student(student : Student) : Student
|
||||
+ update_student_by_id(id : Int, new_student : Student) : Bool
|
||||
+ delete_student_by_id(id : Int) : Bool
|
||||
+ get_student_short_count() : Int
|
||||
}
|
||||
|
||||
class DatabaseConnection {
|
||||
+ initialize()
|
||||
+ client()
|
||||
+ query(sql : String)
|
||||
}
|
||||
|
||||
class BinarySearchTree {
|
||||
- root : Node
|
||||
+ add(student : Student) : void
|
||||
+ each(&block) : void
|
||||
- insert(node : Node, student : Student) : Node
|
||||
- in_order_traversal(node : Node, &block) : void
|
||||
}
|
||||
|
||||
class Node {
|
||||
- student : Student
|
||||
- left : Node
|
||||
- right : Node
|
||||
+ Node(student : Student)
|
||||
}
|
||||
|
||||
class Person {
|
||||
- id : String
|
||||
- git : String
|
||||
- phone : String?
|
||||
- telegram : String?
|
||||
- email : String?
|
||||
+ Person(id : String, git : String, phone: String, telegram: String, email: String)
|
||||
+ phone=(String) : Boolean
|
||||
+ telegram=(String) : Boolean
|
||||
+ email=(String) : Boolean
|
||||
+ surname_initials() : NotImplementedError
|
||||
+ valid_phone_number() : Boolean <<class>>
|
||||
+ valid_telegram() : Boolean <<class>>
|
||||
+ valid_email() : Boolean <<class>>
|
||||
+ git_present() : Boolean
|
||||
+ contact_present() : Boolean
|
||||
+ get_first_contact() : String
|
||||
+ git=(git : String) : void
|
||||
+ validate_id(id: String) : void <<class>>
|
||||
+ validate_git(git : String) : void <<class>>
|
||||
- valid_git?(git : String) : Boolean
|
||||
- valid_id?(id : String) : Boolean
|
||||
}
|
||||
|
||||
class StudentRepository {
|
||||
+ read_from_txt(file_path : String) : List~Student~ <<class>>
|
||||
+ write_to_txt(file_path : String, students : List~Student~) <<class>>
|
||||
}
|
||||
|
||||
class StudentShort {
|
||||
- surname_initials : String
|
||||
+ get_surname_initials() : String
|
||||
+ StudentShort(id : String, surname_initials : String, phone: String, telegram: String, email: String)
|
||||
+ from_student(student : Student) : StudentShort <<class>>
|
||||
+ from_string(id : String, info_string : String) : StudentShort <<class>>
|
||||
+ to_s() : String
|
||||
- parse_contact_string(contact_string: String) : Array<String> <<class>>
|
||||
}
|
||||
|
||||
class Student {
|
||||
- surname : String
|
||||
- name : String
|
||||
- patronymic : String
|
||||
- birth_date : Date
|
||||
- const NAME_REGEX : String
|
||||
+ Student(id : String, git : String, phone: String, telegram: String, email: String, surname : String, name : String, patronymic : String, birth_date : Date)
|
||||
+ from_string(student_string : String) : Student <<class>>
|
||||
+ surname_initials() : String
|
||||
+ to_s() : String
|
||||
+ get_info() : String
|
||||
+ surname=(surname : String)
|
||||
+ name=(name : String)
|
||||
+ patronymic=(patronymic : String)
|
||||
+ birth_date=(birthdate : String)
|
||||
+ valid_name?(name : String) : Boolean <<class>>
|
||||
- name_initial(name : String) : String
|
||||
- patronymic(patronymic : String) : String
|
||||
}
|
||||
|
||||
BinarySearchTree o-- Node
|
||||
Node o-- Student
|
||||
StudentsList o-- Student
|
||||
StudentsListBase o-- Student
|
||||
StudentsListDB o-- DatabaseConnection
|
||||
DatabaseConnection <.. Singleton
|
||||
JSONPersistenceStrategy o-- PersistenceStrategy
|
||||
TXTPersistenceStrategy o-- PersistenceStrategy
|
||||
YAMLPersistenceStrategy o-- PersistenceStrategy
|
||||
Person <|-- Student
|
||||
Person <|-- StudentShort
|
||||
Student <.. StudentRepository
|
||||
```
|
||||
|
||||
|
@ -0,0 +1,46 @@
|
||||
class BinarySearchTree
|
||||
include Enumerable
|
||||
|
||||
class Node
|
||||
attr_accessor :student, :left, :right
|
||||
|
||||
def initialize(student)
|
||||
@student = student
|
||||
@left = nil
|
||||
@right = nil
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@root = nil
|
||||
end
|
||||
|
||||
def add(student)
|
||||
@root = insert(@root, student)
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
in_order_traversal(@root, &block)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def insert(node, student)
|
||||
return Node.new(student) if node.nil?
|
||||
|
||||
if student.birth_date < node.student.birth_date
|
||||
node.left = insert(node.left, student)
|
||||
else
|
||||
node.right = insert(node.right, student)
|
||||
end
|
||||
node
|
||||
end
|
||||
|
||||
def in_order_traversal(node, &block)
|
||||
return if node.nil?
|
||||
|
||||
in_order_traversal(node.left, &block)
|
||||
yield node.student
|
||||
in_order_traversal(node.right, &block)
|
||||
end
|
||||
end
|
@ -0,0 +1,20 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: mysql_container
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: rootpassword
|
||||
MYSQL_DATABASE: project_db
|
||||
MYSQL_USER: project_user
|
||||
MYSQL_PASSWORD: project_password
|
||||
ports:
|
||||
- "3306:3306"
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./db/migrations:/docker-entrypoint-initdb.d # Автоматическая инициализация базы (скрипты миграций)
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
@ -0,0 +1,61 @@
|
||||
require_relative './data_table'
|
||||
|
||||
class DataListStudentShort < DataList
|
||||
def initialize(items)
|
||||
column_names = ['№', 'Фамилия и инициалы', 'Телефон', 'Telegram', 'Email']
|
||||
filtered = items.select { |item| item.is_a?(StudentShort) }
|
||||
super(filtered, column_names)
|
||||
end
|
||||
|
||||
def items=(new_items)
|
||||
filtered = new_items.select { |item| item.is_a?(StudentShort) }
|
||||
@items = filtered.dup
|
||||
end
|
||||
|
||||
def get_names
|
||||
column_names
|
||||
end
|
||||
|
||||
def get_data
|
||||
data = prepare_table_data
|
||||
DataTable.new(data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_names
|
||||
['№', 'Фамилия и инициалы', 'Телефон', 'Telegram', 'Email']
|
||||
end
|
||||
|
||||
def prepare_table_data
|
||||
@items.each_with_index.map do |item, index|
|
||||
prepare_row(item, index)
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_row(item, index)
|
||||
[
|
||||
index + 1, # id
|
||||
item.surname_initials, # Фамилия и инициалы
|
||||
item.phone || '-', # Телефон (или прочерк)
|
||||
item.telegram || '-', # Telegram (или прочерк)
|
||||
item.email || '-' # Email (или прочерк)
|
||||
]
|
||||
end
|
||||
|
||||
def extract_surname_initials(item)
|
||||
item.respond_to?(:surname_initials) ? item.surname_initials : 'N/A'
|
||||
end
|
||||
|
||||
def extract_phone(item)
|
||||
item.respond_to?(:phone) ? item.phone : nil
|
||||
end
|
||||
|
||||
def extract_telegram(item)
|
||||
item.respond_to?(:telegram) ? item.telegram : nil
|
||||
end
|
||||
|
||||
def extract_email(item)
|
||||
item.respond_to?(:email) ? item.email : nil
|
||||
end
|
||||
end
|
@ -0,0 +1,64 @@
|
||||
class DataTable
|
||||
def initialize(data)
|
||||
@data = data.map { |row| row.dup.freeze }.freeze
|
||||
end
|
||||
|
||||
def item(row, col)
|
||||
return nil unless valid_row?(row) && valid_col?(col)
|
||||
@data[row][col]
|
||||
end
|
||||
|
||||
def rows_count
|
||||
@data.size
|
||||
end
|
||||
|
||||
def columns_count
|
||||
return 0 if @data.empty?
|
||||
@data.first.size
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_row?(row)
|
||||
row.between?(0, rows_count - 1)
|
||||
end
|
||||
|
||||
def valid_col?(col)
|
||||
col.between?(0, columns_count - 1)
|
||||
end
|
||||
end
|
||||
|
||||
class DataList
|
||||
def initialize(items, column_names = [])
|
||||
@items = items.dup.freeze
|
||||
@selected = []
|
||||
@column_names = column_names
|
||||
end
|
||||
|
||||
def select(number)
|
||||
index = number.to_i
|
||||
toggle_selection(index) if valid_index?(index)
|
||||
end
|
||||
|
||||
def get_selected
|
||||
@selected.dup
|
||||
end
|
||||
|
||||
def get_names
|
||||
@column_names
|
||||
end
|
||||
|
||||
def get_data
|
||||
raise NotImplementedError, "Implement this method in a subclass"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_index?(index)
|
||||
index.between?(0, @items.size - 1)
|
||||
end
|
||||
|
||||
def toggle_selection(index)
|
||||
@selected.include?(index) ? @selected.delete(index) : @selected << index
|
||||
end
|
||||
end
|
@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS student (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
git VARCHAR(255) NOT NULL,
|
||||
surname VARCHAR(100) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
patronymic VARCHAR(100) NOT NULL,
|
||||
birth_date DATE NOT NULL,
|
||||
phone VARCHAR(20),
|
||||
telegram VARCHAR(50),
|
||||
email VARCHAR(100)
|
||||
);
|
@ -0,0 +1,4 @@
|
||||
INSERT INTO student (git, surname, name, patronymic, birth_date, phone, telegram, email)
|
||||
VALUES
|
||||
('https://github.com/example', 'Иванов', 'Иван', 'Иванович', '2000-01-01', '+123456789', '@telegram', 'email@example.com'),
|
||||
('https://github.com/example2', 'Петров', 'Петр', 'Петрович', '1999-05-15', '+987654321', '@petrov', 'petrov@example.com');
|
@ -0,0 +1,23 @@
|
||||
require 'mysql2'
|
||||
require 'singleton'
|
||||
|
||||
class DatabaseConnection
|
||||
include Singleton
|
||||
|
||||
def initialize
|
||||
@client = Mysql2::Client.new(
|
||||
host: 'localhost',
|
||||
username: 'project_user',
|
||||
password: 'project_password',
|
||||
database: 'project_db'
|
||||
)
|
||||
end
|
||||
|
||||
def client
|
||||
@client
|
||||
end
|
||||
|
||||
def query(sql)
|
||||
@client.query(sql)
|
||||
end
|
||||
end
|
@ -0,0 +1,9 @@
|
||||
module PersistenceStrategy
|
||||
def load(filename)
|
||||
raise NotImplementedError, "Метод load должен быть реализован в стратегии"
|
||||
end
|
||||
|
||||
def save(filename, students)
|
||||
raise NotImplementedError, "Метод save должен быть реализован в стратегии"
|
||||
end
|
||||
end
|
@ -0,0 +1,87 @@
|
||||
require_relative '../student'
|
||||
require_relative '../student_short'
|
||||
require_relative '../data_list_student_short'
|
||||
require_relative '../data_table'
|
||||
|
||||
class StudentsList
|
||||
attr_reader :students
|
||||
|
||||
def initialize(filename, persistence_strategy)
|
||||
@filename = filename
|
||||
@persistence_strategy = persistence_strategy
|
||||
@students = []
|
||||
end
|
||||
|
||||
# Загрузка студентов через выбранную стратегию
|
||||
def load
|
||||
@students = @persistence_strategy.load(@filename)
|
||||
self
|
||||
end
|
||||
|
||||
# Сохранение студентов через выбранную стратегию
|
||||
def save
|
||||
@persistence_strategy.save(@filename, @students)
|
||||
self
|
||||
end
|
||||
|
||||
def get_student_by_id(id)
|
||||
@students.find { |s| s.id.to_s == id.to_s }
|
||||
end
|
||||
|
||||
# Получить список из k студентов (страница n) в виде объекта DataList
|
||||
def get_k_n_student_short_list(k, n, data_list = nil)
|
||||
start_index = (n - 1) * k
|
||||
sorted_students = @students.sort_by { |s| s.surname_initials }
|
||||
selected_students = sorted_students[start_index, k] || []
|
||||
short_objects = selected_students.map { |s| StudentShort.from_student(s) }
|
||||
|
||||
if data_list && data_list.is_a?(DataList)
|
||||
if data_list.respond_to?(:items=)
|
||||
data_list.items = short_objects
|
||||
else
|
||||
data_list = DataListStudentShort.new(short_objects)
|
||||
end
|
||||
data_list
|
||||
else
|
||||
DataListStudentShort.new(short_objects)
|
||||
end
|
||||
end
|
||||
|
||||
# Сортировка студентов по фамилии и инициалам
|
||||
def sort_students!
|
||||
@students.sort_by! { |s| s.surname_initials }
|
||||
end
|
||||
|
||||
def add_student(student)
|
||||
new_id = if @students.empty?
|
||||
1
|
||||
else
|
||||
max_id = @students.map { |s| s.id.to_i }.max
|
||||
max_id + 1
|
||||
end
|
||||
student.id = new_id.to_s
|
||||
@students << student
|
||||
student
|
||||
end
|
||||
|
||||
def update_student_by_id(id, new_student)
|
||||
index = @students.find_index { |s| s.id.to_s == id.to_s }
|
||||
if index
|
||||
new_student.id = @students[index].id
|
||||
@students[index] = new_student
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def delete_student_by_id(id)
|
||||
initial_count = @students.size
|
||||
@students.reject! { |s| s.id.to_s == id.to_s }
|
||||
initial_count != @students.size
|
||||
end
|
||||
|
||||
def get_student_short_count
|
||||
@students.size
|
||||
end
|
||||
end
|
@ -0,0 +1,84 @@
|
||||
require_relative '../student'
|
||||
require_relative '../student_short'
|
||||
require_relative '../data_list_student_short'
|
||||
require_relative '../data_table'
|
||||
|
||||
class StudentsListBase
|
||||
attr_reader :students
|
||||
|
||||
def initialize(filename)
|
||||
@filename = filename
|
||||
@students = []
|
||||
end
|
||||
|
||||
# Абстрактный метод
|
||||
def load_from_file
|
||||
raise NotImplementedError, "Метод load_from_file должен быть реализован в подклассе"
|
||||
end
|
||||
|
||||
# Абстрактный метод
|
||||
def save_to_file
|
||||
raise NotImplementedError, "Метод save_to_file должен быть реализован в подклассе"
|
||||
end
|
||||
|
||||
def get_student_by_id(id)
|
||||
@students.find { |s| s.id.to_s == id.to_s }
|
||||
end
|
||||
|
||||
# Получить список из k студентов (страница n) в виде объекта DataList
|
||||
def get_k_n_student_short_list(k, n, data_list = nil)
|
||||
start_index = (n - 1) * k
|
||||
sorted_students = @students.sort_by { |s| s.surname_initials }
|
||||
selected_students = sorted_students[start_index, k] || []
|
||||
short_objects = selected_students.map { |s| StudentShort.from_student(s) }
|
||||
|
||||
if data_list && data_list.is_a?(DataList)
|
||||
if data_list.respond_to?(:items=)
|
||||
data_list.items = short_objects
|
||||
else
|
||||
data_list = DataListStudentShort.new(short_objects)
|
||||
end
|
||||
data_list
|
||||
else
|
||||
DataListStudentShort.new(short_objects)
|
||||
end
|
||||
end
|
||||
|
||||
# Сортировать список студентов по ФамилияИнициалы
|
||||
def sort_students!
|
||||
@students.sort_by! { |s| s.surname_initials }
|
||||
end
|
||||
|
||||
def add_student(student)
|
||||
new_id = if @students.empty?
|
||||
1
|
||||
else
|
||||
max_id = @students.map { |s| s.id.to_i }.max
|
||||
max_id + 1
|
||||
end
|
||||
student.id = new_id.to_s
|
||||
@students << student
|
||||
student
|
||||
end
|
||||
|
||||
def update_student_by_id(id, new_student)
|
||||
index = @students.find_index { |s| s.id.to_s == id.to_s }
|
||||
if index
|
||||
new_student.id = @students[index].id
|
||||
@students[index] = new_student
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def delete_student_by_id(id)
|
||||
initial_count = @students.size
|
||||
@students.reject! { |s| s.id.to_s == id.to_s }
|
||||
initial_count != @students.size
|
||||
end
|
||||
|
||||
def get_student_short_count
|
||||
@students.size
|
||||
end
|
||||
end
|
@ -0,0 +1,96 @@
|
||||
require_relative '../db_connection'
|
||||
require_relative '../student'
|
||||
require_relative '../student_short'
|
||||
require_relative '../data_list_student_short'
|
||||
require 'date'
|
||||
|
||||
class StudentsListDB
|
||||
def initialize
|
||||
@db = DatabaseConnection.instance
|
||||
end
|
||||
|
||||
def get_student_by_id(id)
|
||||
result = @db.query("SELECT * FROM student WHERE id = #{id} LIMIT 1")
|
||||
row = result.first
|
||||
row ? row_to_student(row) : nil
|
||||
end
|
||||
|
||||
def get_k_n_student_short_list(k, n)
|
||||
offset = (n - 1) * k
|
||||
results = @db.query("SELECT * FROM student ORDER BY surname, name, patronymic LIMIT #{k} OFFSET #{offset}")
|
||||
student_shorts = results.map { |row| StudentShort.from_student(row_to_student(row)) }
|
||||
DataListStudentShort.new(student_shorts)
|
||||
end
|
||||
|
||||
def add_student(student)
|
||||
sql = <<~SQL
|
||||
INSERT INTO student (git, surname, name, patronymic, birth_date, phone, telegram, email)
|
||||
VALUES (
|
||||
'#{escape(student.git)}',
|
||||
'#{escape(student.surname)}',
|
||||
'#{escape(student.name)}',
|
||||
'#{escape(student.patronymic)}',
|
||||
'#{student.birth_date}',
|
||||
#{student.phone ? "'#{escape(student.phone)}'" : "NULL"},
|
||||
#{student.telegram ? "'#{escape(student.telegram)}'" : "NULL"},
|
||||
#{student.email ? "'#{escape(student.email)}'" : "NULL"}
|
||||
)
|
||||
SQL
|
||||
|
||||
@db.query(sql)
|
||||
new_id = @db.query("SELECT LAST_INSERT_ID() as id").first['id']
|
||||
student.id = new_id.to_s
|
||||
student
|
||||
end
|
||||
|
||||
def update_student_by_id(id, new_student)
|
||||
sql = <<~SQL
|
||||
UPDATE student SET
|
||||
git = '#{escape(new_student.git)}',
|
||||
surname = '#{escape(new_student.surname)}',
|
||||
name = '#{escape(new_student.name)}',
|
||||
patronymic = '#{escape(new_student.patronymic)}',
|
||||
birth_date = '#{new_student.birth_date}',
|
||||
phone = #{new_student.phone ? "'#{escape(new_student.phone)}'" : "NULL"},
|
||||
telegram = #{new_student.telegram ? "'#{escape(new_student.telegram)}'" : "NULL"},
|
||||
email = #{new_student.email ? "'#{escape(new_student.email)}'" : "NULL"}
|
||||
WHERE id = #{id}
|
||||
SQL
|
||||
|
||||
@db.query(sql)
|
||||
@db.client.affected_rows > 0
|
||||
end
|
||||
|
||||
def delete_student_by_id(id)
|
||||
@db.query("DELETE FROM student WHERE id = #{id}")
|
||||
@db.client.affected_rows > 0
|
||||
end
|
||||
|
||||
def get_student_short_count
|
||||
result = @db.query("SELECT COUNT(*) as count FROM student")
|
||||
result.first['count']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Преобразование строки результата в объект Student
|
||||
|
||||
def row_to_student(row)
|
||||
Student.new(
|
||||
id: row['id'].to_s,
|
||||
git: row['git'],
|
||||
surname: row['surname'],
|
||||
name: row['name'],
|
||||
patronymic: row['patronymic'],
|
||||
birth_date: Date.parse(row['birth_date'].to_s),
|
||||
phone: row['phone'],
|
||||
telegram: row['telegram'],
|
||||
email: row['email']
|
||||
)
|
||||
end
|
||||
|
||||
# Метод для экранирования строк
|
||||
def escape(value)
|
||||
@db.client.escape(value.to_s)
|
||||
end
|
||||
end
|
@ -0,0 +1,62 @@
|
||||
require 'json'
|
||||
require 'date'
|
||||
require_relative '../student'
|
||||
require_relative 'persistence_strategy'
|
||||
|
||||
class JSONPersistenceStrategy
|
||||
include PersistenceStrategy
|
||||
|
||||
def load(filename)
|
||||
if File.exist?(filename)
|
||||
file_content = File.read(filename)
|
||||
begin
|
||||
data = JSON.parse(file_content)
|
||||
data.map { |student_hash| hash_to_student(student_hash) }
|
||||
rescue JSON::ParserError => e
|
||||
warn "Ошибка парсинга JSON: #{e.message}"
|
||||
[]
|
||||
end
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def save(filename, students)
|
||||
data = students.map { |student| student_to_hash(student) }
|
||||
File.open(filename, 'w') do |file|
|
||||
file.write(JSON.pretty_generate(data))
|
||||
end
|
||||
rescue IOError => e
|
||||
warn "Ошибка при записи в файл: #{e.message}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def student_to_hash(student)
|
||||
{
|
||||
'id' => student.id,
|
||||
'git' => student.git,
|
||||
'surname' => student.surname,
|
||||
'name' => student.name,
|
||||
'patronymic' => student.patronymic,
|
||||
'birth_date' => student.birth_date.to_s,
|
||||
'phone' => student.phone,
|
||||
'telegram' => student.telegram,
|
||||
'email' => student.email
|
||||
}
|
||||
end
|
||||
|
||||
def hash_to_student(hash)
|
||||
Student.new(
|
||||
id: hash['id'],
|
||||
git: hash['git'],
|
||||
surname: hash['surname'],
|
||||
name: hash['name'],
|
||||
patronymic: hash['patronymic'],
|
||||
birth_date: Date.parse(hash['birth_date']),
|
||||
phone: hash['phone'],
|
||||
telegram: hash['telegram'],
|
||||
email: hash['email']
|
||||
)
|
||||
end
|
||||
end
|
@ -0,0 +1,33 @@
|
||||
require_relative '../student'
|
||||
require_relative 'persistence_strategy'
|
||||
|
||||
class TXTPersistenceStrategy
|
||||
include PersistenceStrategy
|
||||
|
||||
def load(filename)
|
||||
if File.exist?(filename)
|
||||
File.open(filename, 'r') do |file|
|
||||
file.each_line.map do |line|
|
||||
line.strip!
|
||||
next if line.empty?
|
||||
begin
|
||||
Student.from_string(line)
|
||||
rescue ArgumentError => e
|
||||
warn "Ошибка при парсинге строки: #{line}. #{e.message}"
|
||||
nil
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def save(filename, students)
|
||||
File.open(filename, 'w') do |file|
|
||||
students.each do |student|
|
||||
file.puts student.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,61 @@
|
||||
require 'yaml'
|
||||
require 'date'
|
||||
require_relative '../student'
|
||||
require_relative 'persistence_strategy'
|
||||
|
||||
class YAMLPersistenceStrategy
|
||||
include PersistenceStrategy
|
||||
|
||||
def load(filename)
|
||||
if File.exist?(filename)
|
||||
begin
|
||||
data = YAML.load_file(filename)
|
||||
data.map { |student_hash| hash_to_student(student_hash) }
|
||||
rescue StandardError => e
|
||||
warn "Ошибка при загрузке YAML: #{e.message}"
|
||||
[]
|
||||
end
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def save(filename, students)
|
||||
data = students.map { |student| student_to_hash(student) }
|
||||
File.open(filename, 'w') do |file|
|
||||
file.write(data.to_yaml)
|
||||
end
|
||||
rescue IOError => e
|
||||
warn "Ошибка при записи в YAML: #{e.message}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def student_to_hash(student)
|
||||
{
|
||||
'id' => student.id,
|
||||
'git' => student.git,
|
||||
'surname' => student.surname,
|
||||
'name' => student.name,
|
||||
'patronymic' => student.patronymic,
|
||||
'birth_date' => student.birth_date.to_s,
|
||||
'phone' => student.phone,
|
||||
'telegram' => student.telegram,
|
||||
'email' => student.email
|
||||
}
|
||||
end
|
||||
|
||||
def hash_to_student(hash)
|
||||
Student.new(
|
||||
id: hash['id'],
|
||||
git: hash['git'],
|
||||
surname: hash['surname'],
|
||||
name: hash['name'],
|
||||
patronymic: hash['patronymic'],
|
||||
birth_date: Date.parse(hash['birth_date']),
|
||||
phone: hash['phone'],
|
||||
telegram: hash['telegram'],
|
||||
email: hash['email']
|
||||
)
|
||||
end
|
||||
end
|
@ -0,0 +1,39 @@
|
||||
require_relative 'student'
|
||||
|
||||
class StudentRepository
|
||||
def self.read_from_txt(file_path)
|
||||
raise IOError, "File does not exist: #{file_path}" unless File.exist?(file_path)
|
||||
|
||||
students = []
|
||||
|
||||
File.foreach(file_path) do |line|
|
||||
line.strip!
|
||||
next if line.empty?
|
||||
|
||||
begin
|
||||
student = Student.from_string(line)
|
||||
students << student
|
||||
rescue ArgumentError => e
|
||||
puts "Error processing line: '#{line}'. Reason: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
students
|
||||
end
|
||||
|
||||
def self.write_to_txt(file_path, students)
|
||||
unless students.is_a?(Array) && students.all? { |s| s.is_a?(Student) }
|
||||
raise ArgumentError, 'Expected an array of Student objects'
|
||||
end
|
||||
|
||||
File.open(file_path, 'w') do |file|
|
||||
students.each do |student|
|
||||
file.puts student.to_s
|
||||
end
|
||||
end
|
||||
|
||||
puts "Data successfully written to #{file_path}"
|
||||
rescue IOError => e
|
||||
puts "An error occurred while writing to the file: #{e.message}"
|
||||
end
|
||||
end
|
@ -1,31 +1,59 @@
|
||||
require_relative 'person'
|
||||
|
||||
class StudentShort < Person
|
||||
attr_reader :surname_initials, :contact
|
||||
attr_reader :surname_initials
|
||||
|
||||
def initialize(student)
|
||||
super(id: student.id, git: student.git_info)
|
||||
@surname_initials = student.surname_and_initials
|
||||
@contact = student.contact_info
|
||||
def initialize(id:, surname_initials:, phone: nil, telegram: nil, email: nil)
|
||||
super(id: id, phone: phone, telegram: telegram, email: email)
|
||||
@surname_initials = surname_initials
|
||||
freeze
|
||||
end
|
||||
|
||||
def self.from_student(student)
|
||||
new(
|
||||
id: student.id,
|
||||
surname_initials: student.surname_initials,
|
||||
phone: student.phone,
|
||||
telegram: student.telegram,
|
||||
email: student.email
|
||||
)
|
||||
end
|
||||
|
||||
def self.from_string(id, info_string)
|
||||
parts = info_string.split(', ').map(&:strip)
|
||||
parts = info_string.split(',').map(&:strip)
|
||||
raise ArgumentError, 'Invalid info string format' if parts.size < 2
|
||||
|
||||
surname_initials = parts[0]
|
||||
git = parts[1].split(': ').last
|
||||
contact = parts[2].split(': ').last
|
||||
contact_string = parts[1].split(': ', 2).last.strip
|
||||
|
||||
phone, telegram, email = parse_contact_string(contact_string)
|
||||
|
||||
new_instance = allocate
|
||||
new_instance.send(:initialize_from_data, id, surname_initials, git, contact)
|
||||
new_instance
|
||||
new(
|
||||
id: id,
|
||||
surname_initials: surname_initials,
|
||||
phone: phone,
|
||||
telegram: telegram,
|
||||
email: email
|
||||
)
|
||||
end
|
||||
|
||||
def to_s
|
||||
contact_info = get_first_contact()
|
||||
"#{@surname_initials}, Contact: #{contact_info}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_from_data(id, surname_initials, git, contact)
|
||||
@id = id
|
||||
@surname_initials = surname_initials
|
||||
@git = git
|
||||
@contact = contact
|
||||
def self.parse_contact_string(contact_string)
|
||||
case contact_string
|
||||
when /\APhone: (.+)\z/i
|
||||
[Regexp.last_match(1).strip, nil, nil]
|
||||
when /\ATelegram: (.+)\z/i
|
||||
[nil, Regexp.last_match(1).strip, nil]
|
||||
when /\AEmail: (.+)\z/i
|
||||
[nil, nil, Regexp.last_match(1).strip]
|
||||
else
|
||||
raise ArgumentError, "Invalid contact string format: #{contact_string}"
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,39 @@
|
||||
require_relative '../data_table'
|
||||
|
||||
data = [
|
||||
[1, "Alice", 25],
|
||||
[2, "Bob", 30],
|
||||
[3, "Charlie", 35]
|
||||
]
|
||||
|
||||
data_table = DataTable.new(data)
|
||||
|
||||
puts "Количество строк: #{data_table.rows_count}" # Ожидаем 3
|
||||
puts "Количество столбцов: #{data_table.columns_count}" # Ожидаем 3
|
||||
puts "Элемент в строке 1, столбце 2: #{data_table.item(1, 2)}" # Ожидаем 30
|
||||
puts "Элемент в строке 0, столбце 1: #{data_table.item(0, 1)}" # Ожидаем "Alice"
|
||||
puts "Элемент за пределами таблицы: #{data_table.item(10, 10)}" # Ожидаем nil
|
||||
|
||||
items = [
|
||||
{ id: 1, name: "Alice", age: 25 },
|
||||
{ id: 2, name: "Bob", age: 30 },
|
||||
{ id: 3, name: "Charlie", age: 35 }
|
||||
]
|
||||
|
||||
data_list = DataList.new(items)
|
||||
|
||||
data_list.select(0)
|
||||
data_list.select(2)
|
||||
puts "Выбранные элементы: #{data_list.get_selected.inspect}"
|
||||
|
||||
begin
|
||||
data_list.get_names
|
||||
rescue NotImplementedError => e
|
||||
puts "Ошибка: #{e.message}" # Ожидаем "Implement this method in a subclass"
|
||||
end
|
||||
|
||||
begin
|
||||
data_list.get_data
|
||||
rescue NotImplementedError => e
|
||||
puts "Ошибка: #{e.message}" # Ожидаем "Implement this method in a subclass"
|
||||
end
|
Loading…
Reference in new issue