用Crystal、MongoDB构建持久的在线编辑体验(datanoise / sam0x17)。
由于文件仍然很稀少,我想补充一些。
这是我目前正在建造的东西。
各个字段将可以用在线编辑器进行编辑,这将自动保存到后台--不需要重新加载整个页面。
整个过程的重点是为我的公司提供一个后台,能够自动处理一些流程,计算库存价格,保持库存等。基本上是一个仓库应用程序,为我量身定做
使用的技术
- 水晶
- Kemal
- 管理员2
- 靴带3
- 数据表
- x-editable - 一个非常棒的内联编辑库,可惜已经有一段时间没有人维护了。
- 见Bootstrap 3的例子 这里
- 梦之城_梦之城娱乐_梦之城国际娱乐_梦之城国际娱乐平台
- Mongo ORM by sam0x17
- 这又是基于 datanoise的Mongo Driver for Crystal (mongo.cr)
- 用于编辑的Sublime Text
- 机器人3T 用于MongoDB的GUI
重要位置
MongoORM使用_id作为主索引。 请注意下划线!
_id不是一个字符串。它的类型是 对象Id
因此,它将需要被构建。如果你想从以前的id中构建它,请使用这个代码。
mongo_id = BSON::ObjectId.new my_string_id
其中my_string_id是一个字符串,而mongo_id是一个ObjectId。
准则
config/database.yml
负责设置与你的Mongo DB实例的通信。请参考 萨姆的文件.
stock.cr,负责定义Stockitem、Subitem和适当的Kemal处理程序。
要求 "mongo_orm"
要求 "json"
要求"./macros.cr"
要求"./weee.cr"模块股票
包括宏程序class Itemgroup < Mongo::ORM::Document #used for grouping, e.g. Pimoroni products, Shipping, etc.
字段组:字符串
has_many :stockitems 1TP3注意,stockitem一次只能属于一个ItemGroup。
结束#这也包括其他项目(运输、数字下载等)。
class Stockitem < Mongo::ORM::Document
字段sku:字符串
字段描述:字符串
field is_alias :Bool
field alias_of :String 1TP3如果这个字段被设置,不要使用这个项目的任何其他定义,获取别名的主项目。
字段 is_set : Bool # 如果这个项目是其他项目的一个集合
embeds_many :subitems #应该只在is_set = true的情况下被设置。
字段 is_physical_item : Bool
field weee_applicable : Bool #can be either: paid for someone else, or item is not electronics, ...
字段重量 : Int32 # in g
时间戳
结束class Subitem < Mongo::ORM::EmbeddedDocument
field sku : String #a 对主要库存物品的引用。注意:这可能会以一种更好的方式用 belongs_to 重新设计? 例如, belongs_to :itemref, class_name: Stock::Stockitem
字段注释:字符串
字段金额:Int32
结束get "/stock/show" do |env|
#https://github.com/datanoise/mongo.cr
sis = Stockitem.all({"is_alias" => {"$eq" => false}})
名称 = "股票 :: 显示和编辑"
渲染 "股票"
结束post "/stock/edit/:what" do|env|
pp "进入编辑,目标是" + env.params.url["什么"]
pp env.params.body
如果 env.params.body.has_key?("pk") && env.params.body.has_key?("value")
pp "内部pk和价值"
pk = env.params.body["pk"]?.as(String)
value = env.params.body["value"]?.as(String)
如果 si = Stockitem.find(BSON::ObjectId.new pk)
case env.params.url["what"]
当 "sku"
si.sku = value
当 "描述"
si.description = value
当 "is_alias "时
si.is_alias = value == "true" ? true : false
当 "alias_of "时
si.alias_of = value
当 "is_set "时
si.is_set = value == "true" ? true : false
当 "is_physical_item"
si.is_physical_item = value == "true" ? true : false
当 "weee_applicable "时
pp "weee_applicable"
si.weee_applicable = value == "true" ? true : false
结束
如果si.save
pp "internal si.save"
env.response.status_code = 200
否则
env.response.status_code = 400
结束
否则
env.response.status_code = 400
结束
否则
env.response.status_code = 400
结束
结束#从WEEE条目中生成
get "/stock/generate/:year" do |env|
1TP3走遍整个数据库,收集项目,并显示一个列表
stock_collection = Hash(String,String).new
invoices = WEEE::Invoice.all({"年份"=>env.params.url["年份"].to_i32})
发票.每个都做|发票|
#p "处理发票#{发票.号码}"
1TP3注意,我们可能不会在这里得到绝对所有的发票,因为easybill对日期有不同的看法(进口)。
发票.项目.各做|项目|
if !item.description.nil? && !item.number.nil?
key = item.number == "" ? item.description : item.number
if !key.nil?
if !Stock_collection.has_key?(key)
描述 = ""
if !item.description.nil?
description = item.description
结束
#stock_collection[key] = item.description
if !description.nil?
stock_collection[key] = description
结束
end # end of !Stock_collection.has_key?
end # end of if !"key.nil?
结束
End #end of invoice.items.each do
结束 #end of invoices.each
1TP3检查这个问题是否已经存在于我们的数据库中,否则就创建它。
储存库中的每件物品都会做|sku,description|。
if !Stockitem.find_by(:sku, sku)
si = Stockitem.new
si.sku = sku
si.is_alias = false
si.alias_of = ""
si.description = 描述
si.is_set = false
si.is_physical_item = true
si.weee_applicable = true
si.weight = 100
si.save!
结束
结束
结束#end的获取宏结束1TP3模块的结束
注意事项。
- 萨姆的文档解释了这段代码的部分内容,我只谈谈其中的绊脚石。
- sis = Stockitem.all({"is_alias" => {"$eq" => false}) - 此代码搜索Stockitems(Mongo ORM将为你处理DB名称等)。
- 注意,我没有设法设置({})--将不得不进一步调查这个问题。
- 这种语法基本上是MongoDB所期望的,见 https://docs.mongodb.com/manual/reference/operator/query/
- si = Stockitem.find(BSON::ObjectId.new pk)
- 注意,这里BSON::ObjectId.new是用来从传递的密钥pk中重建id的。
- 成功时返回代码200,失败时返回代码400,以便x-editable知道编辑是否成功。
si.save的行为
- si.save!如果无法保存,将引发一个错误。
- si.save如果可以保存,将返回true,如果不可以,将返回false。
stock.ecr,该HTML模板
。
。
结束 %。
<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”">
请注意:别名的项目不会从数据库中加载。
<thead>
<tr role="”row”">
<th class="”sorting_asc”" tabindex="”0″">种类</th>
<th>描述</th>
<th>is_alias?</th>
<th>别名_of</th>
<th>is_set?</th>
<th>子项目</th>
<th>is_physical_item?</th>
<th>weee_applicable?</th>
<th>重量(g)</th>
</tr>
</thead>
。
tbd.
<a href="#"
id=".weee_applicable"
class="weee_applicable editable"
data-type="select"
data-pk=""
data-value="true"
data-source="[{value: 'true', text: 'true'}, {value: 'false', text: 'false'}] "
data-url="/stock/edit/weee_applicable"
data-title="WEEE: "
>>%= si.weee_applicable ?"true" : "false" %>
g
结束 %
</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>
结束 %
注意事项。
- 因为我们在这之后添加了我们的JS脚本(我应该重新设计,在最后添加一个自定义脚本部分),我们必须将它们包裹在window.onload函数中--否则$就不会被定义(jQuery快捷键)。
- 请注意,我们在这里使用了一个函数,在保存成功后跳到下一个可编辑的地方。
- si._id.to_s.rchop - BSON id被翻译成一个字符串,然后最后一个奇怪的字符(null terminator?)被砍掉。这是一个重要的绊脚石!
layout.ecr(摘录)。
<!
<html>
<标题
<meta charset=”utf-8″>
。
TaxGod |
<meta content=”width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no” name=”viewport”>
。
。
。
。
。
。
。
<link rel=”stylesheet” href=”/tg_css/bootstrap-editable.css”>
。
。
。
<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”>
<!<链接rel="样式表"
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 ::。
做上帝是很有趣的。。
</h1>
<主要内容
<%= yield_content “main” %>
<!– /.content –>
</div>
。(...)
</div>
<!<!– REQUIRED JS SCRIPTS –>
<!
。
<!– Bootstrap 3.3.7 –>
。
<!
<script src=”/danielm_uploader/js/jquery.dm-uploader.min.js”></script>
<script src=”/bower_components/datatables.net/js/jquery.dataTables.min.js”></script>
。
。
/script>
<script src=”/tg_js/dropzone.min.js”></script>
</body>
</html>
更多参考资料
- 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 -> mongo.cr中save的定义
- https://github.com/sam0x17/mongo_orm/blob/015ae29a29ca8b80a98a531de891095bbd42d028/src/mongo_orm/persistence.cr -> mongo_orm中save的定义