Costruire un'esperienza di editing persistente in linea con Crystal, MongoDB (datanoise / sam0x17)
Poiché la documentazione è ancora scarsa, vorrei aggiungerne un po'.
Questo è quello che sto costruendo attualmente:
I singoli campi saranno modificabili con un editor in linea, che salverà automaticamente nel backend - senza bisogno di ricaricare l'intera pagina.
Il punto del tutto è un backend per la mia azienda, per essere in grado di automatizzare alcuni processi, calcolare i prezzi delle scorte, tenere le cose in magazzino, ecc. Un'applicazione di magazzino in pratica, su misura per me!
Tecnologie utilizzate
- Cristallo
- Kemal
- AdminLTE 2
- Bootstrap 3
- dataTables
- x-editable - una libreria davvero impressionante per l'editing in linea, purtroppo non mantenuta per qualche tempo
- vedi esempi per Bootstrap 3 qui
- MongoDB
- Mongo ORM di sam0x17
- che a sua volta si basa su driver Mongo di datanoise per Crystal (mongo.cr)
- Sublime Text per l'editing
- Robo 3T per una GUI per MongoDB
Bit importanti
MongoORM usa _id come indice principale. Si prega di notare il trattino basso!
L'_id NON è una stringa. È del tipo ObjectId
Pertanto dovrà essere costruito. Se volete costruirlo da un id precedente, usate questo codice:
mongo_id = BSON::ObjectId.new my_string_id
dove my_string_id è una stringa e mongo_id è un ObjectId.
Il codice
config/database.yml
è responsabile dell'impostazione della comunicazione con la tua istanza di Mongo DB. Si prega di fare riferimento a Documentazione di Sam.
stock.cr, responsabile della definizione di Stockitem, Subitem, e dei gestori Kemal appropriati:
richiedere "mongo_orm"
richiedere "json"
richiedere "./macros.cr".
richiedere "./weee.cr".modulo Stock
includere le macroclasse Itemgroup < Mongo::ORM::Document #used per il raggruppamento, ad esempio prodotti Pimoroni, Shipping, ecc.
campo gruppo : Stringa
has_many :stockitems 1TP3Nota che lo stockitem può appartenere solo ad un ItemGroup alla volta.
fine1TP3Questo include anche altri articoli (spedizione, download digitali, ecc.)
classe Stockitem < Mongo::ORM::Document
campo sku : Stringa
descrizione del campo : Stringa
campo is_alias : Bool
campo alias_of : String 1TP3Se questo campo è impostato, non utilizzare altre definizioni da questo elemento, ottenere l'elemento principale con alias
campo is_set : Bool # se questo elemento è un insieme di altri elementi
embeds_many :subitems # dovrebbe essere impostato solo se is_set = true
campo is_physical_item : Bool
campo weee_applicable : Bool #può essere sia: pagato da qualcun altro, o voce non è elettronica, ...
peso del campo : Int32 # in g
timestamps
fineclasse Subitem < Mongo::ORM::EmbeddedDocument
campo sku : Stringa #a riferimento all'articolo principale dello stock. NB: questo potrebbe probabilmente essere rielaborato in modo più carino con belongs_to ? ad esempio belongs_to :itemref, class_name: Stock::Stockitem
campo nota : Stringa
campo importo : Int32
fineottenere "/stock/show" do |env|
#https://github.com/datanoise/mongo.cr
sis = Stockitem.all({"is_alias" => {"$eq" => false}})
nome = "Stock :: Mostra e Modifica"
mrender "stock"
finepost "/stock/edit/:what" do |env|
pp "Entrare in modifica, con destinazione " + env.params.url["cosa"]
pp env.params.body
if env.params.body.has_key?("pk") && env.params.body.has_key?("value")
pp "Dentro pk & valore"
pk = env.params.body["pk"]?.as(String)
valore = env.params.body["value"]?.as(String)
se si = Stockitem.find(BSON::ObjectId.new pk)
caso env.params.url["cosa"]
quando "sku"
si.sku = valore
quando "descrizione"
si.description = valore
quando "is_alias"
si.is_alias = value == "true" ? true : false
quando "alias_di"
si.alias_of = valore
quando "è_set"
si.is_set = value == "true" ? true : false
quando "is_physical_item"
si.is_physical_item = value == "true" ? true : false
quando "weee_applicable"
pp "weee_applicable"
si.weee_applicable = value == "true" ? true : false
fine
se si.save
pp "dentro si.save"
env.response.status_code = 200
else
env.response.status_code = 400
fine
else
env.response.status_code = 400
fine
else
env.response.status_code = 400
fine
fine#generare da voci WEEE
get "/stock/generate/:year" do |env|
1TP3Passare attraverso l'intero database, raccogliere gli elementi e visualizzare un elenco
stock_collection = Hash(String,String).new
fatture = WEEE::Invoice.all({"anno"=>env.params.url["anno"].to_i32})
invoices.each fare |invoice|
#p "elaborazione della fattura #{numero.fattura}"
1TP3Nota che potremmo non ottenere assolutamente tutte le fatture qui, poiché easybill ha una visione diversa delle date (importazione)
invoice.items.each do |item|
if !item.description.nil? && !item.number.nil?
key = item.number == "" ? item.description : item.number
se !key.nil?
if !stock_collection.has_key?(key)
descrizione = ""
if !item.description.nil?
descrizione = item.description
fine
#stock_collection[key] = item.description
if !description.nil?
stock_collection[key] = descrizione
fine
end # end of !stock_collection.has_key?
fine # fine di if !key.nil?
fine
fine #end di invoice.items.each fare
fine #end di invoices.each
1TP3Controlla che questo non esista già nel nostro database, altrimenti crealo.
stock_collection.each do |sku,description|
if !Stockitem.find_by(:sku, sku)
si = Stockitem.new
si.sku = sku
si.is_alias = false
si.alias_of = ""
si.description = descrizione
si.is_set = false
si.is_physical_item = true
si.weee_applicable = true
si.weight = 100
si.save!
fine
fine
fine #end della macro getfine 1TP3Fine del modulo
Note:
- La documentazione di Sam spiega parti di questo codice, io affronterò solo gli ostacoli:
- sis = Stockitem.all({"is_alias" => {"$eq" => false}}) - questo codice cerca Stockitems (Mongo ORM si prenderà cura del nome del DB per te, ecc.)
- notare che non sono riuscito a impostare ({}) - dovrò indagare ulteriormente su questo
- questa sintassi è fondamentalmente ciò che MongoDB si aspetta. vedi https://docs.mongodb.com/manual/reference/operator/query/
- si = Stockitem.find(BSON::ObjectId.new pk)
- notare che qui BSON::ObjectId.new è usato per ricostruire l'id dalla chiave pk che è stata passata
- il codice di ritorno 200 in caso di successo e 400 in caso di fallimento serve a x-editable per sapere se la modifica è riuscita
Comportamento di si.save
- si.save! genera un errore se il salvataggio non è possibile
- si.save restituirà true se il salvataggio è stato possibile, e false se non è stato possibile
stock.ecr, il template HTML
<% content_for “name” do %>
.
.<% content_for “main” do%>
<div class="”col-md-12″">
<div class="”box" box-primary”>
<div class="box-header" with-border”>
<h3 class="”box-title”"><%= name %></h3>
</div>
<div class="”box-body”">
Nota: gli elementi alias non sono caricati dal database.
<thead>
<tr role="”row”">
<th class="”sorting_asc”" tabindex="”0″">sku</th>
<th>descrizione</th>
<th>è_alias?</th>
<th>alias_di</th>
<th>è_set?</th>
<th>sottopunti</th>
<th>is_physical_item?</th>
<th>weee_applicable?</th>
<th>peso (g)</th>
</tr>
</thead>
<% sis.each do |si| %>
tbd.
<A HREF="#"
id=".weee_applicable"
class="weee_applicable editable"
data-type="select"
data-pk=""
data-value="true"
data-source="[{valore: 'vero', testo: 'vero'}, {valore: 'falso', testo: 'falso'}]"
data-url="/stock/edit/weee_applicable"
data-title="WEEE: "
>
g
.</TABLE>
</div>
</div>
</div>
<script>
//https://datatables.net/examples/styling/bootstrap
//demo.js x-editable
window.onload = function() {
$(document).ready(function (){
$("#stock_items").DataTable();
$.fn.editable.defaults.mode = 'popup';
$(".editable").editable();
$(".editable").on('hidden', function(e, reason){
if(reason === 'save' || reason === 'nochange'){
var $next = $(this).closest('tr').next().find('.editable');
setTimeout(function(){
$next.editable('show');
}, 300);
}
});
});
}
</script>
.Note:
- poiché aggiungiamo i nostri script JS dopo questo (dovrei rielaborare questo per aggiungere una sezione di script personalizzati alla fine), dobbiamo avvolgerli in una funzione window.onload - altrimenti $ non è definito (scorciatoia jQuery)
- notate come usiamo una funzione qui per saltare al prossimo modificabile al successo del salvataggio
- si._id.to_s.rchop - l'id BSON viene tradotto in una stringa, e poi l'ultimo carattere strano (terminatore nullo?) viene tagliato via. Questo è uno scoglio importante!
layout.ecr (estratti)
<!DOCTYPE html>
<html>
<head>
.
.
TaxGod |
.
.
.
.
.
.
.
.
.
.
.
<!–[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js”>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js”>
<![endif]–><link rel="foglio di stile"
href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic”>
</head>
<body class="”hold-transition" skin-blue sidebar-mini”>
<div class="”wrapper”">(...)
<!– Content Wrapper. Contains page content –>
<div class="”content-wrapper”">
<!– Content Header (Page header) –>
<h1>
TaxGod ::
Essere Dio è divertente.
</h1>
<!– Main content –>
<%= yield_content “main” %>
<!– /.content –>
</div>
<!– /.content-wrapper –>(...)
</div>
<!– ./wrapper –><!– REQUIRED JS SCRIPTS –>
<!– jQuery 3 –>
<script src=”/bower_components/jquery/dist/jquery.min.js”></script>
<!– Bootstrap 3.3.7 –>
<script src=”/bower_components/bootstrap/dist/js/bootstrap.min.js”></script>
<!– AdminLTE App –>
<script src=”/danielm_uploader/js/jquery.dm-uploader.min.js”></script>
<script src=”/bower_components/datatables.net/js/jquery.dataTables.min.js”></script>
<script src=”/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js”></script>
<script src=”/dist/js/adminlte.min.js”></script>
<script src=”/tg_js/bootstrap-editable.min.js”></script>
<script src=”/tg_js/dropzone.min.js”></script>
</body>
</html>Ulteriori riferimenti
- https://github.com/sam0x17/mongo_orm/blob/893cd520cb90f7049d53b809ada30198deeed0f7/spec/fields_spec.cr
- https://github.com/datanoise/mongo.cr/commit/86c9e530c0ac980ed7b15e653746bc5b6f2527fc
- https://github.com/amberframework/granite/blob/master/docs/getting_started.md
- https://docs.mongodb.com/manual/reference/operator/query/
- https://github.com/datanoise/mongo.cr/issues/19
- https://github.com/datanoise/mongo.cr/blob/86c9e530c0ac980ed7b15e653746bc5b6f2527fc/src/mongo/collection.cr -> definizione di save in mongo.cr
- https://github.com/sam0x17/mongo_orm/blob/015ae29a29ca8b80a98a531de891095bbd42d028/src/mongo_orm/persistence.cr -> definizione di save in mongo_orm
Plugin WordPress Cookie di Real Cookie Banner