From 385dd98dbe03934c8cdb37412e11ea6050341fd0 Mon Sep 17 00:00:00 2001 From: Gregory Ballantine Date: Tue, 14 Mar 2023 20:49:42 -0400 Subject: [PATCH] Migrated app to modular Sinatra application; copied in a lot of improvements for a Sinatra app that I learned from Stage Manager --- .gitignore | 3 + Gemfile | 3 +- Gemfile.lock | 34 +++++++++-- Guardfile | 6 ++ Rakefile | 8 +-- {lib => app}/config.rb | 8 ++- {lib => app}/helpers.rb | 2 +- {lib => app}/models/ip_address.rb | 0 {lib => app}/models/item.rb | 0 {lib => app}/models/item_comment.rb | 0 {lib => app}/models/license.rb | 0 {lib => app}/models/license_comment.rb | 0 {lib => app}/routes.rb | 0 app/routes/index.rb | 16 +++++ app/routes/ip_tracker.rb | 40 +++++++++++++ app/routes/item.rb | 83 ++++++++++++++++++++++++++ app/routes/license.rb | 83 ++++++++++++++++++++++++++ app/routes/search.rb | 19 ++++++ config.ru | 9 +++ {data => config}/defaults.yaml | 4 ++ config/puma.rb | 6 ++ lib/routes/index.rb | 14 ----- lib/routes/ip_tracker.rb | 38 ------------ lib/routes/item.rb | 81 ------------------------- lib/routes/license.rb | 81 ------------------------- lib/routes/search.rb | 15 ----- raven.rb | 35 ----------- server.rb | 71 ++++++++++++++++++++++ 28 files changed, 384 insertions(+), 275 deletions(-) create mode 100644 Guardfile rename {lib => app}/config.rb (55%) rename {lib => app}/helpers.rb (95%) rename {lib => app}/models/ip_address.rb (100%) rename {lib => app}/models/item.rb (100%) rename {lib => app}/models/item_comment.rb (100%) rename {lib => app}/models/license.rb (100%) rename {lib => app}/models/license_comment.rb (100%) rename {lib => app}/routes.rb (100%) create mode 100644 app/routes/index.rb create mode 100644 app/routes/ip_tracker.rb create mode 100644 app/routes/item.rb create mode 100644 app/routes/license.rb create mode 100644 app/routes/search.rb create mode 100644 config.ru rename {data => config}/defaults.yaml (56%) create mode 100644 config/puma.rb delete mode 100644 lib/routes/index.rb delete mode 100644 lib/routes/ip_tracker.rb delete mode 100644 lib/routes/item.rb delete mode 100644 lib/routes/license.rb delete mode 100644 lib/routes/search.rb delete mode 100644 raven.rb create mode 100644 server.rb diff --git a/.gitignore b/.gitignore index 91ce27b..312bd31 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,9 @@ build-iPhoneSimulator/ # Local database storage data/raven.db +# Local configuration +config/raven.yaml + # Node modules for Grunt.js node_modules/ diff --git a/Gemfile b/Gemfile index 891c0ed..016e02e 100644 --- a/Gemfile +++ b/Gemfile @@ -8,5 +8,6 @@ gem 'sequel', '~> 5.63' gem 'sqlite3', '~> 1.5' # Use rerun gem to auto-reload app -gem 'rerun' +gem 'guard-rack' +gem 'wdm', '>= 0.1.0' if Gem.win_platform? diff --git a/Gemfile.lock b/Gemfile.lock index c9cd2c2..a567ed8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,39 @@ GEM remote: https://rubygems.org/ specs: + coderay (1.1.3) ffi (1.15.5) ffi (1.15.5-x64-mingw-ucrt) - listen (3.7.1) + formatador (1.1.0) + guard (2.18.0) + formatador (>= 0.2.4) + listen (>= 2.7, < 4.0) + lumberjack (>= 1.0.12, < 2.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.13.0) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-rack (2.2.1) + ffi + guard (~> 2.3) + spoon + listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) + lumberjack (1.2.8) + method_source (1.0.0) multi_json (1.15.0) mustermann (3.0.0) ruby2_keywords (~> 0.0.1) + nenv (0.3.0) nio4r (2.5.8) + notiffany (0.1.3) + nenv (~> 0.1) + shellany (~> 0.0) + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) puma (6.0.0) nio4r (~> 2.0) rack (2.2.4) @@ -18,10 +42,9 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - rerun (0.13.1) - listen (~> 3.0) ruby2_keywords (0.0.5) sequel (5.63.0) + shellany (0.0.1) sinatra (3.0.4) mustermann (~> 3.0) rack (~> 2.2, >= 2.2.4) @@ -33,8 +56,11 @@ GEM rack-protection (= 3.0.4) sinatra (= 3.0.4) tilt (~> 2.0) + spoon (0.0.6) + ffi sqlite3 (1.5.4-x64-mingw-ucrt) sqlite3 (1.5.4-x86_64-linux) + thor (1.2.1) tilt (2.0.11) PLATFORMS @@ -42,8 +68,8 @@ PLATFORMS x86_64-linux DEPENDENCIES + guard-rack puma (~> 6.0) - rerun sequel (~> 5.63) sinatra (~> 3.0) sinatra-contrib (~> 3.0) diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000..af5d78c --- /dev/null +++ b/Guardfile @@ -0,0 +1,6 @@ +guard 'rack' do + watch('Gemfile.lock') + watch('config.ru') + watch('server.rb') + watch(%r{^(lib)/.*}) +end \ No newline at end of file diff --git a/Rakefile b/Rakefile index fd6f0d8..84653f4 100644 --- a/Rakefile +++ b/Rakefile @@ -10,11 +10,11 @@ namespace :db do end namespace :server do - task :dev do - %x{ruby raven.rb} + task :start do + system("puma -C config/puma.rb") end - task :reload do - %x{rerun --no-notify 'ruby raven.rb'} + task :dev do + %x{guard} end end diff --git a/lib/config.rb b/app/config.rb similarity index 55% rename from lib/config.rb rename to app/config.rb index fe21f39..da2d625 100644 --- a/lib/config.rb +++ b/app/config.rb @@ -2,8 +2,14 @@ require 'yaml' class Config + DEFAULT_CONFIG = 'config/defaults.yaml' + def initialize(config_path) - @data = YAML::load_file(config_path) + @data = YAML::load_file(DEFAULT_CONFIG) + + if File.exists?(config_path) + @data.merge!(YAML::load_file(config_path)) + end end def get(key, depth = 0) diff --git a/lib/helpers.rb b/app/helpers.rb similarity index 95% rename from lib/helpers.rb rename to app/helpers.rb index d4ec2c8..34e6c98 100644 --- a/lib/helpers.rb +++ b/app/helpers.rb @@ -1,4 +1,4 @@ -helpers do +module Helpers def nullable(value) if (value) and (value != '') diff --git a/lib/models/ip_address.rb b/app/models/ip_address.rb similarity index 100% rename from lib/models/ip_address.rb rename to app/models/ip_address.rb diff --git a/lib/models/item.rb b/app/models/item.rb similarity index 100% rename from lib/models/item.rb rename to app/models/item.rb diff --git a/lib/models/item_comment.rb b/app/models/item_comment.rb similarity index 100% rename from lib/models/item_comment.rb rename to app/models/item_comment.rb diff --git a/lib/models/license.rb b/app/models/license.rb similarity index 100% rename from lib/models/license.rb rename to app/models/license.rb diff --git a/lib/models/license_comment.rb b/app/models/license_comment.rb similarity index 100% rename from lib/models/license_comment.rb rename to app/models/license_comment.rb diff --git a/lib/routes.rb b/app/routes.rb similarity index 100% rename from lib/routes.rb rename to app/routes.rb diff --git a/app/routes/index.rb b/app/routes/index.rb new file mode 100644 index 0000000..add4c61 --- /dev/null +++ b/app/routes/index.rb @@ -0,0 +1,16 @@ +class Raven + class IndexController + + get '/' do + items = Item.reverse(:updated_at).limit(10).all() + licenses = License.reverse(:updated_at).limit(10).all() + erb :index, :locals => { + :title => 'Dashboard', + :items => items, + :licenses => licenses + } + end + + end +end + diff --git a/app/routes/ip_tracker.rb b/app/routes/ip_tracker.rb new file mode 100644 index 0000000..1ca6a3f --- /dev/null +++ b/app/routes/ip_tracker.rb @@ -0,0 +1,40 @@ +require 'ipaddr' + +class Raven + class IpTrackerController + + get '/' do + ip_addresses = IpAddress.all() + ip_addresses.sort! { |a,b| IPAddr.new( a.address ) <=> IPAddr.new( b.address ) } + + erb :'ip/ip-tracker', :locals => { + :title => 'IP Tracker', + :ip_addresses => ip_addresses + } + end + + get '/add' do + erb :'ip/add', :locals => { + :title => 'Add IP Address' + } + end + + post '/add' do + ip = IpAddress.create( + address: params[:ip_address], + dns_name: params[:ip_dns], + comment: params[:ip_comment] + ) + + redirect '/ip-tracker' + end + + get '/delete/:ip_id' do + ip = IpAddress.where(id: params[:ip_id]).first() + ip.delete() + + redirect '/ip-tracker' + end + + end +end diff --git a/app/routes/item.rb b/app/routes/item.rb new file mode 100644 index 0000000..d4e59c1 --- /dev/null +++ b/app/routes/item.rb @@ -0,0 +1,83 @@ +class Raven + class ItemController + + get '/' do + redirect '/item/list' + end + get '/list' do + items = Item.reverse(:updated_at).all() + erb :'item/list', :locals => { + :title => 'List of Items', + :items => items + } + end + + get '/create' do + erb :'item/create', :locals => { + :title => 'Create New Item' + } + end + post '/create' do + item = Item.create( + name: params[:item_name], + serial_number: params[:item_serial], + sku_number: params[:item_sku], + purchased_from: params[:item_purchase_from], + purchased_at: params[:item_purchase_date], + manufacturer: params[:item_manufacturer], + type: params[:item_type] + ) + + redirect "/item/#{item.id}" + end + + get '/:item_id' do + item = Item.where(id: params[:item_id]).first() + puts "#{item.name}" + erb :'item/view', :locals => { + :title => item.name, + :item => item + } + end + + get '/:item_id/edit' do + item = Item.where(id: params[:item_id]).first() + puts "#{item.name}" + erb :'item/edit', :locals => { + :title => "Editing: #{item.name}", + :item => item + } + end + post '/:item_id/edit' do + item = Item.where(id: params[:item_id]).first() + + item.name = params[:item_name] + item.serial_number = params[:item_serial] + item.sku_number = params[:item_sku] + item.purchased_from = params[:item_purchase_from] + item.purchased_at = params[:item_purchase_date] + item.manufacturer = params[:item_manufacturer] + item.type = params[:item_type] + item.save() + + redirect "/item/#{item.id}" + end + + get '/:item_id/delete' do + item = Item.where(id: params[:item_id]).first() + item.delete() + + redirect '/item/list' + end + + post '/:item_id/comment' do + item = Item.first(id: params[:item_id]) + + comment = ItemComment.create(body: params[:comment_body]) + item.add_item_comment(comment) + + redirect "/item/#{item.id}" + end + + end +end diff --git a/app/routes/license.rb b/app/routes/license.rb new file mode 100644 index 0000000..4be5112 --- /dev/null +++ b/app/routes/license.rb @@ -0,0 +1,83 @@ +class Raven + class LicenseController + + get '/' do + redirect '/license/list' + end + get '/list' do + licenses = License.reverse(:updated_at).all() + erb :'license/list', :locals => { + :title => 'List of Licenses', + :licenses => licenses + } + end + + get '/create' do + erb :'license/create', :locals => { + :title => 'Create New License' + } + end + post '/create' do + license = License.create( + name: params[:license_name], + key: params[:license_key], + manufacturer: params[:license_manufacturer], + seats_used: params[:license_seats_used], + seats_total: params[:license_seats_total], + purchased_from: params[:license_purchase_from], + purchased_at: params[:license_purchase_date] + ) + + redirect "/license/#{license.id}" + end + + get '/:license_id' do + license = License.where(id: params[:license_id]).first() + puts "#{license.name}" + erb :'license/view', :locals => { + :title => license.name, + :license => license + } + end + + get '/:license_id/edit' do + license = License.where(id: params[:license_id]).first() + puts "#{license.name}" + erb :'license/edit', :locals => { + :title => "Editing: #{license.name}", + :license => license + } + end + post '/:license_id/edit' do + license = License.where(id: params[:license_id]).first() + + license.name = params[:license_name] + license.key = params[:license_key] + license.manufacturer = params[:license_manufacturer] + license.seats_used = params[:license_seats_used] + license.seats_total = params[:license_seats_total] + license.purchased_from = params[:license_purchase_from] + license.purchased_at = params[:license_purchase_date] + license.save() + + redirect "/license/#{license.id}" + end + + get '/:license_id/delete' do + license = License.where(id: params[:license_id]).first() + license.delete() + + redirect '/license/list' + end + + post '/:license_id/comment' do + license = License.first(id: params[:license_id]) + + comment = LicenseComment.create(body: params[:comment_body]) + license.add_license_comment(comment) + + redirect "/license/#{license.id}" + end + + end +end diff --git a/app/routes/search.rb b/app/routes/search.rb new file mode 100644 index 0000000..b8ef16c --- /dev/null +++ b/app/routes/search.rb @@ -0,0 +1,19 @@ +class Raven + class SearchController + + get '/' do + search_parameter = params[:query] + items = Item.where(Sequel.ilike(:name, "%#{search_parameter}%")).all() + licenses = License.where(Sequel.ilike(:name, "%#{search_parameter}%")).all() + + results = items.concat(licenses) + + erb :'search/list', :locals => { + :title => 'Search Results', + :results => results, + :query => search_parameter + } + end + + end +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..6a13800 --- /dev/null +++ b/config.ru @@ -0,0 +1,9 @@ +# Load application config +require_relative 'app/config.rb' +$conf = Config.new(File.join(__dir__, 'data/defaults.yaml')) + +# Load Sinatra server +require_relative './server.rb' + +# Run application +run Raven diff --git a/data/defaults.yaml b/config/defaults.yaml similarity index 56% rename from data/defaults.yaml rename to config/defaults.yaml index 6d7b685..0c88501 100644 --- a/data/defaults.yaml +++ b/config/defaults.yaml @@ -1,3 +1,7 @@ +server: + address: '127.0.0.1' + port: 6200 + database: adapter: 'sqlite' database: 'data/raven.db' diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..04d3a12 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,6 @@ +# Load application config +require './app/config.rb' +$conf = Config.new(File.join(__dir__, 'config/raven.yaml')) + +bind_address = "tcp://#{$conf.get('server.address')}:#{$conf.get('server.port')}" +bind bind_address diff --git a/lib/routes/index.rb b/lib/routes/index.rb deleted file mode 100644 index c37b1a7..0000000 --- a/lib/routes/index.rb +++ /dev/null @@ -1,14 +0,0 @@ -namespace '/' do - - get '' do - items = Item.reverse(:updated_at).limit(10).all() - licenses = License.reverse(:updated_at).limit(10).all() - erb :index, :locals => { - :title => 'Dashboard', - :items => items, - :licenses => licenses - } - end - -end - diff --git a/lib/routes/ip_tracker.rb b/lib/routes/ip_tracker.rb deleted file mode 100644 index 0cfe300..0000000 --- a/lib/routes/ip_tracker.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'ipaddr' - -namespace '/ip-tracker' do - - get '' do - ip_addresses = IpAddress.all() - ip_addresses.sort! { |a,b| IPAddr.new( a.address ) <=> IPAddr.new( b.address ) } - - erb :'ip/ip-tracker', :locals => { - :title => 'IP Tracker', - :ip_addresses => ip_addresses - } - end - - get '/add' do - erb :'ip/add', :locals => { - :title => 'Add IP Address' - } - end - - post '/add' do - ip = IpAddress.create( - address: params[:ip_address], - dns_name: params[:ip_dns], - comment: params[:ip_comment] - ) - - redirect '/ip-tracker' - end - - get '/delete/:ip_id' do - ip = IpAddress.where(id: params[:ip_id]).first() - ip.delete() - - redirect '/ip-tracker' - end - -end diff --git a/lib/routes/item.rb b/lib/routes/item.rb deleted file mode 100644 index 6cda3a5..0000000 --- a/lib/routes/item.rb +++ /dev/null @@ -1,81 +0,0 @@ -namespace '/item' do - - get '' do - redirect '/item/list' - end - get '/list' do - items = Item.reverse(:updated_at).all() - erb :'item/list', :locals => { - :title => 'List of Items', - :items => items - } - end - - get '/create' do - erb :'item/create', :locals => { - :title => 'Create New Item' - } - end - post '/create' do - item = Item.create( - name: params[:item_name], - serial_number: params[:item_serial], - sku_number: params[:item_sku], - purchased_from: params[:item_purchase_from], - purchased_at: params[:item_purchase_date], - manufacturer: params[:item_manufacturer], - type: params[:item_type] - ) - - redirect "/item/#{item.id}" - end - - get '/:item_id' do - item = Item.where(id: params[:item_id]).first() - puts "#{item.name}" - erb :'item/view', :locals => { - :title => item.name, - :item => item - } - end - - get '/:item_id/edit' do - item = Item.where(id: params[:item_id]).first() - puts "#{item.name}" - erb :'item/edit', :locals => { - :title => "Editing: #{item.name}", - :item => item - } - end - post '/:item_id/edit' do - item = Item.where(id: params[:item_id]).first() - - item.name = params[:item_name] - item.serial_number = params[:item_serial] - item.sku_number = params[:item_sku] - item.purchased_from = params[:item_purchase_from] - item.purchased_at = params[:item_purchase_date] - item.manufacturer = params[:item_manufacturer] - item.type = params[:item_type] - item.save() - - redirect "/item/#{item.id}" - end - - get '/:item_id/delete' do - item = Item.where(id: params[:item_id]).first() - item.delete() - - redirect '/item/list' - end - - post '/:item_id/comment' do - item = Item.first(id: params[:item_id]) - - comment = ItemComment.create(body: params[:comment_body]) - item.add_item_comment(comment) - - redirect "/item/#{item.id}" - end - -end diff --git a/lib/routes/license.rb b/lib/routes/license.rb deleted file mode 100644 index af312f9..0000000 --- a/lib/routes/license.rb +++ /dev/null @@ -1,81 +0,0 @@ -namespace '/license' do - - get '' do - redirect '/license/list' - end - get '/list' do - licenses = License.reverse(:updated_at).all() - erb :'license/list', :locals => { - :title => 'List of Licenses', - :licenses => licenses - } - end - - get '/create' do - erb :'license/create', :locals => { - :title => 'Create New License' - } - end - post '/create' do - license = License.create( - name: params[:license_name], - key: params[:license_key], - manufacturer: params[:license_manufacturer], - seats_used: params[:license_seats_used], - seats_total: params[:license_seats_total], - purchased_from: params[:license_purchase_from], - purchased_at: params[:license_purchase_date] - ) - - redirect "/license/#{license.id}" - end - - get '/:license_id' do - license = License.where(id: params[:license_id]).first() - puts "#{license.name}" - erb :'license/view', :locals => { - :title => license.name, - :license => license - } - end - - get '/:license_id/edit' do - license = License.where(id: params[:license_id]).first() - puts "#{license.name}" - erb :'license/edit', :locals => { - :title => "Editing: #{license.name}", - :license => license - } - end - post '/:license_id/edit' do - license = License.where(id: params[:license_id]).first() - - license.name = params[:license_name] - license.key = params[:license_key] - license.manufacturer = params[:license_manufacturer] - license.seats_used = params[:license_seats_used] - license.seats_total = params[:license_seats_total] - license.purchased_from = params[:license_purchase_from] - license.purchased_at = params[:license_purchase_date] - license.save() - - redirect "/license/#{license.id}" - end - - get '/:license_id/delete' do - license = License.where(id: params[:license_id]).first() - license.delete() - - redirect '/license/list' - end - - post '/:license_id/comment' do - license = License.first(id: params[:license_id]) - - comment = LicenseComment.create(body: params[:comment_body]) - license.add_license_comment(comment) - - redirect "/license/#{license.id}" - end - -end diff --git a/lib/routes/search.rb b/lib/routes/search.rb deleted file mode 100644 index 141699e..0000000 --- a/lib/routes/search.rb +++ /dev/null @@ -1,15 +0,0 @@ -namespace '/search' do - get '' do - search_parameter = params[:query] - items = Item.where(Sequel.ilike(:name, "%#{search_parameter}%")).all() - licenses = License.where(Sequel.ilike(:name, "%#{search_parameter}%")).all() - - results = items.concat(licenses) - - erb :'search/list', :locals => { - :title => 'Search Results', - :results => results, - :query => search_parameter - } - end -end diff --git a/raven.rb b/raven.rb deleted file mode 100644 index f0abbd3..0000000 --- a/raven.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'logger' -require 'sequel' -require 'sqlite3' -require 'sinatra' -require 'sinatra/namespace' - -require_relative 'lib/config.rb' - -set :public_folder, __dir__ + '/public' - -set :views, settings.root + '/views' - -# Load configuration file -conf = Config.new(File.join(__dir__, 'data/defaults.yaml')) - -# Initialize logging -logger = Logger.new(STDOUT) -logger.level = Logger::INFO - -# Load the Sequel timestamps plugin -Sequel::Model.plugin :timestamps -# Initialize Sequel gem for database actions -DB = Sequel.connect(adapter: conf.get('database.adapter'), database: conf.get('database.database')) -# Load models -require_relative 'lib/models/item.rb' -require_relative 'lib/models/item_comment.rb' -require_relative 'lib/models/license.rb' -require_relative 'lib/models/license_comment.rb' -require_relative 'lib/models/ip_address.rb' - -# Load helper functions -require_relative 'lib/helpers.rb' - -# Register route handlers -require_relative 'lib/routes.rb' diff --git a/server.rb b/server.rb new file mode 100644 index 0000000..56ef90a --- /dev/null +++ b/server.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'logger' +require 'sequel' +require 'sqlite3' +require 'sinatra/base' +require 'rack/protection' + +# Load the Sequel timestamps plugin +Sequel::Model.plugin(:timestamps) +# Initialize Sequel gem for database actions +DB = Sequel.connect(adapter: $conf.get('database.adapter'), database: $conf.get('database.database')) +# Load models +Dir.glob('./app/models/*.rb').sort().each { |f| require f } + +# Base Sinatra app +class Raven < Sinatra::Base + @@my_app = {} + def self.new(*) self < Raven ? super : Rack::URLMap.new(@@my_app) end + def self.map(url) @@my_app[url] = self end + + # Enable and configure sessions + enable :sessions + + # Enable rack protection middleware + use Rack::Protection + + # Set up static file serving + enable :static + set :public_folder, File.join(__dir__, '/public') + + # Set up our view engine + set :views, File.join(settings.root, '/views') + + # Initialize logging + logger = Logger.new($stdout) + logger.level = Logger::INFO + + # Load helper functions + require_relative 'app/helpers' + helpers Helpers + + ## Map controllers + # Top-level routes controller + class IndexController < Raven + map '/' + end + + # Item routes controller + class ItemController < Raven + map '/item' + end + + # License routes controller + class LicenseController < Raven + map '/license' + end + + # Search routes controller + class SearchController < Raven + map '/search' + end + + # IP tracker routes controller + class IpTrackerController < Raven + map '/ip-tracker' + end +end + +# Load controllers +Dir.glob('./app/routes.rb').sort().each { |f| require f } \ No newline at end of file