{"id":7584,"date":"2019-02-05T19:20:29","date_gmt":"2019-02-05T18:20:29","guid":{"rendered":"https:\/\/pi3g.com\/?p=7584"},"modified":"2019-02-05T19:20:29","modified_gmt":"2019-02-05T18:20:29","slug":"building-a-persistent-in-line-editing-experience-with-crystal-mongodb-datanoise-sam0x17","status":"publish","type":"post","link":"https:\/\/pi3g.com\/de\/building-a-persistent-in-line-editing-experience-with-crystal-mongodb-datanoise-sam0x17\/","title":{"rendered":"Aufbau eines persistenten Inline-Editing-Erlebnisses mit Crystal, MongoDB (datanoise \/ sam0x17)"},"content":{"rendered":"<p>As documentation is still sparse, I would like to add some.<\/p>\n<p>This is what I am building currently:<\/p>\n<p><a href=\"https:\/\/pi3g.com\/wp-content\/uploads\/2019\/02\/image-3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1066\" height=\"389\" title=\"image\" style=\"display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2019\/02\/image_thumb-3.png\" border=\"0\"><\/a><\/p>\n<p>The individual fields are going to be editable with an inline editor, which will automatically save to the backend \u2013 no need for reloading the whole page.<\/p>\n<p><a href=\"https:\/\/pi3g.com\/wp-content\/uploads\/2019\/02\/image-4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"244\" height=\"153\" title=\"image\" style=\"margin: 0px; display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2019\/02\/image_thumb-4.png\" border=\"0\"><\/a><\/p>\n<p>The point of the whole is a backend for my company, to be able to automate some processes, calculate stock prices, keep things in stock, etc. A warehouse application basically, tailored to me!<\/p>\n<h1>Technologies used<\/h1>\n<ul>\n<li>Crystal<\/li>\n<li>Kemal<\/li>\n<li>AdminLTE 2<\/li>\n<ul>\n<li>Bootstrap 3<\/li>\n<li>dataTables<\/li>\n<\/ul>\n<li><a href=\"http:\/\/vitalets.github.io\/x-editable\/\" target=\"_blank\">x-editable<\/a> \u2013 a really awesome library for inline editing, sadly not mantained for some time<\/li>\n<ul>\n<li>see examples for Bootstrap 3 <a href=\"https:\/\/vitalets.github.io\/x-editable\/demo-bs3.html\" target=\"_blank\">here<\/a><\/li>\n<\/ul>\n<li>MongoDB<\/li>\n<ul>\n<li><a href=\"https:\/\/github.com\/sam0x17\/mongo_orm\" target=\"_blank\">Mongo ORM by sam0x17<\/a><\/li>\n<li>which in turn is based off <a href=\"https:\/\/github.com\/datanoise\/mongo.cr\" target=\"_blank\">datanoise\u2019s Mongo Driver for Crystal<\/a> (mongo.cr)<\/li>\n<\/ul>\n<li>Sublime Text for editing <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" style=\"\" alt=\"Smile\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2019\/02\/wlEmoticon-smile.png\"><\/li>\n<li><strong>Robo 3T<\/strong> for a GUI for MongoDB<\/li>\n<\/ul>\n<p><\/p>\n<h1>Important bits<\/h1>\n<p><a href=\"https:\/\/pi3g.com\/wp-content\/uploads\/2019\/02\/image-5.png\"><img loading=\"lazy\" decoding=\"async\" width=\"930\" height=\"54\" title=\"image\" style=\"display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2019\/02\/image_thumb-5.png\" border=\"0\"><\/a><\/p>\n<p>MongoORM uses _id as the main index. <strong>Please note the underscore<\/strong>!<\/p>\n<p>The _id is NOT a string. It is of the type <strong>ObjectId<\/strong><\/p>\n<p>Therefore it will need to be constructed. If you want to construct it from a previous id, use this code:<\/p>\n<blockquote>\n<p>mongo_id = BSON::ObjectId.new my_string_id<\/p>\n<\/blockquote>\n<p>where my_string_id is a String, and mongo_id is an ObjectId.<\/p>\n<p><\/p>\n<h1>The code<\/h1>\n<h2>config\/database.yml<\/h2>\n<p>is responsible for setting up the communication to your Mongo DB instance. Please refer to <a href=\"https:\/\/github.com\/sam0x17\/mongo_orm\" target=\"_blank\">Sam\u2019s documentation<\/a>.<\/p>\n<h2>stock.cr, responsible for defining Stockitem, Subitem, and the apropriate Kemal handlers:<\/h2>\n<blockquote>\n<p>require &#8220;mongo_orm&#8221;<br \/>\nrequire &#8220;json&#8221;<br \/>\nrequire &#8220;.\/macros.cr&#8221;<br \/>\nrequire &#8220;.\/weee.cr&#8221;<\/p>\n<p>module Stock<br \/>&nbsp;&nbsp;&nbsp;&nbsp; include Macros<\/p>\n<p>&nbsp;&nbsp;&nbsp; class Itemgroup &lt; Mongo::ORM::Document #used for grouping, e.g. Pimoroni products, Shipping, etc.<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field group : String<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; has_many :stockitems #note that the stockitem can only belong to one ItemGroup at a time.<br \/>&nbsp;&nbsp;&nbsp;&nbsp; end<\/p>\n<p>&nbsp;&nbsp;&nbsp; #this includes other items as well (shipping, digital downloads, etc)<br \/>&nbsp;&nbsp;&nbsp;&nbsp; class Stockitem &lt; Mongo::ORM::Document<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field sku : String<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field description : String<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field is_alias : Bool<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field alias_of : String #if this field is set, do not use any other definitions from this item, get the aliased master item<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field is_set : Bool # if this item is a set of other items<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; embeds_many :subitems # should only be set if is_set = true <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field is_physical_item : Bool<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field weee_applicable : Bool #can be either: paid for by someone else, or item is not electronics, &#8230;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field weight : Int32 # in g<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timestamps<br \/>&nbsp;&nbsp;&nbsp;&nbsp; end<\/p>\n<p>&nbsp;&nbsp;&nbsp; class Subitem &lt; Mongo::ORM::EmbeddedDocument<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>field sku : String #a reference to the main stock item. NB: this could probably be reworked in a nicer fashion with belongs_to ? e.g. belongs_to :itemref, class_name: Stock::Stockitem<\/strong><br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field note : String<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field amount : Int32<br \/>&nbsp;&nbsp;&nbsp;&nbsp; end<\/p>\n<p>&nbsp;&nbsp;&nbsp; get &#8220;\/stock\/show&#8221; do |env|<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #<a href=\"https:\/\/github.com\/datanoise\/mongo.cr\">https:\/\/github.com\/datanoise\/mongo.cr<\/a><br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sis = Stockitem.all({&#8220;is_alias&#8221; =&gt; {&#8220;$eq&#8221; =&gt; false}})<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name = &#8220;Stock :: Show &amp;amp; Edit&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mrender &#8220;stock&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; end<\/p>\n<p>&nbsp;&nbsp;&nbsp; post &#8220;\/stock\/edit\/:what&#8221; do |env|<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pp &#8220;Entering edit, with target &#8221; + env.params.url[&#8220;what&#8221;]<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pp env.params.body<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if env.params.body.has_key?(&#8220;pk&#8221;) &amp;&amp; env.params.body.has_key?(&#8220;value&#8221;)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pp &#8220;Inside pk &amp; value&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pk = env.params.body[&#8220;pk&#8221;]?.as(String)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value = env.params.body[&#8220;value&#8221;]?.as(String)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if si = Stockitem.find(BSON::ObjectId.new pk)&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case env.params.url[&#8220;what&#8221;]<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; when &#8220;sku&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.sku = value<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; when &#8220;description&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.description = value<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; when &#8220;is_alias&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.is_alias = value == &#8220;true&#8221; ? true : false<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; when &#8220;alias_of&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.alias_of = value<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; when &#8220;is_set&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.is_set = value == &#8220;true&#8221; ? true : false<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; when &#8220;is_physical_item&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.is_physical_item = value == &#8220;true&#8221; ? true : false<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; when &#8220;weee_applicable&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pp &#8220;weee_applicable&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.weee_applicable = value == &#8220;true&#8221; ? true : false<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if si.save<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pp &#8220;inside si.save&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; env.response.status_code = 200<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; env.response.status_code = 400<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; env.response.status_code = 400&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; env.response.status_code = 400<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br \/>&nbsp;&nbsp;&nbsp;&nbsp; end<\/p>\n<p>&nbsp;&nbsp;&nbsp; #generate from WEEE entries<br \/>&nbsp;&nbsp;&nbsp;&nbsp; get &#8220;\/stock\/generate\/:year&#8221; do |env|<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #walk through the entire database, collect the items, and display a list<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stock_collection = Hash(String,String).new <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoices = WEEE::Invoice.all({&#8220;year&#8221;=&gt;env.params.url[&#8220;year&#8221;].to_i32})&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoices.each do |invoice|<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #p &#8220;processing invoice #{invoice.number}&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #note that we might not get absolutely all invoices here, as easybill has a different view of the dates (import)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoice.items.each do |item|<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if !item.description.nil? &amp;&amp; !item.number.nil?<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; key = item.number == &#8220;&#8221; ? item.description : item.number<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if !key.nil?<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if !stock_collection.has_key?(key)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; description = &#8220;&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if !item.description.nil?<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; description = item.description<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #stock_collection[key] = item.description<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if !description.nil?<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stock_collection[key] = description<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end # end of !stock_collection.has_key?<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end # end of if !key.nil?<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end #end of invoice.items.each do <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end #end of invoices.each<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #check that this does not exist already in our database, else create it.<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stock_collection.each do |sku,description|<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if !Stockitem.find_by(:sku, sku)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si = Stockitem.new<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.sku = sku<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.is_alias = false<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.alias_of = &#8220;&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.description = description<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.is_set = false<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.is_physical_item = true<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.weee_applicable = true<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.weight = 100<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; si.save!<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br \/>&nbsp;&nbsp;&nbsp;&nbsp; end #end of get macro<\/p>\n<p>end #end of module<\/p>\n<\/blockquote>\n<p><strong>Notes:<\/strong><\/p>\n<ul>\n<li>Sam\u2019s documentation explains parts of this code, I will only address the stumbling blocks:<\/li>\n<li>sis = Stockitem.all({&#8220;is_alias&#8221; =&gt; {&#8220;$eq&#8221; =&gt; false}}) \u2013 this code searches for Stockitems (Mongo ORM will take care of the DB name for you, etc.)<\/li>\n<ul>\n<li>note that I did not manage to set ({}) \u2013 will have to investigate this further<\/li>\n<li>this syntax is basically what MongoDB expects. see <a href=\"https:\/\/docs.mongodb.com\/manual\/reference\/operator\/query\/\" target=\"_blank\">https:\/\/docs.mongodb.com\/manual\/reference\/operator\/query\/<\/a><\/li>\n<\/ul>\n<li>si = Stockitem.find(BSON::ObjectId.new pk)<\/li>\n<ul>\n<li>note that here BSON::ObjectId.new is used to reconstruct the id from the key pk which was passed<\/li>\n<\/ul>\n<li>return code 200 on success and 400 on failure is for x-editable to know whether the edit succeeded<\/li>\n<\/ul>\n<p><strong>Behaviour of si.save<\/strong><\/p>\n<ul>\n<li>si.save! will raise an error if saving is not possible<\/li>\n<li>si.save will return true if saving was possible, and false if it was not possible<\/li>\n<\/ul>\n<h2>stock.ecr, the HTML template<\/h2>\n<blockquote>\n<p>&lt;% content_for &#8220;name&#8221; do %&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;%= name %&gt;<br \/>\n&lt;% end %&gt;<\/p>\n<p>&lt;% content_for &#8220;main&#8221; do%&gt;<br \/>\n&lt;div class=&#8221;col-md-12&#8243;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;div class=&#8221;box box-primary&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div class=box-header with-border&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;h3 class=&#8221;box-title&#8221;&gt;&lt;%= name %&gt;&lt;\/h3&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/div&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div class=&#8221;box-body&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Please note: alias&#8217;ed items are not loaded from the database.<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TABLE class=&#8221;table table-bordered table-striped dataTable&#8221; role=&#8221;grid&#8221; aria-describedby=&#8221;Table with stock items&#8221; id=&#8221;stock_items&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;thead&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr role=&#8221;row&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th class=&#8221;sorting_asc&#8221; tabindex=&#8221;0&#8243;&gt;sku&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;description&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;is_alias?&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;alias_of&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;is_set?&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;subitems&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;is_physical_item?&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;weee_applicable?&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;weight (g)&lt;\/th&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/tr&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/thead&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;% sis.each do |si| %&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TR role=&#8221;row&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;&lt;%= si.sku.to_s %&gt;&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;&lt;%= si.description.to_s %&gt;&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;&lt;%= si.is_alias ? &#8220;true&#8221; : &#8220;false&#8221; %&gt;&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;&lt;%= si.alias_of.to_s %&gt;&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;&lt;%= si.is_set ? &#8220;true&#8221; : &#8220;false&#8221; %&gt;&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;tbd.&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;&lt;%= si.is_physical_item ? &#8220;true&#8221; : &#8220;false&#8221; %&gt;&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;&lt;A HREF=&#8221;#&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id=&#8221;&lt;%= si._id.to_s.rchop %&gt;.weee_applicable&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=&#8221;weee_applicable editable&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data-type=&#8221;select&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data-pk=&#8221;&lt;%= si._id.to_s.rchop %&gt;&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data-value=&#8221;true&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data-source=&#8221;[{value: &#8216;true&#8217;, text: &#8216;true&#8217;}, {value: &#8216;false&#8217;, text: &#8216;false&#8217;}]&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data-url=&#8221;\/stock\/edit\/weee_applicable&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data-title=&#8221;WEEE: &lt;%= si.sku.to_s %&gt;&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;&lt;%= si.weee_applicable ? &#8220;true&#8221; : &#8220;false&#8221; %&gt;&lt;\/A&gt;&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;TD&gt;&lt;%= si.weight.to_s %&gt; g&lt;\/TD&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/TR&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;% end %&gt;<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/TABLE&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/div&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/div&gt;<br \/>\n&lt;\/div&gt;<br \/>\n&lt;script&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; \/\/<a href=\"https:\/\/datatables.net\/examples\/styling\/bootstrap\">https:\/\/datatables.net\/examples\/styling\/bootstrap<\/a><br \/>&nbsp;&nbsp;&nbsp;&nbsp; \/\/demo.js x-editable<br \/>&nbsp;&nbsp;&nbsp;&nbsp; window.onload = function() {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(document).ready(function (){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&#8220;#stock_items&#8221;).DataTable();<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $.fn.editable.defaults.mode = &#8216;popup&#8217;;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&#8220;.editable&#8221;).editable();<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&#8220;.editable&#8221;).on(&#8216;hidden&#8217;, function(e, reason){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(reason === &#8216;save&#8217; || reason === &#8216;nochange&#8217;){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var $next = $(this).closest(&#8216;tr&#8217;).next().find(&#8216;.editable&#8217;);<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setTimeout(function(){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $next.editable(&#8216;show&#8217;);<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }, 300);<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br \/>&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>\n&lt;\/script&gt;<br \/>\n&lt;% end %&gt;<\/p>\n<\/blockquote>\n<p><strong>Notes:<\/strong><\/p>\n<ul>\n<li>since we add our JS scripts after this (I should rework this to add a custom script section at the end), we have to wrap them in a window.onload function \u2013 otherwise $ is not defined (jQuery shortcut)<\/li>\n<li>note how we use a function here to jump to the next editable on save success<\/li>\n<li><strong>si._id.to_s.rchop \u2013 the BSON id is translated to a string, and then the last weird character (null terminator?) is chopped off. This is an important stumbling block!<\/strong><\/li>\n<\/ul>\n<h2>layout.ecr (excerpts)<\/h2>\n<blockquote>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html&gt;<br \/>\n&lt;head&gt;<br \/>&nbsp;&nbsp; &lt;meta charset=&#8221;utf-8&#8243;&gt;<br \/>&nbsp;&nbsp; &lt;meta http-equiv=&#8221;X-UA-Compatible&#8221; content=&#8221;IE=edge&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;title&gt;TaxGod | &lt;%= yield_content &#8220;name&#8221; %&gt;&lt;\/title&gt;<br \/>&nbsp;&nbsp; &lt;meta content=&#8221;width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no&#8221; name=&#8221;viewport&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/bower_components\/bootstrap\/dist\/css\/bootstrap.min.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/bower_components\/font-awesome\/css\/font-awesome.min.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/bower_components\/Ionicons\/css\/ionicons.min.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/dist\/css\/AdminLTE.min.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/dist\/css\/skins\/skin-blue.min.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/danielm_uploader\/css\/jquery.dm-uploader.min.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/bower_components\/datatables.net-bs\/css\/dataTables.bootstrap.min.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/tg_css\/bootstrap-editable.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/tg_css\/picockpit.css&#8221;&gt;<br \/>&nbsp;&nbsp; &lt;link rel=&#8221;stylesheet&#8221; href=&#8221;\/tg_css\/dropzone.css&#8221;&gt;&nbsp; <br \/>&nbsp;&nbsp; &lt;!&#8211;[if lt IE 9]&gt;<br \/>&nbsp;&nbsp; &lt;script src=&#8221;<a href=\"https:\/\/oss.maxcdn.com\/html5shiv\/3.7.3\/html5shiv.min.js&quot;\">https:\/\/oss.maxcdn.com\/html5shiv\/3.7.3\/html5shiv.min.js&#8221;<\/a>&gt;&lt;\/script&gt;<br \/>&nbsp;&nbsp; &lt;script src=&#8221;<a href=\"https:\/\/oss.maxcdn.com\/respond\/1.4.2\/respond.min.js&quot;\">https:\/\/oss.maxcdn.com\/respond\/1.4.2\/respond.min.js&#8221;<\/a>&gt;&lt;\/script&gt;<br \/>&nbsp;&nbsp; &lt;![endif]&#8211;&gt;<\/p>\n<p>&nbsp; &lt;link rel=&#8221;stylesheet&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; href=&#8221;<a href=\"https:\/\/fonts.googleapis.com\/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic&quot;\">https:\/\/fonts.googleapis.com\/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic&#8221;<\/a>&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body class=&#8221;hold-transition skin-blue sidebar-mini&#8221;&gt;<br \/>\n&lt;div class=&#8221;wrapper&#8221;&gt;<\/p>\n<p>(\u2026)<\/p>\n<p>&nbsp; &lt;!&#8211; Content Wrapper. Contains page content &#8211;&gt;<br \/>&nbsp;&nbsp; &lt;div class=&#8221;content-wrapper&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;!&#8211; Content Header (Page header) &#8211;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;section class=&#8221;content-header&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;h1&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TaxGod :: &lt;%= yield_content &#8220;name&#8221; %&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;small&gt;Being God is fun.&lt;\/small&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/h1&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/section&gt;<\/p>\n<p>&nbsp;&nbsp;&nbsp; &lt;!&#8211; Main content &#8211;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;section class=&#8221;content container-fluid&#8221;&gt;<\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp; &lt;%= yield_content &#8220;main&#8221; %&gt;<\/p>\n<p>&nbsp;&nbsp;&nbsp; &lt;\/section&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;!&#8211; \/.content &#8211;&gt;<br \/>&nbsp;&nbsp; &lt;\/div&gt;<br \/>&nbsp;&nbsp; &lt;!&#8211; \/.content-wrapper &#8211;&gt;<\/p>\n<p>(\u2026)<br \/>\n&lt;\/div&gt;<br \/>\n&lt;!&#8211; .\/wrapper &#8211;&gt;<\/p>\n<p>&lt;!&#8211; REQUIRED JS SCRIPTS &#8211;&gt;<\/p>\n<p>&lt;!&#8211; jQuery 3 &#8211;&gt;<br \/>\n&lt;script src=&#8221;\/bower_components\/jquery\/dist\/jquery.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n&lt;!&#8211; Bootstrap 3.3.7 &#8211;&gt;<br \/>\n&lt;script src=&#8221;\/bower_components\/bootstrap\/dist\/js\/bootstrap.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n&lt;!&#8211; AdminLTE App &#8211;&gt;<br \/>\n&lt;script src=&#8221;\/danielm_uploader\/js\/jquery.dm-uploader.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n&lt;script src=&#8221;\/bower_components\/datatables.net\/js\/jquery.dataTables.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n&lt;script src=&#8221;\/bower_components\/datatables.net-bs\/js\/dataTables.bootstrap.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n&lt;script src=&#8221;\/dist\/js\/adminlte.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n&lt;script src=&#8221;\/tg_js\/bootstrap-editable.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n&lt;script src=&#8221;\/tg_js\/dropzone.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n&lt;\/body&gt; <br \/>\n&lt;\/html&gt;<\/p>\n<\/blockquote>\n<h1>Further references<\/h1>\n<ul>\n<li><a href=\"https:\/\/github.com\/sam0x17\/mongo_orm\/blob\/893cd520cb90f7049d53b809ada30198deeed0f7\/spec\/fields_spec.cr\">https:\/\/github.com\/sam0x17\/mongo_orm\/blob\/893cd520cb90f7049d53b809ada30198deeed0f7\/spec\/fields_spec.cr<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/datanoise\/mongo.cr\/commit\/86c9e530c0ac980ed7b15e653746bc5b6f2527fc\">https:\/\/github.com\/datanoise\/mongo.cr\/commit\/86c9e530c0ac980ed7b15e653746bc5b6f2527fc<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/amberframework\/granite\/blob\/master\/docs\/getting_started.md\">https:\/\/github.com\/amberframework\/granite\/blob\/master\/docs\/getting_started.md<\/a><\/li>\n<li><a href=\"https:\/\/docs.mongodb.com\/manual\/reference\/operator\/query\/\">https:\/\/docs.mongodb.com\/manual\/reference\/operator\/query\/<\/a><\/li>\n<li><a title=\"https:\/\/github.com\/datanoise\/mongo.cr\/issues\/19\" href=\"https:\/\/github.com\/datanoise\/mongo.cr\/issues\/19\">https:\/\/github.com\/datanoise\/mongo.cr\/issues\/19<\/a><\/li>\n<li><a title=\"https:\/\/github.com\/datanoise\/mongo.cr\/blob\/86c9e530c0ac980ed7b15e653746bc5b6f2527fc\/src\/mongo\/collection.cr\" href=\"https:\/\/github.com\/datanoise\/mongo.cr\/blob\/86c9e530c0ac980ed7b15e653746bc5b6f2527fc\/src\/mongo\/collection.cr\">https:\/\/github.com\/datanoise\/mongo.cr\/blob\/86c9e530c0ac980ed7b15e653746bc5b6f2527fc\/src\/mongo\/collection.cr<\/a> \u2013&gt; definition of save in mongo.cr<\/li>\n<li><a title=\"https:\/\/github.com\/sam0x17\/mongo_orm\/blob\/015ae29a29ca8b80a98a531de891095bbd42d028\/src\/mongo_orm\/persistence.cr\" href=\"https:\/\/github.com\/sam0x17\/mongo_orm\/blob\/015ae29a29ca8b80a98a531de891095bbd42d028\/src\/mongo_orm\/persistence.cr\">https:\/\/github.com\/sam0x17\/mongo_orm\/blob\/015ae29a29ca8b80a98a531de891095bbd42d028\/src\/mongo_orm\/persistence.cr<\/a> \u2013&gt; definition of save in mongo_orm<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Da die Dokumentation immer noch sp\u00e4rlich ist, w\u00fcrde ich gerne welche hinzuf\u00fcgen. Dies ist das, was ich derzeit baue: Die einzelnen Felder werden mit einem Inline-Editor editierbar sein, der automatisch im Backend gespeichert wird - kein Neuladen der ganzen Seite n\u00f6tig. Der Sinn des Ganzen ist ein Backend f\u00fcr meine...<\/p>","protected":false},"author":830,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[416],"tags":[409,535,532,534,531,533],"class_list":["post-7584","post","type-post","status-publish","format-standard","hentry","category-crystal-language","tag-crystal","tag-crystal-mongo-driver","tag-datanoise","tag-mongo_orm","tag-mongodb","tag-sam0x17"],"_links":{"self":[{"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/posts\/7584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/users\/830"}],"replies":[{"embeddable":true,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/comments?post=7584"}],"version-history":[{"count":1,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/posts\/7584\/revisions"}],"predecessor-version":[{"id":7585,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/posts\/7584\/revisions\/7585"}],"wp:attachment":[{"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/media?parent=7584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/categories?post=7584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/tags?post=7584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}