搜索
Table_bottom

标签云
Table_bottom

分类
Table_bottom

声明
文章若未特別註明,皆採用 知识共享许可协议 請自覺遵守
Table_bottom

鏈。。。
Table_bottom

存档
Table_bottom

匆匆过客
39468
Table_bottom

功能
Table_bottom

Rocket使用小結

(本文亦在wikiblog中)

在今年Increase Rust's Reach中,我參與Rust新網站的i18n及l10n。其中新網站要基於Rocket構建,所以也就(跟着官方教程)學習了一下Rocket。 既然學了,就順便記錄一點心得和體會,以方便後來者。

Rocket是一個 web框架 。我個人對web編程(尤前端)並不太感興趣(主要是感到 web技術棧 太過麻煩/複雜),所以涉及不太多,之前也只用過Python那邊的Flask以及(一小段時間)Django以及Go自帶的http服務器,故而本文不怎麼會涉及和其他web框架的對比。

本文不打算成爲通常意義上的Rocket教程,而只是打算給有興趣者一個快速的(對rocket的)觀感。其中也會有一些個人的經驗教訓等。

Rocket概覽

類似我之前用過的框架,Rocket也將函數作爲不同的路由的處理器。Rocket在每個函數之前使用形如#[get("/myroute")] 屬性 作爲標記,之後在Rocket入口對象/結構體上對所需要的路由(函數)進行mount即可。

#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}

fn main() {
    rocket::ignite()
        .mount("/", routes![index])
        .launch();
}

(代碼片段來自這裏。)

這點粗看很像Flask中使用@app.route()進行路由設定,僅有這兩點不同:

  • Rocket不使用全局的app對象
  • 路由可以定義但不掛載

我最初也以爲Rocket和Flask的設計極爲相似,且兩者都只打算做web框架而不涉及其他;然而,越到後面越是發現兩者不同之處的巨大。相對而言,我個人更喜歡Rocket的設計:更加函數導向(亦強調使用局部變量)。

  • 在Rocket中,函數是處理路由的全部,不需要使用如flask中魔法一般的全局request對象;
  • 函數的參數和屬性中的設定共同決定了路由是否匹配,手動類型的優勢在這裏有所體現;
  • 各種(預定義或自定義的) 請求哨衛 可以被添加到函數參數表中參與決定路由的匹配性;
  • 使用 整流器 在請求到達前或應答發送時對請求或應答進行調整;
  • 使用State做狀態存儲,以便的確需要“全局”變量的情況。

路由匹配

Rocket通過在屬性上設定不同的HTTP方法以及URL段來做匹配。 具體細節見官方文檔,這裏僅做摘要:

  • 常見HTTP方法均被支持,只是每次只能設定一個方法
  • 在未定義時,HEAD請求會被自動轉到相應的GET請求上(不過會刪除應答體)
  • 表單首個字段爲_method時,POST請求會被自動重譯爲相應的請求
    • 該設定是爲了方便瀏覽器,畢竟瀏覽器通常只有GET和POST

在路由的URL上,可以設定將部分(或全部) 注入到函數的對應參數中。Rocket會自動進行類型轉換,且僅匹配轉換成功的路由。

#[get("/hello/<name>/<age>/<cool>")]
fn hello(name: String, age: u8, cool: bool) -> String {
    if cool {
        format!("You're a cool {} year old, {}!", age, name)
    } else {
        format!("{}, we need to talk about your coolness.", name)
    }
}

(代碼片段來自這裏。)

自定義類型也可用在路由匹配中,只要其實現了FromParam trait即可。

請求及應答

在不考慮整流器的情況下,用戶的請求將直接進入相應的路由函數中,然後經過函數的處理,最後函數的返回值將作爲應答。路由匹配的過程即是請求處理器的選擇過程。

Rocket不要求路由函數的返回值是一個HTTP應答(Response),而是通過Responder機制方便編程:路由函數的返回值需要是一個實現了Responder trait的類型,而Rocket負責調用Responder的相關函數將路由函數的返回值轉換爲HTTP應答。這樣,在Rocket中我們便可以用String等類型作爲函數返回值。

Rocket提供一些實現以應對常見的應答情況:

  • 應答包裝器 可以包含其他Responder,並且執行自己的修改
    • status可以覆蓋應答中的狀態代碼
    • content可以覆蓋應答中的Content-Type字段
    • Error(我沒嘗試過,所以直接鏈接至官方文檔)
  • String&str會被作爲應答體,且Content-Type會被設置爲text/plain
  • Option是應答包裝器,Option<T>T需要實現Responder
    • 當是Some時,其內容將會被作爲應答
    • 當是None時,返回404
  • Result是應答包裝器,且其功能取決於E是否實現Responder
    • E實現了Responder,則該Result會被作爲應答(無論是Ok還是Err
    • E沒有實現Responder,則Ok會被作爲應答,但Err會被記錄在終端中且返回500

官方文檔中還列出了幾個常見的對於HTTP很有意義的Responder實現,包括下面所說的Template。)

網頁模板

作爲一個web框架,提供對網頁模板的支持幾乎是理所應當。Rocket本身提供了Template機制,而在rocket_contrib crate中提供了一些特定模板支持。

Template被實現爲一個Responder,這樣讓響應函數返回Template類型即可:

#[get("/")]
fn index() -> Template {
    let context = /* object-like value */;
    Template::render("index", &context)
}

Rocket不限制使用何種模板,但官方文檔提到了.hbs Handlebars和.tera Tera。而Rocket的Template機制之所以有效,還需要整流器的幫助——所以需要在Rocket實例上.attach(Template::fairing());以便可以正確使用模板。

整流器

依Rocket文檔所說,整流器的功能類似於中間件,可以介入請求和應答過程以進行額外操作。由於我沒有學過相關課程,也沒有太多瞭解相關知識,所以無法給出個人對此的看法,只能照搬官方文檔的說法。

在類似其他框架的中間件之外,Rocket對整流器的功能有一些額外規定:

  • 整流器不能終止或直接響應請求
  • 整流器不能任意注入非請求數據至請求中
  • 整流器可以阻止程序的啓動
  • 整流器可以修改程序的配置

官方文檔對整流器有更多說明,但對我來說最需要知道的還有這些:

  • 整流器應當只用於“全局”適用的東西(比如做日誌)
  • 更多時候,需要的其實是 請求哨衛 數據哨衛
  • 整流器按順序執行,所以.attach()的順序需要注意

全局共享數據

這裏的“全局”指的是Rocket之內,在各個路由中共享數據。由於路由是由Rocket管理的,故而其參數表中沒辦法添加更多參數;而Rust又沒有全局變量(即使有也不符合美感),故而Rocket提供的 狀態 機制可謂實用非常。Rocket官方教程中也教導使用狀態來管理數據庫連接。

使用上,狀態同樣通過 請求哨衛 機制,作爲路由函數的參數。任何類型的數據均可作爲State,且不需要額外實現任何東西。唯一的要求就是在Rocket實例載入時要求管理狀態

像這樣要求Rocket去管理某個狀態

struct HitCount {
    count: AtomicUsize
}

rocket::ignite()
    .manage(HitCount { count: AtomicUsize::new(0) });

像這樣要求在某路由函數上使用某狀態

#[get("/count")]
fn count(hit_count: State<HitCount>) -> String {
    let current_count = hit_count.count.load(Ordering::Relaxed);
    format!("Number of visits: {}", current_count)
}

#[get("/state")]
fn state(hit_count: State<HitCount>, config: State<Config>) -> T { ... }

需要注意的是,Rocket對每種數據類型管理一個狀態,而不是每個數據。

另外,在自定義的 請求哨衛 中,也可以使用狀態

fn from_request(req: &'a Request<'r>) -> request::Outcome<T, ()> {
    let hit_count_state = req.guard::<State<HitCount>>()?;
    let current_count = hit_count_state.count.load(Ordering::Relaxed);
    ...
}

(代碼片段來自這裏。)

總結

總得來說,我個人對Rocket的設計較爲欣賞/膜拜,尤其是其對Rust各項機制的有效利用。

之前用其他框架時總有覺得彆扭的地方,但它們均不存在於Rocket中,讓我寫起來覺得比較順手:

  • Django(2013年底或2014年初)
    • 框架內耦合性太強,但框架的手又過長
    • 什麼都想讓框架承包,初學者用起來束手束腳
      • 當然,也可以說是我還沒有體會到Django的好處。但我實在是對封閉花園式的東西感到反感,所以不見得可以體會到Django的妙處
    • 而且當年對Py3的支持還不怎麼樣,但我又恰恰想用Py3
  • Go(2015年)
    • Go的自帶http庫直接將請求和應答對象作爲參數傳入,手動解析很難受
    • Go的html模板是語言提供的,靈活度上讓我懷疑
    • 當時(2015年)查過其他的go語言web框架,比較看好的有beego以及另一個想不起來名稱的。但其教程寫得並不如人意(不如我意),又由於當年需求十分簡單,所以直接裸上語言庫
  • Flask(2016年)
    • Flask要使用全局的app對象和db對象,設計上很詭異
    • Flask要使用魔法一般的request對象,總讓人覺得不安心
      • 且request對象暴露太多內容,類似Go用http庫的感覺(和使用Android的Context對象的感覺很像)

然而,我對Rocket的部分trait和/或類型設計仍有疑惑,還在尋找解決方案的過程中。 另外,我暫時還沒有在Rust中使用過數據庫連接,所以無法對其聯合使用後的手感做出評價。 但綜合來看,Rocket的設計可以說是我所用過的所有框架中最符合我心意的框架;而且它的設計理念可以說符合了我對web框架所構想的所有主要要求。如果不是因爲Rust仍算小衆,Rocket的用戶量和教程量應當早就超過現有的數量了吧(2018-08-02 Google搜“Rust Rocket 教程”,得到11,200條結果)。

 

Yu Yan Wen Zi (Chinese)

(This article is the English translation of my another post 語言文字.)

Whenever we talk about China, we usually have some thoughts about "long history", "continous civilization" or so; we are proud of this undoubtedly. The main reason we say that Chinese civilization is continous compared to other antient civilizations is that the language never cut off.

 

From the Seal Script to Clerical Script and from Clerical Script to Regular Script, the historians tell us the scripts/glyphs (i.e. the written part of the Chinese language) linearly derives from the same origin; although the appearance differs a lot, the Oracle Bone Script (discovered in the 20th century) still shows the same thread. After the set up of the Regular Script, the biggest change is the so-called Simplified Chinese, but it still lies in Regular Script. This is what everyone knows: we even feel our ears have been grinded to emerge cocoon. However, intentionally or unintentionally, we seem to forget that "Yu Wen" (Chinese) is "Yu Yan" (vocal form) "Wen Zi" (written form) -- except for the written part, there is still the vocal part.

Yes, the change of vocal form is harder to record compared to the written form -- there are handwrittings, records and materials and it can be written or drawn; however it's almost impossible to record the voice during the era without phonograph. The ancestors tried best only to do transliteration/transcript and/or record analogue voices -- this may be the only pity of our logogram / logographic language. Luckily, the ancient pronunciation is not entirely buried under time: phonologists figuried out some methods to try to capture the original form of the ancient pronounciations -- by using books like 《廣韻》, with the 反切 notations accummulated during history (by ancient people), and rhythmic literals like poems. My understanding towards this direction only goes as far as here, but at least I capture one important message: there are patterns and threads in the pronunciation change.

This understanding facilitates my point of view towards the present standard Chinese (i.e. Mandarin). Like what I answered in the Zhihu question《为什么角色的角念jue,却仍有人念Jiao ?》:

Instead of the view of most people that "pronunciation is always changing so there is nothing to keep an eye on", my point of view is that "some pronunciations are acceptable (i.e. follows the evolution pattern), while some are not acceptable (i.e. violates the evolution pattern and does randomly)", and I believe that "the official standard should keep a balance".

The foundation of this point of view is that: language is the tool of communications, but it is not only for the communication of people within the same era, but also across the river of time. The best choice is, apparently, to satisfy both, but if not possible, try to satisfy as much as possible and choose the one which does less "harm". However, unfortunately, people / organizations tend to, for most cases, choose to satisfy the contemporary needs -- anyway who they deal with is the people of current era, not the ancestors or descendants.

Today, when I read an article titled 《说shuō客?坐骑qí?我怕是上了个假学!》 which (again) lists what the National Language Working Commission (I used its old name, Language Reform Commission, for a derogatory sense) does to the pronunciation standards, I feel both rejoice and helpless: the LRC is still what the LRC was -- brain-damaged and doing nothing useful.

What the LRC does mostly in recent years is changing the pronuncations of some characters in Mandarin. The goal is, with no exception, to "accord with the public understanding", which simply means "if a lie is only printed often enough, it becomes a quasi-truth". This is, apparently, a method with no regards to the history, and I'm afraid it has little influence towards the communications between contenporary people. The self-learning ability of people is far better than what the bureaucrats in LRC imagine -- we can figure out and understand that two different pronunciations possibily refer to the same word, and we can "ask" even if we don't understand. If you argue that what the LRC does is to reduce the burden of students (especially those before college), this is the biggest joke of the world. There is also some (not a small portion of) people who don't possess the same pronunciation as the "standard" (because of dialect, mis-hearing, following the historical pronunciation, etc.), so there is always the need to teach about the "standard" in primary, secondary and high school; since there is the need to teach about the "standard", the effort is always needed, and time consumptions is always needed. It doesn't make any sense to consider whether there are more students or less students who need to work hard to memorize the standard (because this is not possible due to the variety).

That article specifically points out one case: some "standard" has changed from "follow the history evolution" to "accord with public" (set aside whether the sample is typical or not) and then back to "follow the history evolution". This demonstrates again that the LRC has no consensus about what the standard should be.

 

Of course, it seems that the article 《说shuō客?坐骑qí?我怕是上了个假学!》 possesses a ridicule attitude towards that "the public requires to keep the previous standard of the pronunciation" -- (I think) the author would either think that the pronunciation only needs to suit contemporary needs, or is just ridiculing habitually. First of all, the author seems to consider that we could only choose either to be completely unchanged or to change arbitrarily. Secondly, the author argues that the public's attitude towards pronunciation standard is "to support what fits my habit, to oppose what doesn't" -- this is shown from the fact that the author lists, for many times, how the public reacts to the "previous" change of pronunciation standard and even meaning and concludes that the public has no objection. Finally, the author seems to believe that people, i.e. the public, is only the (passive) acceptor of pronunciation standard change, and all what will be changed solely depends on the bureaucrats of LRC.

However, for all these three points, I beg to differ.

As stated previously, the choice of pronunciation should be a trade-off procedure, and it should "try to satisfy as much as possible and choose the one which does less 'harm'". The crucial point is neither to consider to change or not to change, nor to consider whom to listen to -- it is to find the balance between history (both retrospective and prospective) and contemporary era. Still, as stated above, whichever the standard is won't make a significant impact of the current era, so I believe history shoule be placed extra emphasis on. The reaction (of standard change) of contenporaries is foreseeable: some (but will never be "most", as long as the change is reasonable) people will oppose. Therefore, why doesn't the people who make the standard issue the reason at the same time? People are not idiots and we can reason about things. As long as the change is reasonable, most people will agree.

The author seems to be unaware that in some parts of the WWW, many people are discovering and researching about the original meaning of words and proper pronunciation of characters, and they spontaneously sum up the law and keep telling other people what these things should be like. Maybe the author considers the number of these people is too few, but what I saw is that the number is increasing and more and more people accepted the dissemination of the "proper". Possibily because this process moisturizes things in silence, the author may, probably, have already seen the outcomes of it, but was unaware where this came from.

Then, for the people who makes the standard, LRC as mentioned, I will sneer at them as always. If they agreed with the people's republic, instead of being sinecures, LRC should be cautious and conscientious, seek for balance between now and history, and detail that to people; if they were composed of the scholar-officials (like in the antient time), LRC should insist on the history, rather than changing back and force. Whichever form they were supposed to be, LRC didn't do the right thing, so they ought to accept my ridicule. And either in the ancient time or in mordern time, either in the East or the West, the government is supposed to listen to or be operated by the people. Now the fact that some departments of the government doesn't perform in accordance is not the reason we agree with them -- on the contrary, this is the reason that we should work harder to point out and combat.

Against EU Copyright Directive Article 13

Description

While reading wikipedia these days, there is a new banner calling for against the new "Directive on Copyright in the Digital Single Market" (which will be voted on 5 July) / "EU Copyright directive" / "file 2016/0280(COD)".

The three buttons link to the following pages respectively:

A brief reading shows that many organizations/foundations (e.g. EFF, Creative Commons, Wikimedia Foundation) oppose to this directive.

The context of this directive can be found at: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:52016PC0593

The main against of this directive is Article 11 and Article 13, especially Article 13.

My words

I should say I agree with many of the disputes that Article 13 should be reformed before applied (explanation at the end). One most probable consequence is that MEMEs will become unavailable. (And this will be one more of the stupid things people have done in recent years, just to satisfy the stale copyright laws.)

I don't know what really works, but I found three sites describing some ways:

Explanation

The texts in the given link differs some bit between what's described in that wikipedia article. I presume this because of the different versions of the document.

Article 13 grants the censoring and blocking ability and obligation to "Information society service providers" (mostly social networking websites, including both commercial ones, e.g. twitter, facebook, and non-commercial ones, e.g. GNUSocial instances, if I understand correctly). This ability suggests them to use "effective content recognition technologies" to "prevent the availability on their services of works or other subject-matter identified by rightholders (through the cooperation with the service providers)".

Although this ability and obligation is supposed to be "appropriate and proportionate", but at least I don't believe commercial bodies (i.e. companies) will do this "appropriately" in a minimal effort way. They will overactive, both for their "compliance with the law" and for their profit purpose. Some companies in China have already demonstrated this, and I guess there are also some examples in Europe and America.

The piece of text says "identified by rightholders", but in reality "rightholders" in many cases are not a single human but a company. We have heard many stories how companies over-use their copyright to prevent what we as humans see as normal behaviours (let alone I don't totally agree with the copyright [law] nowadays because I think they are developed for paper-publishing era not digital era).

The "Information society service providers" are, most of the time, companies; companies are for profit. Therefore, the nature of capital makes them not sympathetic, and blocking the Internet doesn't really affect their profit (because "everyone" does this, leaving us no choice). https://twitter.com/EvenDragsnes/status/1014394747706925056 is definitely not a future I want.

(Terrorism and some other things shall be dealt with, but this directive has nothing to do with that.)

在GitLab Pages部署TiddlyWiki5

想將TiddlyWiki搭建在GitLab Pages已經很久。今日終於用半個小時時間完成此事,完成後覺得竟然如此簡單。然而之前始終找不到合適的教程,實在讓人糾結,故而記錄要點(並簡單介紹TiddlyWiki在該模式下的使用),以便有需要者。

簡介

TiddlyWiki是一個工作在瀏覽器中的wiki系統,其對我來說最有用的特性是:1) 支持插件(通過JS);2) 支持自定義宏 (macro)。

由於TiddlyWiki設計爲在瀏覽器中工作,所以理所當然地可以託管在GitHub Pages或GitLab Pages這類靜態網站上。雖然使用TiddlyWiki本身的單一HTML版即可,但我總覺得這樣比較臃腫,而且不好追蹤版本變更,所以更傾向於這種使用server版功能的方式(即每個tiddler是單一文件,之後轉換爲網站)。

雖然已有前人給出的搭建模板,只用fork過來即可使用,但我個人略有強迫症,想要一個“清淨”的版本。所以簡單翻閱了GitLab的相關說明頁面後,仿照其例子自己從頭構建了一個倉庫(及對應Pages)。

本文假設讀者有基本的git知識,並且已安裝tiddlywiki服務端軟件(tiddlywiki)。下面的描述均假設當前在一個空目錄/空的git倉庫中。

初始化TiddlyWiki

TiddlyWiki支持兩種模式:單一HTML頁面模式本地服務器模式。既然要使用版本控制,本地服務器模式更加適合:該模式下,每個頁面(即Tiddler)均分別以文件形式存放,更適宜版本控制。

首先,使用下面命令創建服務器模式的TiddlyWiki:

tiddlywiki wiki --init server

該命令將會創建一個名叫wiki的目錄,其中存放TiddlyWiki服務器模式配置。要在本地瀏覽或更改該wiki的內容,執行下面命令,並且打開瀏覽器訪問localhost:8080:

tiddlywiki wiki --server

對該wiki的編輯均會存放在wiki目錄下(的tiddlers目錄中)。而且配置、插件等也是以文件形式存在,可以說是無縫接合。

在該模式下,對tiddler的修改會自動保存(可能有較小延遲),而不需要(也不應該)使用“保存”按鈕——除非你的確想將你的wiki保存爲單一HTML。類似地,在GitLab Pages的頁面上的修改不會被持久化,因爲沒有後端服務程序在執行——但你可以將其當成一個安全的沙箱隨便修改。

GitLab Pages

GitLab Pages是使用GitLab的流水線機制而構建起來的特殊功能。其理念源於GitHub Pages,但實現方式有所區別,且GitLab Pages更爲靈活(至於github pages如要達到該靈活度,則必須依賴外部CI)。

在倉庫根目錄中,.gitlab-ci.yml控制GitLab流水線如何執行——當push發生時,GitLab會自動根據該文件執行CI。GitLab Pages在.gitlab-ci.yml中使用專門的pages節。在該CI執行結束後,倉庫(生成文件中)的public目錄中的文件會被發佈到GitLab Pages上(網址構成同GitHub Pages類似)。GitLab Pages完全依賴該配置文件中的描述,不需要像GitHub那樣手動打開Pages。

對於部署TiddlyWiki來說,所用的.gitlab-ci.yml內容其實很短:

image: node:latest

pages:
  stage: deploy
  script:
    - npm install -g tiddlywiki
    - tiddlywiki wiki --build
    - mv wiki/output public
  artifacts:
    paths:
      - public
  only:
    - master

原理上看,搭建的方法就是通過tiddlywiki的工具,在CI時生成頁面,然後放到public目錄。由於tiddlywiki是nodejs程序,所以在.gitlab-ci.ymlimage部分聲明使用nodejs的docker鏡像。

注意,如前文所說,我這裏將tiddlywiki的文件結構(tiddlywiki.info所在地)放在之前所描述的wiki目錄中。該代碼片段中所有引用wiki目錄的地方均是出於此處。

至此,GitLab方面也已經設置完畢。之後只要正常將該倉庫提交,並push到GitLab上即可。