Migrated app to modular Sinatra application; copied in a lot of improvements for a Sinatra app that I learned from Stage Manager

This commit is contained in:
Gregory Ballantine 2023-03-14 20:49:42 -04:00
parent 2340dddd06
commit 385dd98dbe
28 changed files with 384 additions and 275 deletions

3
.gitignore vendored
View File

@ -59,6 +59,9 @@ build-iPhoneSimulator/
# Local database storage # Local database storage
data/raven.db data/raven.db
# Local configuration
config/raven.yaml
# Node modules for Grunt.js # Node modules for Grunt.js
node_modules/ node_modules/

View File

@ -8,5 +8,6 @@ gem 'sequel', '~> 5.63'
gem 'sqlite3', '~> 1.5' gem 'sqlite3', '~> 1.5'
# Use rerun gem to auto-reload app # Use rerun gem to auto-reload app
gem 'rerun' gem 'guard-rack'
gem 'wdm', '>= 0.1.0' if Gem.win_platform?

View File

@ -1,15 +1,39 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
coderay (1.1.3)
ffi (1.15.5) ffi (1.15.5)
ffi (1.15.5-x64-mingw-ucrt) 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-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
lumberjack (1.2.8)
method_source (1.0.0)
multi_json (1.15.0) multi_json (1.15.0)
mustermann (3.0.0) mustermann (3.0.0)
ruby2_keywords (~> 0.0.1) ruby2_keywords (~> 0.0.1)
nenv (0.3.0)
nio4r (2.5.8) 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) puma (6.0.0)
nio4r (~> 2.0) nio4r (~> 2.0)
rack (2.2.4) rack (2.2.4)
@ -18,10 +42,9 @@ GEM
rb-fsevent (0.11.2) rb-fsevent (0.11.2)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
rerun (0.13.1)
listen (~> 3.0)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
sequel (5.63.0) sequel (5.63.0)
shellany (0.0.1)
sinatra (3.0.4) sinatra (3.0.4)
mustermann (~> 3.0) mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4) rack (~> 2.2, >= 2.2.4)
@ -33,8 +56,11 @@ GEM
rack-protection (= 3.0.4) rack-protection (= 3.0.4)
sinatra (= 3.0.4) sinatra (= 3.0.4)
tilt (~> 2.0) tilt (~> 2.0)
spoon (0.0.6)
ffi
sqlite3 (1.5.4-x64-mingw-ucrt) sqlite3 (1.5.4-x64-mingw-ucrt)
sqlite3 (1.5.4-x86_64-linux) sqlite3 (1.5.4-x86_64-linux)
thor (1.2.1)
tilt (2.0.11) tilt (2.0.11)
PLATFORMS PLATFORMS
@ -42,8 +68,8 @@ PLATFORMS
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES
guard-rack
puma (~> 6.0) puma (~> 6.0)
rerun
sequel (~> 5.63) sequel (~> 5.63)
sinatra (~> 3.0) sinatra (~> 3.0)
sinatra-contrib (~> 3.0) sinatra-contrib (~> 3.0)

6
Guardfile Normal file
View File

@ -0,0 +1,6 @@
guard 'rack' do
watch('Gemfile.lock')
watch('config.ru')
watch('server.rb')
watch(%r{^(lib)/.*})
end

View File

@ -10,11 +10,11 @@ namespace :db do
end end
namespace :server do namespace :server do
task :dev do task :start do
%x{ruby raven.rb} system("puma -C config/puma.rb")
end end
task :reload do task :dev do
%x{rerun --no-notify 'ruby raven.rb'} %x{guard}
end end
end end

View File

@ -2,8 +2,14 @@ require 'yaml'
class Config class Config
DEFAULT_CONFIG = 'config/defaults.yaml'
def initialize(config_path) 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 end
def get(key, depth = 0) def get(key, depth = 0)

View File

@ -1,4 +1,4 @@
helpers do module Helpers
def nullable(value) def nullable(value)
if (value) and (value != '') if (value) and (value != '')

16
app/routes/index.rb Normal file
View File

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

40
app/routes/ip_tracker.rb Normal file
View File

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

83
app/routes/item.rb Normal file
View File

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

83
app/routes/license.rb Normal file
View File

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

19
app/routes/search.rb Normal file
View File

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

9
config.ru Normal file
View File

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

View File

@ -1,3 +1,7 @@
server:
address: '127.0.0.1'
port: 6200
database: database:
adapter: 'sqlite' adapter: 'sqlite'
database: 'data/raven.db' database: 'data/raven.db'

6
config/puma.rb Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'

71
server.rb Normal file
View File

@ -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 }