YodaOS 中是如何生成 API 的?

在 Node.js 社區(qū)中,其實不乏通過 Markdown 生成 RESTful API 的框架冲九,按照一定的格式約定好 API 所需要的數(shù)據(jù),然后再通過解析 Markdown 文檔跟束,將這些關鍵數(shù)據(jù)提取出來莺奸,最后生成數(shù)據(jù)庫模型和 HTTPS 服務。

YodaOS 作為一個前端操作系統(tǒng)冀宴,同樣使用了類似的技術灭贷。YodaOS 中的應用分為:lightapp 和 extapp,前者是集成在語音交互運行時(Vui-daemon)進程內(nèi)部的輕應用略贮,它主要是用于一個交互簡單甚疟,需要快速響應的場景,比如音量控制逃延、系統(tǒng)控制等览妖。后者作為一個獨立的進程,通過 Child Process 與主進程通訊真友,使用場景主要是音樂黄痪、游戲、電話等需要長時期使用的應用盔然。

為什么要有輕應用桅打?輕應用更像是一個腳本,每當用戶一次進行一次交互愈案,只需要從預先加載的腳本中調(diào)用定義在對應腳本的函數(shù)即可完成一次響應挺尾,往往這類應用交互比較簡單,如果為此要創(chuàng)建在每次交互的過程中進行一次 ipc 甚至 fork 時站绪,無論對性能還是內(nèi)存來說遭铺,都是比較浪費的。

在設計之初恢准,我們期望對于開發(fā)者來說魂挂,并不需要針對不同類型的應用,只需要在 package.json 中修改類型即可馁筐,YodaOS API 應當保持完全一致涂召。這樣的話,我們則面對一個問題敏沉,即使是能做到高度抽象果正,也需要在每次新增一個接口時炎码,修改兩處代碼,這其實是有違我們的設計初衷的秋泳。

API Descriptor

為此潦闲,我們引入了 API Descriptor 的概念:https://github.com/yodaos-project/yodart/blob/master/runtime/lib/descriptor/activity-descriptor.js∑戎澹可以把它看作是用 JavaScript 寫的 DSL歉闰,它用于描述每個 YodaOS API,包括命名空間舍杜、事件新娜、方法等定義。系統(tǒng)在初始化時既绩,會加載所有 API Descriptor概龄,然后分別在 lightapp 和 extapp 生成對應的 API。

Object.assign(ActivityDescriptor.prototype,{/**? ? * When the app is active.? ? * @event yodaRT.activity.Activity#active? ? */active:{type:'event'},/**? ? * When the Activity API is ready.? ? * @event yodaRT.activity.Activity#ready? ? */ready:{type:'event'},/**? ? * When an activity is created.? ? * @event yodaRT.activity.Activity#create? ? */created:{type:'event'}})

上面的代碼分別定義了 Activity 中的幾個事件:active饲握、ready 和 create私杜。因此,在任何應用中都可以這樣寫:

module.exports=activity=>{activity.on('active',()=>console.log('app activated'))activity.on('ready',()=>console.log('app is ready'))activity.on('created',()=>console.log('app is created'))}

接下來我們再看看“方法”是如何定義:

Object.assign(ActivityDescriptor.prototype,{/**? ? * Get all properties, it contains the following fields:? ? * - `deviceId` the device id.? ? * - `deviceTypeId` the device type id.? ? * - `key` the cloud key.? ? * - `secret` the cloud secret.? ? * - `masterId` the userId or masterId.? ? *? ? * @memberof yodaRT.activity.Activity? ? * @instance? ? * @function get? ? * @returns {Promise<object>}? ? * @example? ? * module.exports = function (activity) {? ? *? activity.on('ready', () => {? ? *? ? activity.get().then((props) => console.log(props))? ? *? })? ? * }? ? */get:{type:'method',returns:'promise',fn:functionget(){returnPromise.resolve(this._runtime.getCopyOfCredential())}},})

可以看到救欧,與定義事件的方式一樣衰粹,只需要在 Descriptor 的原型鏈中,增加對應的對象笆怠,然后設置類型(type)為 method 即可铝耻,然后在 fn 中實現(xiàn)函數(shù)。

module.exports=activity=>{activity.get().then((data)=>console.log('credentialse is',data),(err)=>console.error('something went wrong',err))}

這樣除了 API 定義可以統(tǒng)一起來了蹬刷,也能比較方便地基于 JSDoc 生成統(tǒng)一的 API Reference 給開發(fā)者瓢捉,使得整個 API 的修改能做到簡單易讀、門檻低和修改成本低等办成。

API Translator

那么在 YodaOS 中泡态,又是如何將上述的 Descriptor 生成為開發(fā)者直接使用的接口的呢?下面就為大家介紹我們引入的 Translator迂卢。

Translator 是按照我們支持的應用類型對應的某弦,因此對于 lightapp 和 extapp 來說,我們也分為兩個 translator:

進程內(nèi)的?https://github.com/yodaos-project/yodart/blob/master/runtime/client/translator-in-process.js

進程間的?https://github.com/yodaos-project/yodart/blob/master/runtime/client/translator-ipc.js

本文并不具體展開每個 translator 的工作原理而克,但會做一些簡單的流程介紹靶壮。以 translator-ipc 為例:

module.exports.translate=translatefunctiontranslate(descriptor){if(typeofprocess.send!=='function'){thrownewError('IpcTranslator must work in child process.')}varactivity=PropertyDescriptions.namespace(null,descriptor,null,null)listenIpc()returnactivity}

每個 translator 提供一個函數(shù),即 translate(descriptor)员萍。它接受一個 descriptor 對象亮钦,然后會遍歷原型鏈中的對象,并且分別按照 namespace充活、event 和 method 去生成一個叫 activity 的對象蜂莉,最后將這個對象返回給開發(fā)者。

當開發(fā)者在使用某個 API 時混卵,activity 對象會按照 translator 預先生成(約定)好的邏輯調(diào)用到服務端(Vui-daemon)映穗,最后再通過 Promise 返回調(diào)用后的結果,從而完成一次接口調(diào)用幕随。

后記

本文簡單介紹了 YodaOS 在 API 設計過程中蚁滋,如何利用 DSL,解決 YodaOS API 在多種應用形態(tài)保持一致性赘淮。以此辕录,我們希望拋磚引玉:

幫助讀者更好地了解 YodaOS API 的生成過程

幫助讀者了解到 DSL,也能將這種思路應用在自己的項目中

如有更多問題梢卸,歡迎評論走诞,或者直接在 GitHub 上給我們提問題:Build software better, together

參考

D-Bus introspection:Introspection - Using of D-Bus

YodaOS:YODAOS Project

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蛤高,隨后出現(xiàn)的幾起案子蚣旱,更是在濱河造成了極大的恐慌,老刑警劉巖戴陡,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塞绿,死亡現(xiàn)場離奇詭異,居然都是意外死亡恤批,警方通過查閱死者的電腦和手機异吻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喜庞,“玉大人诀浪,你說我怎么就攤上這事「尘#” “怎么了笋妥?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窄潭。 經(jīng)常有香客問我春宣,道長,這世上最難降的妖魔是什么嫉你? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任月帝,我火速辦了婚禮,結果婚禮上幽污,老公的妹妹穿的比我還像新娘嚷辅。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布晰搀。 她就那樣靜靜地躺著竖席,像睡著了一般显晶。 火紅的嫁衣襯著肌膚如雪泰佳。 梳的紋絲不亂的頭發(fā)上役拴,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天葫哗,我揣著相機與錄音作烟,去河邊找鬼寺擂。 笑死暇务,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的怔软。 我是一名探鬼主播垦细,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼挡逼!你這毒婦竟也來了括改?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤挚瘟,失蹤者是張志新(化名)和其女友劉穎叹谁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乘盖,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡焰檩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了订框。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片析苫。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖穿扳,靈堂內(nèi)的尸體忽然破棺而出衩侥,到底是詐尸還是另有隱情,我是刑警寧澤矛物,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布茫死,位于F島的核電站,受9級特大地震影響履羞,放射性物質發(fā)生泄漏峦萎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一忆首、第九天 我趴在偏房一處隱蔽的房頂上張望爱榔。 院中可真熱鬧,春花似錦糙及、人聲如沸详幽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唇聘。三九已至版姑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雳灾,已是汗流浹背漠酿。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谎亩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓宇姚,卻偏偏與公主長得像匈庭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子浑劳,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容