diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..757b643 --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +== aero + +Все права на данный проект принадлежат ГОУ МГИУ. Использование его +в коммерческих или других целях является преступлением, преследуемым +законодательством РФ и зарубежных стран. \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..8fb67e1 --- /dev/null +++ b/README @@ -0,0 +1,39 @@ +== aero + +Для установки проделайте следующие шаги: + + 0. Распаковываем проект + 0.1. scp архив_с_проектом мой_новый_логин_на_удаленной_машине@asrv9-ctx-7:/куда_положить_архив + 0.2. если спросило (yes/no) печатаем yes и нажимаем Enter + 0.3. ssh asrv9-ctx-7 -l мой_новый_логин_на_этой_машине + 0.4. см. пункт 0.2. + 0.5. cd куда_положить_архив + 0.6. tar xzvf архив_с_проектом + (теперь путь_до_папки_с_проектом = /куда_положить_архив/aero) + 1. Инициализируем свою разработческую базу данных при помощи initdb + 1.1. export LC_ALL=ru_RU.UTF-8 + 1.2. initdb -D папка_где_я_хочу_хранить_базу + 1.3. pg_ctl -D папка_где_я_хочу_хранить_базу -o "-i -p мой_уникальный_порт" start + 1.4. создаем в репозитории пользователя worker + 1.4.1. psql -p мой_уникальный_порт template1 + 1.4.2. выполняем в БД команду: + CREATE USER worker WITH PASSWORD 'worker' CREATEDB; + 1.4.3. выходим, нажав Ctrl+d + 2. Создаем ссылки на свое приложение в папке /var/www + 2.1. cd /var/www/cgi-bin/мой_новый_логин_на_удаленной_машине + 2.2. ln -sf путь_до_папки_с_проектом/lib aero + 2.3. cd /var/www/html/мой_новый_логин_на_удаленной_машине + 2.4. ln -sf путь_до_папки_с_проектом/lib/stylesheets + 2.5. меняем в файле проекта путь_до_папки_с_проектом/lib/templates/layout/header.rb + адрес /stylesheets/aero.css на /мой_новый_логин_на_удаленной_машине/stylesheets/aero.css + 3. Инициализируем БД + 3.1. Меняем в файле путь_до_папки_с_проектом/lib/db_core/db_driver.rb порт 5432 на мой_уникальный_порт + 3.2. cd путь_до_папки_с_проектом + 3.3. rake test + 4. Открываем в браузере + 4.1. Открываем свою папку и папку с проектом на просмотр для всех: + 4.1.1. chmod 755 ~ + 4.1.2. chmod 755 путь_до_папки_с_проектом -R + 4.2. Запускаем браузер + 4.3. Открываем адрес: asrv9-ctx-7.msiu.ru/cgi-bin/мой_новый_логин_на_удаленной_машине/aero/aero.rb + 5. Если что-то не так, то возвращаемся к пункту 0 и внимательно ищем ошибку! \ No newline at end of file diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..daad7e9 --- /dev/null +++ b/Rakefile @@ -0,0 +1,45 @@ +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +require 'rubygems' +require 'rake' +require 'rake/clean' +require 'rake/gempackagetask' +require 'rake/rdoctask' +require 'rake/testtask' + +spec = Gem::Specification.new do |s| + s.name = 'aero' + s.version = '0.0.1' + s.has_rdoc = true + s.extra_rdoc_files = ['README', 'LICENSE'] + s.summary = 'Your summary here' + s.description = s.summary + s.author = '' + s.email = '' + # s.executables = ['your_executable_here'] + s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*") + s.require_path = "lib" + s.bindir = "bin" +end + +Rake::GemPackageTask.new(spec) do |p| + p.gem_spec = spec + p.need_tar = true + p.need_zip = true +end + +Rake::RDocTask.new do |rdoc| + files =['README', 'LICENSE', 'lib/**/*.rb'] + rdoc.rdoc_files.add(files) + rdoc.main = "README" # page to start on + rdoc.title = "aero Docs" + rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder + rdoc.options << '--line-numbers' +end + +Rake::TestTask.new do |t| + t.test_files = FileList['test/**/*.rb'] +end diff --git a/lib/aero.rb b/lib/aero.rb new file mode 100644 index 0000000..c650fd7 --- /dev/null +++ b/lib/aero.rb @@ -0,0 +1,46 @@ +#!/usr/local/bin/ruby + +require 'cgi' +require 'controllers/controller' +require 'convertors' + +DEFAULT_CONTROLLER = 'Flights' +DEFAULT_ACTION = 'departure_list' + +def valid_controllers() + Dir[File.join(File.dirname(__FILE__), 'controllers', "*.rb")].map do + |f| Convertors::controller_file_to_class_name(f) + end +end + +def render() + cgi = CGI.new('html4') + begin + unless cgi.params.include?('controller') or + valid_controllers.include?(cgi.params['controller'][0]) + cgi.params['controller'] = [DEFAULT_CONTROLLER] + cgi.params['action'] = [DEFAULT_ACTION] + end + c = eval(cgi.params['controller'][0] + 'Controller').new(cgi) + cgi.out({ + "type" => "text/html; charset=utf-8", + "language" => "ru" + }){ c.response() } + rescue Exception => e + cgi.out({ + "type" => "text/html; charset=utf-8", + "language" => "ru" + }){ display_errors(e) } + end +end + +def display_errors(e) + " + + Error! + #{e.message}
#{e.backtrace.join('
')} + + " +end + +render() diff --git a/lib/controllers/auth_controller.rb b/lib/controllers/auth_controller.rb new file mode 100644 index 0000000..63420eb --- /dev/null +++ b/lib/controllers/auth_controller.rb @@ -0,0 +1,25 @@ +class AuthController < Controller + def is_authorized_action?() + return false + end + + def login() + if @cgi.params['action'][0] == 'login' and + @cgi.params['controller'][0] == 'Auth' + @reverse_action = DEFAULT_ACTION + @reverse_controller = DEFAULT_CONTROLLER + else + @reverse_action = @cgi.params['action'][0] + @reverse_controller = @cgi.params['controller'][0] + end + render_template(@action) + end + + def logout() + @session.delete() + @session = nil + @reverse_action = DEFAULT_ACTION + @reverse_controller = DEFAULT_CONTROLLER + render_template(@action) + end +end diff --git a/lib/controllers/controller.rb b/lib/controllers/controller.rb new file mode 100644 index 0000000..54d8648 --- /dev/null +++ b/lib/controllers/controller.rb @@ -0,0 +1,197 @@ +require 'cgi' +require 'cgi/session' +require 'cgi/session/pstore' +require 'convertors' +require 'db_core/db_driver' +require 'rubygems' +require 'controllers/helper' + +# Подключение всех db_core/models/*.rb +Dir[File.join(File.dirname(__FILE__), '..', 'db_core', 'models', "*.rb")].each do |f| + require(f) +end + +class Controller + include Helper + + DEFAULT_CONTROLLER = :Flights + DEFAULT_ACTION = :list + + # Список всех контроллеров + def Controller.all_controllers() + Dir[File.join(File.dirname(__FILE__), '', "*.rb")].select do |f| + File.basename(f) != 'controller.rb' + end.map{ |f| Convertors.controller_file_to_class_name(f) } + end + + # Методы, выполняющиеся до запуска основного действия + def before_filters + [:session, :set_action, :auth, :login_link, :menu] + end + + # Методы, выполняющиеся после генерации осноного действия + def after_filters + [:save_session] + end + + # Создание и/или подключение к сессии + def session() + @cgi = CGI.new('html4') if @cgi.nil? + @session = CGI::Session.new(@cgi, + 'database_manager' => CGI::Session::PStore, + 'session_key' => '_aero_sess_id', + 'session_expires' => Time.now + 5 * 60, + 'prefix' => 'pstore_aero_sid_') if @session.nil? + end + + # Закрытие сессии + def save_session() + @session.close() unless @session.nil? + end + + # Проверка прав доступа и, если нужно, перенаправление на ввод пароля + def auth() + if !@session['user'].nil? and @session['user'] != '' + @user = @session['user'] + elsif @cgi.params.has_key?('user') and @cgi.params['user'][0] != '' and + @cgi.params.has_key?('password') and @cgi.params['password'][0] != '' + if User.check_user(@db, @cgi.params['user'][0], @cgi.params['password'][0]) + @user = @cgi.params['user'][0] + @session['user'] = @user + else + @action = :login + @controller = :Auth + end + elsif !self.is_authorized_action? + @user = nil + else + @action = :login + @controller = :Auth + end + end + + # Формирование ссылки в левом верхнем углу Вход/Выход + def login_link() + if @controller == :Auth + @login_link_html = ' ' + elsif !@user.nil? + @login_link_html = render_layout('logout_link') + else + @login_link_html = render_layout('login_link') + end + end + + # Формирование меню + def menu() + @menu = Controller.all_controllers.map do |c| + eval(c + 'Controller').actions(@user).map do |a| + "
  • #{a[1]}
  • " + end.join("\n") + end.join("\n") + @menu = '' unless @menu == '' + end + + # Метод, отвечающий требует ли данный запрос доступа по паролю или нет + def is_authorized_action?() + return true + end + + # Установка значение controller/action + def set_action() + params = @cgi.params + @action = params['action'][0].to_sym if @action.nil? + @controller = (params.has_key?('controller') ? + params['controller'][0].to_sym : + self.class.to_sym) if @controller.nil? + end + + # Проверяет где искать метод (в текущем контроллере или нет) + def my_action?() + return @controller.to_s + 'Controller' == self.class.to_s + end + + # Формирование html документа + def response() + render_layout('header', :rb) + + @html + + render_layout('footer', :rb) + end + + # Формирование только содержимого тела html документа + def response_withot_layout() + @html + end + + # Конструктор + def initialize(cgi = nil, session = nil, action = nil, controller = nil) + @action = action + @controller = controller + @db = DbDriver.instance() + @cgi = cgi unless cgi.nil? + @session = session unless session.nil? + + before_filters().each{ |f| self.send(f.to_s) } + + if my_action?() + @html = self.send(@action.to_s) + else + c = eval(@controller.to_s + 'Controller').new(@cgi, @session, @action, + @controller) + @html = c.response_withot_layout() + end + + after_filters().each{ |f| self.send(f.to_s) } + DbDriver.close() + end + + # Загружает html-код из файла в строку + def render_template(name, mode = :rb) + if mode == :rb + f = File.new("templates/#{Convertors.class_name_to_controller_dir(@controller)}/#{name}.rb") + html = eval(f.read) + f.close + return html + else + f = File.new("templates/#{@controller}/#{name}") + html = f.read + f.close + return html + end + end + + # Загружает html-код из файла, лежащего в папке layout, в строку + def render_layout(name, mode = :html) + if mode == :rb + f = File.new("templates/layout/#{name}.rb") + html = eval(f.read) + f.close + return html + else + f = File.new("templates/layout/#{name}.html") + html = f.read + f.close + return html + end + end + + # Возвращает списки элементов меню + def Controller.actions(user) + [] + end + + # Выбирает из параметров только параметры с именем вида: prefix[имя] + def filter_for_params(prefix = 'item') + keys = @cgi.params.keys.select{ |k| k =~ /^#{prefix}/ }.map do + |k| k.gsub(/^#{prefix}\[(.*)\]$/, '\1') + end + params = {} + keys.each{ |k| params[k] = @cgi.params["#{prefix}[#{k}]"] } + return params + end +end + +# Подключение всех *_controller.rb +Dir[File.join(File.dirname(__FILE__), '', "*.rb")].each do |f| + require f if File.basename(f) != 'controller.rb' +end + diff --git a/lib/controllers/flights_controller.rb b/lib/controllers/flights_controller.rb new file mode 100644 index 0000000..e929f8b --- /dev/null +++ b/lib/controllers/flights_controller.rb @@ -0,0 +1,81 @@ +class FlightsController < Controller + def is_authorized_action?() + !([:departure_list, :arrival_list].include?(@action)) + end + + def list() + @items = Flight.find_all(@db) + render_template(@action) + end + + def departure_list() + @items = Flight.find_all(@db, true) + @header = 'Вылетающие рейсы' + render_template('easy_list') + end + + def arrival_list() + @items = Flight.find_all(@db, false) + @header = 'Прилетающие рейсы' + render_template('easy_list') + end + + def edit() + if @cgi.params.has_key?('is_commit') + params = filter_for_params() + if params.has_key?('id') and params['id'][0] != '' + @item = Flight.find_first(@db, params['id'][0]) + @header = 'Редактирование информации о рейсе' + @message = 'Информация о рейсе записана' + if @item.nil? + @item = Flight.new + @header = 'Внесение новой информации о рейсах' + @message = 'Информация о новом рейсе внесена в БД' + end + else + @item = Flight.new + @header = 'Внесение новой информации о рейсах' + @message = 'Информация о новом рейсе внесена в БД' + end + params.each do |k, v| + @item[k] = v[0] if k != 'id' and v != '' + end + @item.save(@db) + else + if @cgi.params.has_key?('id') + @item = Flight.find_first(@db, @cgi.params['id'][0]) + @header = 'Редактирование информации о рейсе' + if @item.nil? + @item = Flight.new + @header = 'Внесение новой информации о рейсах' + end + else + @item = Flight.new + @header = 'Внесение новой информации о рейсах' + end + end + render_template('edit') + end + + def destroy() + if @cgi.params.has_key?('id') and @cgi.params['id'][0] != '' + @item = Flight.find_first(@db, @cgi.params['id'][0]) + unless @item.nil? + @item.destroy(@db) + @message = 'Объект удален!' + else + @message = 'Объект не найден!' + end + end + render_template(@action) + end + + def FlightsController.actions(user) + result = [[:departure_list, 'Вылетающие рейсы'], + [:arrival_list, 'Прилетающие рейсы']] + unless user.nil? + result += [[:list, 'Все рейсы']] + end + result + end +end diff --git a/lib/controllers/helper.rb b/lib/controllers/helper.rb new file mode 100644 index 0000000..185b9a7 --- /dev/null +++ b/lib/controllers/helper.rb @@ -0,0 +1,22 @@ +module Helper + def companies_select(name, selected) + "" + end + + def is_departure_select(name, selected = true) + options = {:true => 'отлетающий', :false => 'прилетающий'} + "" + end +end diff --git a/lib/convertors.rb b/lib/convertors.rb new file mode 100644 index 0000000..bd60f36 --- /dev/null +++ b/lib/convertors.rb @@ -0,0 +1,25 @@ +module Convertors + def Convertors.controller_file_to_class_name(file) + File.basename(file)[0..-14].split(/_/).map{ |i| i.capitalize }.join + end + + def Convertors.controller_file_to_full_class_name(file) + File.basename(file)[0..-4].split(/_/).map{ |i| i.capitalize }.join + end + + def Convertors.controller_file_to_dir(file) + File.basename(file)[0..-14] + end + + def Convertors.full_class_name_to_dir(class_name) + Convertors.class_name_to_controller_dir(File.basename(class_name.to_s)[0..-11]) + end + + def Convertors.class_name_to_controller_file(class_name) + class_name.to_s.gsub(/(\W)/, '_\1').downcase + '_controller.rb' + end + + def Convertors.class_name_to_controller_dir(class_name) + class_name.to_s.gsub(/(\W)/, '_\1').downcase + end +end diff --git a/lib/db_core/create_database.rb b/lib/db_core/create_database.rb new file mode 100644 index 0000000..f48500c --- /dev/null +++ b/lib/db_core/create_database.rb @@ -0,0 +1,85 @@ +require 'db_core/db_driver' + +# Подключает все файлы db_core/model/*.rb +Dir[File.join(File.dirname(__FILE__), 'models', "*.rb")].each do |f| + require(f) +end + +class CreateDatabase + # Используемые модели + MODELS = [Terminal, CheckInDesk, Company, Flight, + CheckInDeskFlight, FlightTerminal, FlightStatus, User] + CREATE_OK = 1 + MODELS.size + + def initialize() + end + + # Создание пустой БД и всех таблиц. Внимание! БД сначала удаляется! + def create + creation_counter = 0 + @db_empty = DbDriver.empty_connection() + creation_counter += self.drop_db() + creation_counter += self.create_db() + creation_counter += self.create_tables() + @db_empty.disconnect() + return creation_counter + end + + # Создание пустой БД + def create_db() + begin + @db_empty.do("CREATE DATABASE aero") + return 1 + rescue DBI::ProgrammingError => e + return 0 + end + end + + # Удаление БД + def drop_db() + begin + @db_empty.do("DROP DATABASE aero") + return 1 + rescue DBI::ProgrammingError => e + return 0 + end + end + + # См. константу MODELS + def models() + MODELS + end + + # Создание всех таблиц + def create_tables() + creation_counter = 0 + db = DbDriver.instance() + self.models.each do |cls| + creation_counter += 1 if cls.create_table(db) + end + DbDriver.close() + return creation_counter + end + + # Тестовое добавление данных + def insert_test_data() + db = DbDriver.instance() + c = Company.new + c[:name] = 'Сибирь' + c[:code] = 'SB' + c[:description] = 'Авиакомпания "Сибирь" - Россия' + c.save(db) + f = Flight.new + f[:code] = (c[:code] + '00001') + f[:arrival_date] = '2009-10-10 10:00:00' + f[:departure_date] = '2009-10-10 22:00:00' + f[:arrival_place] = 'Лондон' + f[:departure_place] = 'Москва' + f[:arrival_airport] = 'Хитроу' + f[:departure_airport] = 'Внуково' + f[:is_departure] = true + f[:company_id] = c[:id] + f.save(db) + DbDriver.close() + end +end diff --git a/lib/db_core/db_driver.rb b/lib/db_core/db_driver.rb new file mode 100644 index 0000000..66ae593 --- /dev/null +++ b/lib/db_core/db_driver.rb @@ -0,0 +1,42 @@ +require 'rubygems' +require 'dbi' +require 'singleton' + +class DbDriver + include Singleton + + HOST = 'localhost' + PORT = 5432 + USER = 'worker' + PASSWORD = 'worker' + DBNAME = 'aero' + DBEMPTYNAME = 'template1' + + # Возвращает указатель на установленное соединение + def DbDriver.instance() + if @connection.nil? or !@connection.connected? + DbDriver.connect() + end + return @connection + end + + # Устанавливает соединение с БД + def DbDriver.connect() + @connection = DBI.connect("dbi:Pg:dbname=#{DBNAME};host=#{HOST};port=#{PORT}", + USER, PASSWORD, 'AutoCommit' => true, 'pg_client_encoding' => 'UTF-8') + end + + # Устанавливает соединение с пустым репозиторием (необходимо для createdb) + def DbDriver.empty_connection() + if @empty_connection.nil? or !@empty_connection.connected? + @empty_connection = DBI.connect("dbi:Pg:dbname=#{DBEMPTYNAME};host=#{HOST};port=#{PORT}", + USER, PASSWORD, 'AutoCommit' => true, 'pg_client_encoding' => 'UTF-8') + end + return @empty_connection + end + + # Закрывает соединение с БД + def DbDriver.close() + @connection.disconnect if @connection.connected? + end +end \ No newline at end of file diff --git a/lib/db_core/models/check_in_desk.rb b/lib/db_core/models/check_in_desk.rb new file mode 100644 index 0000000..16586d2 --- /dev/null +++ b/lib/db_core/models/check_in_desk.rb @@ -0,0 +1,18 @@ +require 'db_core/models/model' + +class CheckInDesk < Model + def CheckInDesk.create_table(connection) + begin + connection.do(" +CREATE TABLE check_in_desks( + id serial PRIMARY KEY, + name varchar(16) UNIQUE NOT NULL, + description text +) WITH OIDS + ") + return true + rescue DBI::ProgrammingError => e + return false + end + end +end diff --git a/lib/db_core/models/check_in_desk_flight.rb b/lib/db_core/models/check_in_desk_flight.rb new file mode 100644 index 0000000..7f0f287 --- /dev/null +++ b/lib/db_core/models/check_in_desk_flight.rb @@ -0,0 +1,20 @@ +require 'db_core/models/model' + +class CheckInDeskFlight < Model + def CheckInDeskFlight.create_table(connection) + begin + connection.do(" +CREATE TABLE check_id_desk_flights( + id serial PRIMARY KEY, + flight_id integer REFERENCES flights(id) NOT NULL, + check_in_desk_id integer REFERENCES check_in_desks(id) NOT NULL, + info text, + UNIQUE(flight_id, check_in_desk_id) +) WITH OIDS + ") + return true + rescue DBI::ProgrammingError => e + return false + end + end +end diff --git a/lib/db_core/models/company.rb b/lib/db_core/models/company.rb new file mode 100644 index 0000000..77f4044 --- /dev/null +++ b/lib/db_core/models/company.rb @@ -0,0 +1,35 @@ +require 'db_core/models/model' + +class Company < Model + def Company.create_table(connection) + begin + connection.do(" +CREATE TABLE companies( + id serial PRIMARY KEY, + name text UNIQUE NOT NULL, + code varchar(8) UNIQUE NOT NULL, + description text +) WITH OIDS + ") + return true + rescue DBI::ProgrammingError => e + return false + end + end + + def Company.table_name() + return 'companies' + end + + def initialize(attributes = {}) + @attributes = { + :id => nil, + :name => nil, + :code => nil, + :description => nil + } + attributes.each do |k, v| + @attributes[k] = v + end + end +end diff --git a/lib/db_core/models/flight.rb b/lib/db_core/models/flight.rb new file mode 100644 index 0000000..675df16 --- /dev/null +++ b/lib/db_core/models/flight.rb @@ -0,0 +1,88 @@ +require 'db_core/models/model' + +class Flight < Model + attr_reader :company + attr_writer :company + + def Flight.create_table(connection) + begin + connection.do(" +CREATE TABLE flights( + id serial PRIMARY KEY, + code varchar(16) UNIQUE NOT NULL, + arrival_date timestamp NOT NULL, + departure_date timestamp NOT NULL, + arrival_place text NOT NULL, + departure_place text NOT NULL, + arrival_airport text NOT NULL, + departure_airport text NOT NULL, + company_id integer REFERENCES companies(id) NOT NULL, + is_departure boolean NOT NULL DEFAULT true +) WITH OIDS + ") + return true + rescue DBI::ProgrammingError => e + return false + end + end + + def initialize(attributes = {}) + @company = nil + @attributes = { + :id => nil, + :code => nil, + :arrival_date => nil, + :departure_date => nil, + :arrival_place => nil, + :departure_place => nil, + :arrival_airport => nil, + :departure_airport => nil, + :company_id => nil, + :is_departure => nil + } + attributes.each do |k, v| + @attributes[k.to_sym] = v unless v.nil? or v == '' + end + end + + def company_name() + @company.nil? ? ' ' : @company[:name] + end + + def Flight.find_all(connection, departure = nil) + query = [] + res = [] + if departure.nil? + query = ["SELECT * FROM flights + ORDER BY departure_place, departure_date"] + else + query = ["SELECT * FROM flights + WHERE is_departure = ? + ORDER BY departure_place, departure_date", departure] + end + connection.select_all(*query) do |r| + f = self.new + r.column_names.each do |c| + f[c.to_sym] = r[c] + end + f.company = Company.find_first(connection, + f[:company_id]) unless f[:company_id].nil? + res << f + end + return res + end + + def Flight.find_first(connection, id) + id = id.to_i + query = ["SELECT * FROM #{table_name()} WHERE id = ?", id] + r = connection.select_one(*query) + return nil if r.nil? + f = self.new + r.column_names.each do |c| + f[c.to_sym] = r[c] + end + f.company = Company.find_first(connection, + f[:company_id]) unless f[:company_id].nil? + return f + end +end diff --git a/lib/db_core/models/flight_status.rb b/lib/db_core/models/flight_status.rb new file mode 100644 index 0000000..00a11f5 --- /dev/null +++ b/lib/db_core/models/flight_status.rb @@ -0,0 +1,22 @@ +require 'db_core/models/model' + +class FlightStatus < Model + STATUSES = {0 => 'отменен', 1 => 'задержан', 2 => 'вылетел', 3 => 'сел'} + + def FlightStatus.create_table(connection) + begin + connection.do(" +CREATE TABLE flight_statuses( + id serial PRIMARY KEY, + flight_id integer REFERENCES flights(id) NOT NULL, + status_id integer NOT NULL CONSTRAINT status_id_ck CHECK(status_id in(0, 1, 2, 3)), + event_date timestamp NOT NULL, + UNIQUE(flight_id, event_date) +) WITH OIDS + ") + return true + rescue DBI::ProgrammingError => e + return false + end + end +end diff --git a/lib/db_core/models/flight_terminal.rb b/lib/db_core/models/flight_terminal.rb new file mode 100644 index 0000000..23d2394 --- /dev/null +++ b/lib/db_core/models/flight_terminal.rb @@ -0,0 +1,20 @@ +require 'db_core/models/model' + +class FlightTerminal < Model + def FlightTerminal.create_table(connection) + begin + connection.do(" +CREATE TABLE flight_terminals( + id serial PRIMARY KEY, + flight_id integer REFERENCES flights(id) NOT NULL, + terminal_id integer REFERENCES terminals(id) NOT NULL, + info text, + UNIQUE(flight_id, terminal_id) +) WITH OIDS + ") + return true + rescue DBI::ProgrammingError => e + return false + end + end +end diff --git a/lib/db_core/models/model.rb b/lib/db_core/models/model.rb new file mode 100644 index 0000000..bd510b2 --- /dev/null +++ b/lib/db_core/models/model.rb @@ -0,0 +1,94 @@ +class Model + # Описывает какие поля не надо преобразовывать в NULL + def Model.not_needs_to_set_null() + [] + end + + # Метод, создающий в БД таблицу, описывающую модель + def Model.create_table(connection) + return false + end + + # Имя таблицы, описывающей модель + def table_name() + self.class.table_name() + end + + # См. table_name() + def Model.table_name() + self.to_s.gsub(/(\W)/, '_\1').downcase + 's' + end + + # Конструктор + def initialize(attributes = {}) + @attributes = {} + end + + # Позволяет обращаться к полям объекта, минуя @attributes + def [](name) + @attributes[name.to_sym] + end + + # Позволяет присваивать полям объекта значения, минуя @attributes + def []=(name, value) + if value == '' and !Model.not_needs_to_set_null().include?(name.to_sym) + value = nil + end + @attributes[name.to_sym] = value + end + + # Синхронизирует объет с БД + def save(connection) + query = '' + params = [] + if self[:id].nil? + query = "INSERT INTO #{self.table_name()}(" + + (@attributes.keys - [:id]).join(', ') + ') VALUES(' + + (@attributes.keys - [:id]).map{ |k| '?' }.join(', ') + ') RETURNING id' + params = (@attributes.keys - [:id]).map{ |k| self[k] } + else + query = "UPDATE #{self.table_name()} SET " + + (@attributes.keys - [:id]).map{ |k| "#{k} = ?" }.join(', ') + + ' WHERE id = ? RETURNING id' + params = (@attributes.keys - [:id]).map{ |k| self[k] } << self[:id] + end + rs = connection.select_one(query, *params) + self[:id] = rs[0].to_i + end + + # Достает из БД все объекты данного вида + def Model.find_all(connection) + query = [] + res = [] + query = ["SELECT * FROM #{table_name()}"] + connection.select_all(*query) do |r| + o = self.new + r.column_names.each do |c| + o[c.to_sym] = r[c] + end + res << o + end + return res + end + + # Достает из БД объект по его id + def Model.find_first(connection, id) + id = id.to_i + query = ["SELECT * FROM #{table_name()} WHERE id = ?", id] + r = connection.select_one(*query) + return nil if r.nil? + o = self.new + r.column_names.each do |c| + o[c.to_sym] = r[c] + end + return o + end + + # Удаляет объект в БД + + def destroy(connection) + unless self[:id].nil? + connection.do("DELETE FROM #{table_name()} WHERE id = ?", self[:id]) + end + end +end diff --git a/lib/db_core/models/terminal.rb b/lib/db_core/models/terminal.rb new file mode 100644 index 0000000..b5e5ecf --- /dev/null +++ b/lib/db_core/models/terminal.rb @@ -0,0 +1,19 @@ +require 'db_core/models/model' + +class Terminal < Model + def Terminal.create_table(connection) + begin + connection.do(" +CREATE TABLE terminals( + id serial PRIMARY KEY, + name varchar(8) UNIQUE NOT NULL, + description text, + needs_bus boolean NOT NULL DEFAULT false +) WITH OIDS + ") + return true + rescue DBI::ProgrammingError => e + return false + end + end +end diff --git a/lib/db_core/models/user.rb b/lib/db_core/models/user.rb new file mode 100644 index 0000000..12ba6a7 --- /dev/null +++ b/lib/db_core/models/user.rb @@ -0,0 +1,34 @@ +# To change this template, choose Tools | Templates +# and open the template in the editor. + +class User < Model + def User.create_table(connection) + begin + connection.do(" +CREATE TABLE users( + id serial PRIMARY KEY, + login varchar(16) UNIQUE NOT NULL, + password varchar(16) NOT NULL, + info text +) WITH OIDS + ") + return true + rescue DBI::ProgrammingError => e + return false + end + end + + def User.check_user(connection, login, password) + id = connection.select_one(" +SELECT id FROM users WHERE login = ? and password = ? + ", login, password) + return (id.size > 0) + end + + def User.create_admin(connection) + connection.do(" +INSERT INTO users(login, password, info) + VALUES('admin', 'qwerty', 'Администратор системы') + ") + end +end diff --git a/lib/stylesheets/aero.css b/lib/stylesheets/aero.css new file mode 100644 index 0000000..6030846 --- /dev/null +++ b/lib/stylesheets/aero.css @@ -0,0 +1,96 @@ +table.layout { + border: none; + width: 800px; +} + +table.layout thead tr th { + background-color: #BBDDFF; + color: Blue; + padding: 5px 5px 5px 5px; + text-align: center; + vertical-align: middle; + border-bottom: 1px Solid Blue; + font-size: x-large; +} + +table.layout tbody tr th { + background-color: #BBDDFF; + color: Blue; + padding: 5px 5px 5px 5px; + text-align: left; + vertical-align: top; + border-right: 1px Solid Blue; + width: 200px; +} + +table.layout tbody tr th ul{ + padding: 6px; + list-style-type: none; +} + +table.layout tbody tr th ul li{ + background-color: #EEFFFF; + padding: 5px 5px 5px 5px; + width: 180px; + margin: 5px 5px 5px 5px; + border: 1px Solid #8888FF; +} + +table.layout tbody tr th ul li a:link, table.layout tbody tr th ul li a:visited{ + text-decoration: none; + color: #2200FF; + font-weight: normal; +} + +table.layout tbody tr th ul li a:hover, table.layout tbody tr th ul li a:active{ + text-decoration: none; + color: #557788; + font-weight: normal; +} + +table.layout tbody tr td { + padding: 10px 10px 10px 10px; + text-align: center; + vertical-align: middle; +} + +table.list{ + margin-left: auto; + margin-right: auto; + border: 1px Solid Blue; +} + +table.list thead tr{ + background-color: #BBDDFF; +} + +table.list thead tr th{ + border: none; + text-align: center; + vertical-align: middle; + font-size: medium; +} + +table.list tbody tr th{ + text-align: left; + vertical-align: middle; + font-size: medium; +} + +table.list tfoot tr th{ + border-width: 1px 0px 0px 0px; + border-style: solid none none none; + border-color: Blue; + text-align: center; + vertical-align: middle; + font-size: medium; + background-color: white; +} + +table.list tbody tr.list1{ + background-color: #EEEEFF; +} + +table.list tbody tr.list0{ + background-color: #DDDDFF; +} diff --git a/lib/templates/auth/login.rb b/lib/templates/auth/login.rb new file mode 100644 index 0000000..071057a --- /dev/null +++ b/lib/templates/auth/login.rb @@ -0,0 +1,27 @@ +" +
    + + +" + (@cgi.params.keys - ['controller', 'action']).map do |k| + @cgi.params[k].map do |i| + "" + end.join("\n") +end.join("\n") + +" + + + + + + + + + + + + + + +
    Пользовательское имя:
    Пароль:
    +
    +" diff --git a/lib/templates/auth/logout.rb b/lib/templates/auth/logout.rb new file mode 100644 index 0000000..b0e321a --- /dev/null +++ b/lib/templates/auth/logout.rb @@ -0,0 +1,3 @@ +" +

    Выход осуществлен!

    +" \ No newline at end of file diff --git a/lib/templates/flights/destroy.rb b/lib/templates/flights/destroy.rb new file mode 100644 index 0000000..94fcd9f --- /dev/null +++ b/lib/templates/flights/destroy.rb @@ -0,0 +1,4 @@ +" +

    #{@message}

    +

    Назад

    +" \ No newline at end of file diff --git a/lib/templates/flights/easy_list.rb b/lib/templates/flights/easy_list.rb new file mode 100644 index 0000000..88e0504 --- /dev/null +++ b/lib/templates/flights/easy_list.rb @@ -0,0 +1,37 @@ +count = 0 +" + + + + + + + + + + + +" + @items.map do |i| + count += 1 +" + + + + + + +" +end.join("\n") + " + +
    + #{@header} +
    + Номер рейса + + Вылет + + Посадка + + Авиакомпания +
    #{i[:code]}#{i[:departure_place]}, #{i[:departure_airport]}: #{i[:departure_date]}#{i[:arrival_place]}, #{i[:arrival_airport]}: #{i[:arrival_date]}#{i.company_name()}
    +" \ No newline at end of file diff --git a/lib/templates/flights/edit.rb b/lib/templates/flights/edit.rb new file mode 100644 index 0000000..b133d32 --- /dev/null +++ b/lib/templates/flights/edit.rb @@ -0,0 +1,65 @@ +" +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + #{@header}
    + #{@message} +
    Номер рейса:
    + Вылет
    +
    город:
    +
    время:
    +
    аэропорт:
    +
    +  
    +
    +
    + +
    + Посадка
    +
    город:
    +
    время:
    +
    аэропорт:
    +
    +  
    +
    +
    +
    +
    Авиакомпания:#{companies_select('item[company_id]', "#{@item[:company_id]}")}
    Каким рейс является для нашего аэропорта:#{is_departure_select('item[is_departure]', "#{@item[:is_departure]}")}
    + + +
    +" \ No newline at end of file diff --git a/lib/templates/flights/list.rb b/lib/templates/flights/list.rb new file mode 100644 index 0000000..b6a9c88 --- /dev/null +++ b/lib/templates/flights/list.rb @@ -0,0 +1,44 @@ +count = 0 +" + + + + + + + + + + + + +" + @items.map do |i| + count += 1 +" + + + + + + + +" +end.join("\n") + " + +
    + Создать новую запись +
    + Номер рейса + + Вылет + + Посадка + + Авиакомпания + + Действия +
    #{i[:code]}#{i[:departure_place]}, #{i[:departure_airport]}: #{i[:departure_date]}#{i[:arrival_place]}, #{i[:arrival_airport]}: #{i[:arrival_date]}#{i.company_name()} + Редактировать + Удалить +
    +" \ No newline at end of file diff --git a/lib/templates/layout/footer.rb b/lib/templates/layout/footer.rb new file mode 100644 index 0000000..087b5e3 --- /dev/null +++ b/lib/templates/layout/footer.rb @@ -0,0 +1,7 @@ +" + + + + + +" \ No newline at end of file diff --git a/lib/templates/layout/header.rb b/lib/templates/layout/header.rb new file mode 100644 index 0000000..2bb6147 --- /dev/null +++ b/lib/templates/layout/header.rb @@ -0,0 +1,20 @@ +" + + + Аэропорт + + + + + + + + + + + + + + +
    #{@login_link_html}Аэропорт
    #{@menu} +" diff --git a/lib/templates/layout/login_link.html b/lib/templates/layout/login_link.html new file mode 100644 index 0000000..284c196 --- /dev/null +++ b/lib/templates/layout/login_link.html @@ -0,0 +1,3 @@ + +Вход + diff --git a/lib/templates/layout/logout_link.html b/lib/templates/layout/logout_link.html new file mode 100644 index 0000000..3411e20 --- /dev/null +++ b/lib/templates/layout/logout_link.html @@ -0,0 +1,3 @@ + +Выход + diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties new file mode 100644 index 0000000..34b8c3d --- /dev/null +++ b/nbproject/private/private.properties @@ -0,0 +1 @@ +platform.active=Ruby diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml new file mode 100644 index 0000000..c1f155a --- /dev/null +++ b/nbproject/private/private.xml @@ -0,0 +1,4 @@ + + + + diff --git a/nbproject/private/rake-d.txt b/nbproject/private/rake-d.txt new file mode 100644 index 0000000..e2da934 --- /dev/null +++ b/nbproject/private/rake-d.txt @@ -0,0 +1,18 @@ +clean=Remove any temporary products. +clobber=Remove any generated file. +clobber_package=Remove package products +clobber_rdoc=Remove rdoc products +doc= +doc/rdoc= +doc/rdoc/index.html= +gem=Build the gem file aero-0.0.1.gem +package=Build all the packages +pkg= +pkg/aero-0.0.1= +pkg/aero-0.0.1.gem= +pkg/aero-0.0.1.tgz= +pkg/aero-0.0.1.zip= +rdoc=Build the rdoc HTML Files +repackage=Force a rebuild of the package files +rerdoc=Force a rebuild of the RDOC files +test=Run tests diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..22bbf01 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,7 @@ +javac.classpath= +main.file=aero.rb +platform.active=Ruby +source.encoding=UTF-8 +spec.src.dir=spec +src.dir=lib +test.src.dir=test diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..4b74d45 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.ruby.rubyproject + + + aero + + + + + + + + + + diff --git a/test/test_create_database.rb b/test/test_create_database.rb new file mode 100644 index 0000000..5a3ce97 --- /dev/null +++ b/test/test_create_database.rb @@ -0,0 +1,33 @@ +$:.unshift File.join(File.dirname(__FILE__),'..','lib') + +require 'test/unit' +require 'db_core/create_database' + +class TestCreateDatabase < Test::Unit::TestCase + def test_creation + creator = CreateDatabase.new + assert(creator.create() >= CreateDatabase::CREATE_OK, + 'Процесс создания пустой БД не может завершиться успешно!') + end + + def test_insert_test_data() + creator = CreateDatabase.new + creator.insert_test_data() + db = DbDriver.instance + res = db.select_one("SELECT count(*) FROM flights") + assert(res[0].to_i == 1, 'Тестовые данные внесены успешно!') + User.create_admin(db) + assert(User.check_user(db, 'admin', 'qwerty'), 'Ошибка добавления администратора!') + DbDriver.close() + end + + def test_models() + db = DbDriver.instance + cs = Company.find_all(db) + assert(cs.size == 1, 'Не работает find_all для модели Company!') + fs = Flight.find_all(db) + assert(fs.size == 1, 'Не работает find_all для модели Flight!') + assert(!fs[0].company.nil?, 'Не находятся связанные объекты!') + DbDriver.close() + end +end diff --git a/test/test_db_driver.rb b/test/test_db_driver.rb new file mode 100644 index 0000000..2bbbde4 --- /dev/null +++ b/test/test_db_driver.rb @@ -0,0 +1,18 @@ +$:.unshift File.join(File.dirname(__FILE__),'..','lib') + +require 'test/unit' +require 'db_core/db_driver' + +class TestDbDriver < Test::Unit::TestCase + def test_connection + begin + connection = DbDriver.instance + rescue Exception => e + assert(false, "Соединение с БД не может быть установлено! (#{e.message})") + end + res = connection.select_one("SELECT 1 as one") + assert(res, 'Не могу выполнить тестовый запрос!') + assert(res['one'].to_i == 1, 'Неверный результат запроса!') + DbDriver.close() + end +end