Infrangere le regole: utilizzare SQLite per provare le app Web

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Quindi, hai creato la prossima app Web killer, ma ora ti viene presentata la domanda che ogni nuovo prodotto deve considerare: "Come faccio a mostrare a tutti quanto è fantastica la mia app?"

La maggior parte dei potenziali utenti vorrà provare il software o il servizio prima di dedicare tempo e denaro. Alcuni prodotti funzionano alla grande semplicemente offrendo agli utenti una prova gratuita, mentre altre app funzionano meglio con dati di esempio già in atto. Spesso è qui che entra in gioco il vecchio conto demo.

Tuttavia, chiunque abbia mai implementato un account demo può attestare i problemi associati. Sai come funzionano le cose su Internet: chiunque può inserire dati (che abbiano senso o meno per il prodotto) e ci sono buone probabilità che il contenuto aggiunto da utenti anonimi o bot possa essere offensivo per gli altri. Certo, puoi sempre resettare il database, ma con quale frequenza e quando? E alla fine, questo risolve davvero il problema? La mia soluzione per usare SQLite .

Perché non utilizzare SQLite per la versione di produzione?

È noto che SQLite non gestisce più thread poiché l'intero database è bloccato durante un comando di scrittura, motivo per cui non dovresti usarlo in un normale ambiente di produzione. Tuttavia, nella mia soluzione, viene utilizzato un file SQLite separato per ogni utente che esegue la demo del software. Ciò significa che la limitazione di scrittura è limitata solo a quell'utente, ma più utenti simultanei (ciascuno con il proprio file di database) non sperimenteranno questa limitazione. Ciò consente un'esperienza controllata per l'utente che prova alla guida del software e consente loro di visualizzare esattamente ciò che si desidera che vedano.

Altro dopo il salto! Continua a leggere sotto ↓

Questo tutorial si basa su una soluzione del mondo reale che ho eseguito con successo per un'app Web dimostrativa SaaS dal 2015. Il tutorial è scritto per Ruby on Rails (il mio framework preferito) versione 3 e successive, ma i concetti di base dovrebbero essere in grado di essere adattato a qualsiasi altro linguaggio o struttura. Infatti, poiché Ruby on Rails segue il paradigma del software "convention over configuration" potrebbe anche essere più facile da implementare in altri framework, specialmente in linguaggi nudi (come PHP diretto) o framework che non fanno molto in termini di gestione delle connessioni al database .

Detto questo, questa tecnica è particolarmente adatta per Ruby on Rails. Come mai? Perché, per la maggior parte, è "agnostico del database". Ciò significa che dovresti essere in grado di scrivere il tuo codice Ruby e passare da un database all'altro senza problemi.

Un esempio di una versione finita di questo processo può essere scaricato da GitHub.

Il primo passo: ambiente di distribuzione

Arriveremo all'implementazione in seguito, ma Ruby on Rails è suddiviso per impostazione predefinita in ambienti di sviluppo, test e produzione. Aggiungeremo a questo elenco un nuovo ambiente demo per la nostra app che sarà quasi identico all'ambiente di produzione ma ci consentirà di utilizzare diverse impostazioni del database.

In Rails, crea un nuovo ambiente duplicando il file config/environments/production.rb e rinominalo demo.rb . Poiché l'ambiente demo verrà utilizzato in un'impostazione simile alla produzione, potrebbe non essere necessario modificare molte opzioni di configurazione per questo nuovo ambiente, anche se suggerirei di modificare config.assets.compile da false a true , il che renderà più semplice il test locale senza dover precompilare.

Se stai utilizzando Rails 4 o versioni successive, dovrai anche aggiornare config/secrets.yml per aggiungere una secret_key_base per l'ambiente demo. Assicurati di rendere questa chiave segreta diversa dalla produzione per garantire che le sessioni siano uniche tra ogni ambiente, proteggendo ulteriormente la tua app.

Successivamente è necessario definire la configurazione del database in config/database.yml . Sebbene l'ambiente demo utilizzerà principalmente il database duplicato di cui parleremo nella prossima sezione, dobbiamo definire il file di database predefinito e le impostazioni da utilizzare per la nostra demo. Aggiungi quanto segue a config/database.yml :

 demo: adapter: sqlite3 pool: 5 timeout: 5000 database: db/demo.sqlite3

In Rails, potresti anche voler controllare il tuo Gemfile per assicurarti che SQLite3 sia disponibile nel nuovo ambiente demo. Puoi impostarlo in diversi modi, ma potrebbe assomigliare a questo:

 group :development, :test, :demo do gem 'sqlite3' end

Una volta configurato il database, è necessario eseguire il rake db:migrate RAILS_ENV=demo e quindi inserire i dati nel database come si desidera (che provenga da un file seed, inserendo manualmente nuovi dati o anche duplicando il file development.sqlite3 ). A questo punto, dovresti verificare che tutto funzioni eseguendo rails server -e demo dalla riga di comando. Mentre esegui il server nel nuovo ambiente demo, puoi assicurarti che i tuoi dati di test siano come desideri, ma puoi sempre tornare indietro e modificare quel contenuto in un secondo momento. Quando aggiungi i tuoi contenuti al database demo, ti consiglio di creare un set di dati pulito in modo che il file sia il più piccolo possibile. Tuttavia, se è necessario migrare i dati da un altro database, consiglio YamlDb, che crea un formato indipendente dal database per il dump e il ripristino dei dati.

Se la tua applicazione Rails funziona come previsto, puoi passare al passaggio successivo.

Il secondo passaggio: utilizzo del database demo

La parte essenziale di questo tutorial è poter consentire a ciascuna sessione di utilizzare un file di database SQLite diverso. Normalmente la tua applicazione si connetterà allo stesso database per ogni utente in modo che sia necessario codice aggiuntivo per questa attività.

Per iniziare a consentire a Ruby on Rails di cambiare database, dobbiamo prima aggiungere i seguenti quattro metodi privati ​​in application_controller.rb . Sarà inoltre necessario definire un filtro prima per il metodo set_demo_database in modo che la logica che fa riferimento al database demo corretto venga chiamata ad ogni caricamento della pagina.

 # app/controllers/application_controller.rb # use `before_filter` for Rails 3 before_action :set_demo_database, if: -> { Rails.env == 'demo' } private # sets the database for the demo environment def set_demo_database if session[:demo_db] # Use database set by demos_controller db_name = session[:demo_db] else # Use default 'demo' database db_name = default_demo_database end ActiveRecord::Base.establish_connection(demo_connection(db_name)) end # Returns the current database configuration hash def default_connection_config @default_config ||= ActiveRecord::Base.connection.instance_variable_get("@config").dup end # Returns the connection hash but with database name changed # The argument should be a path def demo_connection(db_path) default_connection_config.dup.update(database: db_path) end # Returns the default demo database path defined in config/database.yml def default_demo_database return YAML.load_file("#{Rails.root.to_s}/config/database.yml")['demo']['database'] end

Poiché ogni sessione del server avrà un database diverso, memorizzerai il nome del file del database in una variabile di sessione. Come puoi vedere, stiamo usando session[:demo_db] per tracciare il database specifico per l'utente. Il metodo set_demo_database controlla quale database utilizzare stabilendo la connessione al database impostato nella variabile di sessione. Il metodo default_demo_database carica semplicemente il percorso del database come definito nel file di configurazione database.yml .

Se stai utilizzando un linguaggio semplice, a questo punto probabilmente puoi semplicemente aggiornare lo script di connessione al database in modo che punti al nuovo database e quindi passare alla sezione successiva. In Rails, le cose richiedono qualche passaggio in più perché segue il paradigma del software "convention over configuration".

Il terzo passaggio: duplicare il file SQLite

Ora che l'app è configurata per utilizzare il nuovo database, è necessario un trigger per la nuova sessione demo. Per semplicità, inizia semplicemente utilizzando un pulsante di base "Avvia demo". Potresti anche trasformarlo in un modulo in cui raccogli un nome e un indirizzo e-mail (per un follow-up dal team di vendita, ecc.) O qualsiasi numero di cose.

Rispettando le convenzioni di Rails, crea un nuovo controller "Demo":

 rails generate controller demos new

Successivamente, dovresti aggiornare le route in modo che puntino alle tue nuove azioni del controller, avvolgendole in un condizionale per impedirne la chiamata nell'ambiente di produzione. Puoi nominare i percorsi come preferisci o nominarli utilizzando le convenzioni Rails standard:

 if Rails.env == 'demo' get 'demos/new', as: 'new_demo' post 'demos' => 'demos#create', as: 'demos' end

Successivamente, aggiungiamo un modulo molto semplice a views/demos/new.html.erb . Potresti voler aggiungere ulteriori campi modulo per acquisire:

 <h1>Start a Demo</h1> <%= form_tag demos_path, method: :post do %> <%= submit_tag 'Start Demo' %> <% end %>

La magia avviene nell'azione di create . Quando l'utente si sottopone a questa route, l'azione copierà il file demo.sqlite3 con un nuovo nome file univoco, imposterà le variabili di sessione, farà il login dell'utente (se applicabile), quindi reindirizzerà l'utente alla pagina appropriata (la chiameremo 'Pannello').

 class DemosController < ApplicationController def new # Optional: setting session[:demo_db] to nil will reset the demo session[:demo_db] = nil end def create # make db/demos dir if doesn't exist unless File.directory?('db/demos/') FileUtils.mkdir('db/demos/') end # copy master 'demo' database master_db = default_demo_database demo_db = "db/demos/demo-#{Time.now.to_i}.sqlite3" FileUtils::cp master_db, demo_db # set session for new db session[:demo_db] = demo_db # Optional: login code (if applicable) # add your own login code or method here login(User.first) # Redirect to wherever you want to send the user next redirect_to dashboard_path end end

Ora dovresti essere in grado di provare il codice demo in locale avviando ancora una volta il server utilizzando l'esecuzione di rails server -e demo .

Se il server è già in esecuzione, sarà necessario riavviarlo per eventuali modifiche apportate poiché è configurato per memorizzare nella cache il codice come il server di produzione.

Una volta che tutto il codice funziona come previsto, salva le modifiche nel controllo della versione e assicurati di eseguire il commit del file demo.sqlite3 , ma non dei file nella directory db/demos . Se stai usando git, puoi semplicemente aggiungere quanto segue al tuo file .gitignore :

Se desideri raccogliere informazioni aggiuntive dall'utente demo (come nome e/o e-mail), probabilmente vorrai inviare tali informazioni tramite un'API alla tua applicazione principale o ad un'altra pipeline di vendita poiché il tuo database demo non sarà affidabile (si ripristina ogni volta che ridistribuisci).

 !/db/demo.sqlite3 db/demos/*

Passaggio finale: distribuire il server demo

Ora che la tua configurazione demo funziona localmente, vorrai ovviamente distribuirla in modo che tutti possano usarla. Sebbene ogni app sia diversa, consiglierei che l'app demo risieda su un server separato e quindi un dominio come app di produzione (come demo.myapp.com). Ciò garantirà che i due ambienti siano isolati. Inoltre, poiché il file SQLite è archiviato sul server, servizi come Heroku non funzioneranno in quanto non forniscono l'accesso al filesystem. Tuttavia, puoi comunque utilizzare praticamente qualsiasi provider VPS (come AWS EC2, Microsoft Azure, ecc.). Se ti piace la comodità automatizzata, ci sono altre piattaforme come opzioni di servizio che ti consentono di lavorare con VPS.

Indipendentemente dal processo di distribuzione, potresti anche dover verificare che l'app disponga delle autorizzazioni di lettura/scrittura appropriate per la directory in cui archivi i file SQLite demo. Questo potrebbe essere gestito manualmente o con un hook di distribuzione.

SQLite non funzionerà per me. Che dire di altri sistemi di database?

Non vengono create due app uguali e nemmeno i requisiti del database. Utilizzando SQLite, hai il vantaggio di poter duplicare rapidamente il database, oltre a poter archiviare il file nel controllo della versione. Anche se credo che SQLite funzionerà per la maggior parte delle situazioni (specialmente con Rails), ci sono situazioni in cui SQLite potrebbe non essere adatto alle esigenze della tua applicazione. Fortunatamente, è ancora possibile utilizzare gli stessi concetti di cui sopra con altri sistemi di database. Il processo di duplicazione di un database sarà leggermente diverso per ciascun sistema, ma delineerò una soluzione per MySQL e un processo simile esiste con PostgreSQL e altri.

La maggior parte dei metodi descritti sopra funzionano senza ulteriori modifiche. Tuttavia, invece di archiviare un file SQLite nel controllo della versione, dovresti usare mysqldump (o pg_dump per PostgreSQL) per esportare un file SQL di qualsiasi database abbia il contenuto che desideri utilizzare per la tua esperienza demo. Questo file dovrebbe anche essere archiviato nel controllo della versione.

Le uniche modifiche al codice precedente si troveranno nell'azione demos#create . Invece di copiare il file SQLite3, l'azione del controller creerà un nuovo database, caricherà il file sql in quel database e concederà le autorizzazioni per l'utente del database, se necessario. Il terzo passaggio per la concessione dell'accesso è necessario solo se l'utente amministratore del database è diverso dall'utente utilizzato dall'app per connettersi. Il codice seguente utilizza i comandi MySQL standard per gestire questi passaggi:

 def create # database names template_demo_db = default_demo_database new_demo_db = "demo_database_#{Time.now.to_i}" # Create database using admin credentials # In this example the database is on the same server so passing a host argument is not require `mysqladmin -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } create #{new_demo_db}` # Load template sql into new database # Update the path if it differs from where you saved the demo_template.sql file `mysql -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } #{new_demo_db} < db/demo_template.sql` # Grant access to App user (if applicable) `mysql -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } -e "GRANT ALL on #{new_demo_db}.* TO '#{ ENV['DB_USERNAME'] }'@'%';"` # set session for new db session[:demo_db] = new_demo_db # Optional: login code (if applicable) # add your own login code or method here login(User.first) redirect_to dashboard_path end

Ruby, come molti altri linguaggi incluso PHP, ti permette di usare i backtick per eseguire un comando di shell (cioè, `ls -a` ) dall'interno del tuo codice. Tuttavia, è necessario utilizzarlo con cautela e assicurarsi che nel comando non possano essere inseriti parametri o variabili rivolti all'utente per proteggere il server da codice dannoso. In questo esempio, stiamo interagendo esplicitamente con gli strumenti della riga di comando di MySQL, che è l'unico modo per creare un nuovo database. Questo è lo stesso modo in cui il framework Ruby on Rails crea un nuovo database. Assicurati di sostituire ENV['DB_ADMIN'] e ENV['DB_ADMIN_PASSWORD'] con la tua variabile di ambiente o qualsiasi altro modo per impostare il nome utente del database. Dovrai fare lo stesso per ENV['DB_USERNAME'] se il tuo utente amministratore è diverso dall'utente per la tua app.

Questo è tutto ciò che serve per passare a MySQL! Il vantaggio più ovvio di questa soluzione è che non devi preoccuparti di potenziali problemi che potrebbero apparire dalla diversa sintassi tra i sistemi di database.

Alla fine, una decisione finale viene presa in base alla qualità e al servizio previsti, piuttosto che alla comodità e alla velocità, e non è necessariamente influenzata dal solo prezzo.

Pensieri finali

Questo è solo un punto di partenza per ciò che puoi fare con il tuo nuovo server demo. Ad esempio, il tuo sito web di marketing potrebbe avere un link a "Prova la funzione XYZ". Se non hai bisogno di un nome o di un'e-mail, puoi collegare il metodo demos#create con un collegamento come /demos/?feature=xyz e l'azione reindirizzerà semplicemente alla funzione e/o alla pagina desiderata, piuttosto che alla dashboard in l'esempio sopra.

Inoltre, se utilizzi SQLite per gli ambienti di sviluppo e demo, avere sempre questo database di esempio nel controllo della versione darebbe a tutti i tuoi sviluppatori l'accesso a un database pulito da utilizzare nello sviluppo locale, negli ambienti di test o nei test di garanzia della qualità. Le possibilità sono infinite.

Puoi scaricare una demo completata da GitHub.