duangsuse::Echo
714 subscribers
4.24K photos
127 videos
583 files
6.46K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
duangsuse::Echo
GeekSpec := ApiSpec* ApiSpec := (httpMethod '@')? interfaceName '(' Arg* ')' ('->' (ReturnType|Dict))? '=' urlPath Arg := argName '?'? OptionVals? (',' Arg?) OptionVals := '{' possibleValue (',' possibleValue)* '}' ReturnType := (array|object) ':'…
递归的规则能不要写就不要写

上面那个

Args := argName '?'? OptionVals? (',' Args)?
就应该写成 Repeat 组合子的形式,一上来就递归很难受,免得麻烦,要知道 PEG.js 那个鸡肋破真参数绑定工作下,即使好好分析了也未必能做到解决问题... 难受的很
{
let filterIsNotArray = (xs => xs.filter(x => !Array.isArray(x)));
let isNotUndef = (s => s != 'undefined');
let head = xs => xs[0]
}

GeekSpec = specs:(_ ApiSpec _)* { return specs.map(x => head(filterIsNotArray(x))); }

entityName = letter+
interfaceName = letter+
urlPath "url letters" = (letter / [:/.{}?&=%])+
argName = letter+
possibleValue = letter+
fieldName = letter+

httpMethod
= "GET" / "POST"
/ "PUT" / "DELETE"
/ "PATCH" / "OPTIONS"
/ "HEAD"

ApiSpec =
name:interfaceName _ '(' _ a:Arg? as:(_ "," _ Arg)* _ ')'
_ rtxd:('->' _ (ReturnType / Dict))? _ '=' _ url:urlPath {
return {
method: "GET",
name: name.join(''),
args: a == null ? [] : [a].concat(as.map(ra => ra[3])),
return : isNotUndef(typeof rtxd) ? (rtxd == null ? null : rtxd[2]) : null,
url: url.join('')
};
}
/
method:httpMethod '@' name:interfaceName _ '(' _ a:Arg? as:(_ "," _ Arg)* _ ')'
_ rtxd:('->' _ (ReturnType / Dict))? _ '=' _ url:urlPath {
return {
method: method,
name: name.join(''),
args: a == null ? [] : [a].concat(as.map(ra => ra[3])),
return : isNotUndef(typeof rtxd) ? rtxd[2] : null,
url: url.join('')
};
}

Arg = name:argName _ q:('?' / "!"?) _ ov:OptionVals? {
return {
name: name.join(''),
required: q != "?",
options: ov
};
}

OptionVals = '{' _ pv:possibleValue? pvs:(_ ',' _ possibleValue)* _ '}' {
return pv == null ? [] : [pv.join('')].concat(pvs.map(rpv => rpv[3].join('')));
}

ReturnType
= axo:("array" / "object") ':' a:Atom { return { type: axo, of: ((typeof a != 'string') ? a.join('') : a) }; }

Atom = "boolean" / "number"
/ "string" / "datetime"
/ "plain" / entityName

Dict = '[' _ di:DictItem? dis:(_ ',' _ DictItem)* _ ']' {
return di == null ? [] : [di].concat(dis.map(rd => rd[3]))
}

DictItem
= ReturnType
/ '$' name:fieldName ':' rt:ReturnType {
return { name: name.join(''), type: rt };
}

_ "whitespace"
= [ \t\n\r]*

letter "letter"
= [A-Za-z0-9_\-]


大功告成(其实一点都不,而且某非常智障的 duangsuse 没有用到递归(因为这个玩意开始的时候我输入的数据比较多,分析起来当场爆炸留下心理阴影,列表处理方法我又到处乱用 e.g. splice, reverse, concat 却没有想到 map...),非常不爽) #Parser #PL

管他呢反之先火速处理(
geekspec_parser.js
56.4 KB
#PL #DSL GeekSpec DSL 的解析器,准备写代码生成来的
duangsuse::Echo
{ let filterIsNotArray = (xs => xs.filter(x => !Array.isArray(x))); let isNotUndef = (s => s != 'undefined'); let head = xs => xs[0] } GeekSpec = specs:(_ ApiSpec _)* { return specs.map(x => head(filterIsNotArray(x))); } entityName = letter+ interfaceName…
简单点来说,GeekSpec DSL 使用类似如下语法:

GET@methodName(args) = urlTemplate
如果 使用的 HTTP 动词是 GET,那 GET@ 可以省掉不写

args:
argName! argName?
如果不写 ? 或者 !,默认 ?(可选参数)
后面还可以跟 {} 包含参数值选项

返回值:符合 JSON 规范,开头必须是 object: 或者 array:,不支持递归类型(比如 array:array:string)

后面可以跟 boolean/number/string/datetime/plain/entityName(随便什么其他的东西)

返回值也可以是 Dict:
[]

dict 组织一打不同的返回值,其实是 object(不关心属性名),可以为他们命名

[$name:type]
duangsuse::Echo
geekspec_parser.js
GeekApk 的 v1b API 规范正在使用 GeekSpec DSL 语言重写
好像能用
duangsuse::Echo
好像能用
目前勉强能用的 GeekSpec DSL 将能够解决不断手工重复代码的问题,起码可以大大提升以后的接口开发效率

当然你也可以喷我不上 RESTful 所以才有这些鬼事情,不过我觉得没什么。
duangsuse::Echo
GeekApk 的 v1b API 规范正在使用 GeekSpec DSL 语言重写
https://github.com/duangsuse/GeekApk/blob/add-controller-template/code_writer.js

现在已经开始使用 Code Writer JavaScript 脚本自动生成测试了,明天会尝试使用它生成 Timelinecontroller

GET user/{id}/checkHash: checkHash(id-path:UserId!,hash:String!)
Returning object of string

GET user/all: listUser(sort:String?,sliceFrom:UserSize?,sliceTo:UserSize?)
Returning array of GeekUser


所作额外的扩展是一些内部类型信息上的,这样就可以用它来生成 Kotlin 控制器接口,只需要简单调用一下 String.prototype.split 就可以。
所有参数名都可以额外有 :TypeName 类型信息,optional 的参数(?参数)自动适配 Kotlin 可空类型系统

参数名也可以额外有 - 位置信息,目前可选项为 body 或 path,对应 Spring Controller Binding 的 @RequestBody@PathVariable

目前的生成效果(未来将被替换为生成 Kotlin 代码)
还是会用 radare2 的 ( #reveng
Forwarded from 羽毛的小白板
可以得出一个链表每项后代的方法。这里也用到数组作为中间状态避免链表里的环导致无法结束迭代 #Pg好玩
目前,GeekSpec 的 Kotlin/Spring 代码生成器已经可以勉强正常工作,不过不是所有特性都测试到了,目前借助强大的 IDEAc 它的效果很好,一 瞬 生 成了之前我要写到手软的代码,大家可以来体验一下(虽然一点都不 production ready,有点像那种小脚本
duangsuse::Echo
目前,GeekSpec 的 Kotlin/Spring 代码生成器已经可以勉强正常工作,不过不是所有特性都测试到了,目前借助强大的 IDEAc 它的效果很好,一 瞬 生 成了之前我要写到手软的代码,大家可以来体验一下(虽然一点都不 production ready,有点像那种小脚本
目前的版本它的作用就是把这种 GeekSpec 代码:

searchUser(type:String?{username,nickname,bio}, kw-path:String, sort:String?{created,followers}) -> array:GeekUser
= user/search/{kw}

翻译成 Kotlin + Spring Controller bind 的控制器接口

@GetMapping("user/search/{kw}")
@ResponseBody
fun searchUser(@RequestParam("type") type: String/* Maybe username or nickname or bio */, @PathVariable("kw") kw: String, @RequestParam("sort") sort: String/* Maybe created or followers */): List<GeekUser> {
TODO()
}

目前仍然有不少小瑕疵正在修改
This media is not supported in your browser
VIEW IN TELEGRAM
(因为我的 Throws 上面已经有了,这里转发个大佬的(

当作自己的祝福喽(因为人类的本质是...
Forwarded from 羽毛的小白板
祝大家新年快乐
duangsuse::Echo
目前,GeekSpec 的 Kotlin/Spring 代码生成器已经可以勉强正常工作,不过不是所有特性都测试到了,目前借助强大的 IDEAc 它的效果很好,一 瞬 生 成了之前我要写到手软的代码,大家可以来体验一下(虽然一点都不 production ready,有点像那种小脚本
作为对比,Swagger 代码:

https://github.com/GoliGoliTV/api-document#simplified-guides-on-openapi-specification

  /users:
get:
summary: Returns a list of users.
description: Optional extended description in CommonMark or HTML.
responses:
"200": # status code
description: A JSON array of user names
content:
application/json:
schema:
type: array
items:
type: string

在 VSCode 上,可以有自动补齐(估计是类似 XML 的 Spec 文件)

GeekSpec 的主要区别是它完全只是为简化 GeekApk 开发设计的,它单单是为了解决类似 GeekApk 一样代码繁复的问题,而不是如何让自己对 80% 的项目可用的问题

所以它可以写得这么简洁,而又能生成真正对开发有效果的 boilerplate code:

getAllUsers() -> array:string = /users

利用目前可用的 Kotlin Spring Codegen,它可以生成这种代码:

@GetMapping("/users")
@ResponseBody
fun getAllUsers(): List<String> {
TODO()
}

它省掉了上面 Swagger 文档的很多信息,比如 Content-Type、status code、description 等等,但是,它真的非常适合用来进行 API 设计,因为你可以把心思全用在枚举和思考上,而不是去辨识 YAML 二维文法结构

因为我太垃圾(因为我是熬夜写的 PEG 语法)的缘故,它没有在语言层面上支持注释和文档,不过 GeekApk 以后如果要重写,GeekSpec 自然也会跟着重写的。那时候,可能 GeekApk 服务端将完全使用 Project Template 和 GeekSpec (那时应该支持利用宏系统和预处理器实现接口逻辑编写了)语言重写


请允许我飘一会,理解一下吧,因为熬夜的成品真的是... 血汗码啊(Maybe

所以,单单对 GeekApk 和类似 GeekApk 的项目来说,使用 GeekSpec(如果你了解 Visitor Pattern,或者尝试写过图遍历算法诸如解释器的话)可以大大减少 Boilerplate code (这里包括部分 Swagger OpenAPI Spec)对你编程效率的影响,因为一切都将可以被自动生成。

自动生成,不需要再一遍一遍 Check,不需要再一次一次进行重复相同的击键,利用 GeekSpec 和拥有 JSON 解析功能的语言和 GeekSpec 解析器和类似 IDEA 的好开发工具,就可以快速编写 Codegen 程序,解决繁复的接口定义!

instance:

https://github.com/GoliGoliTV/api-document 使用 GeekSpec 梗概:

# Sample API
# description
# version: 0.1.9

# Servers
# http://api.example.com/v1: Optional server description, e.g. Main (production) server
# http://staging-api.example.com: Optional server description, e.g. Internal staging server for testing

# SecuritySchemes
# bearerAuth: JWT(bearer, http)
# schema: User(id: Int, name: String)

# Schemas
# User(id: Int, name: String)

# Returns a list of users.
getAllUsers() -> array:string
= /users


# Returns a user by ID.
getUserById(userId-path:Int) -> object:User
= /users/{userId}

# Creates a user.
POST@createUser(user-body:userNameObject)
= /users
Forwarded from duangsuse Throws
其实狗利狗利的问题也是没有募集到几个稍微有点常识的程序员 🐸 #MoHa #Statement

如果去看看他们的『项目主页』(@ GitHub,就会发现,很多项目都只是一个空壳,根本没有一点业务代码

如果说高代码质量、消息队列、Server side push、完全全栈自动化集成测试、OAuth 和社会化登录、使用图数据库、真正的 RESTful(其实这个也不算) 算是稍微复杂一点的需求,基本的 endpoints 定义和 RDBMS 的 Query Validate 逻辑算是简单一点的需求(对于 MVC 应用)


那么苟利苟利做得还不够好啊,因为它的公开源里,

只有 - 项目,是真正能用的。

如果没有那两个项目的开发者(都是一个人)
苟利苟利可能复活吗?其实完全看这一个人。