由于 Ghost 可以非常方便的将整个博客系统的数据导出为 JSON 格式, 所以还是非常的易于迁移的.
不过呢, 由于没有找到一个合适的迁移工具(GitHub 上的迁移工具基本上都已经不适用于新的 Ghost 版本了)
就自己造了个轮子.
迁移过程中, 花时间最多的就是找一个喜欢的主题了…最终选用了 Nlvi indigo Nlvi.
完.
]]>由于自动化生成路由是 Beego 默认也是最常用的使用模式, 那么我们就从这种模式谈谈 Beego 的硬伤(无法修复或者难以修复的问题).
Beego 使用了一种奇怪的方式去自动生成路由: 解析 routers/router.go
文件, 去拿到里边每个 controller
, 再去每个 controller
中解析每个方法的注释, 最终在 routers 目录下自动生成一个名为 commentsRouter_controllers.go
的文件.
这种做法乍一看没什么毛病, 然而这个路由生成行为只有一种方式可以触发: 使用 bee run
或者 go build && ./project_name
在开发模式下把项目运行起来. 而不是通过一个单独的解耦的不需要把项目运行起来的命令去自动生成, 并且在 prod 模式是不会自动生成文件的.
还有一个问题就是如果直接使用 go run
去运行项目的话, 那么就会生成一个诡异的路由文件名: commentsRouter_________________________Users_tuotoo_GoProj_src_git_spiritframe_com_tuotoo_tbee_controllers.go
. (WTF???)
这就导致了如果使用了 CI 的话, 就需要将这个 commentsRouter_controllers.go
文件上传到代码库中, 因为这种代码生成方式对 CI 是不友好的. 但是如果将这个文件上传到代码库中的话, 那么在多人协作时, 由于其诡异的生成规则又会造成代码冲突的问题. 使得出现冲突时, 不得不将该文件删掉, 再重新生成, 再提交代码…
另一个问题就是 routers/router.go
的写法是固定的, 必须是如下这种格式:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15func init() {
ns := beego.NewNamespace("/v1",
beego.NSNamespace("/object",
beego.NSInclude(
&controllers.ObjectController{},
),
),
beego.NSNamespace("/user",
beego.NSInclude(
&controllers.UserController{},
),
),
)
beego.AddNamespace(ns)
}
就算稍微改一下:1
2
3
4
5
6
7
8
9
10
11
12
13
14func init() {
beego.AddNamespace(beego.NewNamespace("/v1",
beego.NSNamespace("/object",
beego.NSInclude(
&controllers.ObjectController{},
),
),
beego.NSNamespace("/user",
beego.NSInclude(
&controllers.UserController{},
),
),
))
}
虽然是一个意思的代码, 但是修改后的代码会导致 swagger 文档生成失败…(WTF???)
假如我们有一个 object 库, 其中有一个写好的 ObjectController, 当我们想把这个 controller 导入项目中:
1 | func init() { |
乍一看, 没什么毛病. 然而在应用启动时, Beego 会无法生成 commentsRouter_controllers.go
且陷入死循环(从 CPU 占用上看是这样的).
解决办法是在 object 库中新建一个 routers/router.go
, 编写以下内容:
1 | func init() { |
并将该 routers
模块在项目中初始化…且项目中的 object 路由必须保持原样, 不能删掉. 也就是说, 我们必须在两个库中定义两个一毛一样的 namespace.
( WTF???无法理解这种莫名其妙的设计… )
这样的话, 在项目运行时 Beego 会在 object 项目的 routers 目录下自动生成一个 commentsRouter____object_controllers.go
文件, 这就更莫名其妙了…往上层库添加自动生成的代码…
这就更别说外部的 controller 需要传入额外的参数了, 毫无疑问也是不支持的…
关于 swagger 生成, 一个好消息是, Beego 提供了一个解耦的命令来执行这个动作: bee generate docs
, 这使得它不那么糟糕.
不支持其他库的 type 解析. 假如我们用了 sql.NullString
等类型的话, 那么会导致 swagger 生成失败. 不过, 这个问题还算不大, 可以通过以下方式进行修复:
将这块代码修改为1
2
3
4if !ok {
beeLogger.Log.Warnf("Unknown type without TypeSec: %v\n", d)
return
}
另一个问题就是 Beego 的 swagger 不支持 vendor, 即使你将依赖放在了 vendor 下, Beego 仍然会去 GOPATH 寻找源码来进行解析.
两个 namespace 引用同一个 controller, 如:
1 | func init() { |
在这种情况下 Beego 无法正确的进行 API 分组:
Swagger 中一米长的报错:
每次打开 Swagger 做的第一件事就是把错误信息折叠起来, 否则没法看…
由于 Beego 从设计上就没有兼容官方库 net/http
, 以及从注释去解析路由等原因导致了 Beego 项目中的 controller
无法测试的问题. 虽然可以将数据逻辑放在 models
中, 对 models
编写测试, 但业务逻辑的无法测试导致项目中有 50% 的逻辑是无法测试的, 无法测试路由/传参/返回值…关于这个问题作者在2014年表过态, 后来就没消息了…
无法通过编写测试来保证代码的质量, 这大概就是 Beego 最大的缺陷了.
不过不可否认的是, Beego 是一款非常成功的 web 框架, 极低的入门成本, 快速的开发速度以及成功的宣传, 间接为 Go 吸引来了非常多的爱好者.
下面是 Biu 的软文, 不想看的可以关闭 tab 了 :-)
由于 Beego 存在以上缺陷, 于是我开始寻找另外一款好用的 go web 框架. 它必须: 稳定的接口, 支持自动生成 swagger 文档, 简单易用的路由, 可以方便的进行定制. 然而, 多数流行的 web 框架都不支持自动生成 swagger 文档(前后端对接神器/懒人必备).
然后选择了 go-restful 作为基础框架, 并对其进行定制, 最终产生了 Biu 这个项目.
go-restful 是容器集群管理系统中的王者 kubernetes 的 API Server 所使用的 Web 框架, 其稳定性以及后续的维护都是有保障的, 堪称工业级别的框架了. 代码文档也是非常之详尽, 框架的架构方案和所提供的接口扩展性非常之强, 在路由算法上采用了成熟的JSR311算法, 最重要的是框架作者对于 Issue 的回复也很快.
一个基于 Biu 的项目可参考 biu-example.
Beego 一个 namespace 对应一个 controller 的做法是个非常棒的工程实践, Biu 的 router 保留了个设定.1
2
3
4
5
6
7biu.AddServices("/v1", nil,
biu.NS{
NameSpace: "foo",
Controller: Foo{},
Desc: "Foo Controller",
},
)
唯一的不同之处就是: Biu 不需要生成一个新的路由, 所编写的路由文件就是真实的路由.
在 Biu 中, 一个 controller 也称为一个 service, 可通过 biu.AddServices
添加到根路由中, 每个 controller 只需实现 func (ctl) WebService(ws biu.WS)
方法, 并在该方法内定义该 service 下每个 handler 的配置即可, 如:
1 | // Foo controller |
也就是将 Beego 在每个 handler 前面的注释改成放到 func (ctl) WebService(ws biu.WS)
里面, 并且通过显式的方法来进行传入, 而非约定第一个值为 xxx 第二个值为 xxx …
Biu 中还对自定义 JSON 错误码进行了支持, 并且设置的错误码是会出现在 swagger 中的.
紧接着在 handler 这一层:
1 | func (ctl Foo) getBar(ctx biu.Ctx) { |
Biu 内置了 errc 的支持, 将1
2
3
4
5
6if err != nil {
log(err)
...
ctx.ResponseJSON({Code: 100, Msg: Errors[100]})
return
}
缩减为一句 ctx.Must(err, 100)
完事, msg 会自动使用 WebService 中定义的 Error[100]
对应的字符串.
biu.Ctx
实际上是一个:1
2
3
4
5
6type Ctx struct {
*restful.Request
*restful.Response
*restful.FilterChain
ErrCatcher errc.Catcher
}
所以在 go-restful 的 handler 中的方法全都可以直接在 biu 的 handler 中使用.
既然吐槽了 Beego 对 Controller 的测试问题, 那么 Biu 是怎么解决这个问题的呢?
实际上, go-restful 的 container 就已经实现了 http.Handler
接口, 所以可以直接使用 httptest.NewServer
对其进行测试. Biu 中也内置了 biu.NewTestServer()
方法, 可以方便的返回一个 test server. 接下来就只需简单的对 test server 发起请求, 验证请求结果就可以了.
一直都是用着 Genymotion 的我
今天突然想看看新版 AVD 变成什么样了 (=゚ω゚)=
愉快的打开 AVD ,新建虚拟设备,无脑的下一步,下一步,完成,一切顺利( flag),但是当我点击运行的时候,duang~!
1 | libGL error: unable to load driver: r600_dri.so |
这错误看上去还有点 眼熟
翻回去看了下前面设置的过程,果不其然:
好消息:AVD 支持硬件渲染啦!
坏消息:Linux 上的各种残念的显卡问题……
好吧,那么再 Google 一下,找到了这个 issue
进入到这个目录下(ANDROID_HOME
为 Android SDK
目录):cd $ANDROID_HOME/android-sdk-linux_x86/tools/lib64/libstdc++
先备份就的 libstdc++.so.6
文件:mv libstdc++.so.6 libstdc++.so.6.bak
然后建立一个软链接:ln -s /usr/lib64/libstdc++.so.6 $ANDROID_HOME/android-sdk-linux_x86/tools/lib64/libstdc++
再然后就可以顺利运行啦
]]>Interesting! 不过他是用 Ruby 写的 (=゚ω゚)=
所以我就用 Go 写了一个功能一毛一样的
1 | package main |
一样也是每秒生产一句古风句子
然而功力不足,代码比起 Ruby 版长好多 ╮(╯▽╰)╭
Python版:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35from time import sleep
from random import choice
from random import randint
two_chars_words = "朱砂 天下 杀伐 人家 韶华 风华 繁华 血染 墨染 白衣 \
素衣 嫁衣 倾城 孤城 空城 旧城 旧人 伊人 心疼 春风 古琴 无情 迷离 奈何 \
断弦 焚尽 散乱 陌路 乱世 笑靥 浅笑 明眸 轻叹 烟火 一生 三生 浮生 桃花 \
梨花 落花 烟花 离殇 情殇 爱殇 剑殇 灼伤 仓皇 匆忙 陌上 清商 焚香 墨香 \
微凉 断肠 痴狂 凄凉 黄梁 未央 成双 无恙 虚妄 凝霜 洛阳 长安 江南 忘川 \
千年 纸伞 烟雨 回眸 公子 红尘 红颜 红衣 红豆 红线 青丝 青史 青冢 白发 \
白首 白骨 黄土 黄泉 碧落 紫陌".split(" ")
four_chars_words = "情深缘浅 情深不寿 莫失莫忘 阴阳相隔 如花美眷 \
似水流年 眉目如画 曲终人散 繁华落尽 不诉离殇 一世长安".split(" ")
sentence_model = "xx,xx,xx了xx。 xxxx,xxxx,不过是一场xxxx。 \
你说xxxx,我说xxxx,最后不过xxxx。 xx,xx,许我一场xxxx。 \
一x一x一xx,半x半x半xx。 你说xxxxxxxx,后来xxxxxxxx。 \
xxxx,xxxx,终不敌xxxx。".split(" ")
def get_sentence():
model = choice(sentence_model)
while "xxxx" in model:
model = model.replace("xxxx", choice(four_chars_words), 1)
while "xx" in model:
model = model.replace("xx", choice(two_chars_words), 1)
while "x" in model:
model = model.replace(
"x", choice(two_chars_words)[randint(0, 1)])
print(model)
while True:
get_sentence()
sleep(1)
一看 README ,我勒个乖乖:
Make your app 4x faster.
真真是刁刁的样子啊。
先看看用法:1
2
3
4
5
6
7resp, err := http.Get(blah)
if err != nil {
return err
}
//defer resp.Body.Close()
defer drainclose.Close(resp.Body)
json.NewDecoder(resp.Body).Decode(&data)
只要把以前的 defer resp.Body.Close()
替换掉就好了,真是简单易用。
那么黑魔法是怎么完成的呢? mattn 大大只丢下一句话:
好吧,那就看看这个 PR ,大概就是像下面这样1
2
3
4
5
6//defer resp.Body.Close()
defer func() {
// Drain and close the body to let the Transport reuse the connection
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
}()
Magic!
代码的意思就是复用 TCP/TLS 连接来达到更高更快更强的目的啦。
当然啦,这样做的坏处也是有的,就是要实现连接的复用的话,那么就必须把前一个连接的内容全部读完才能复用(就算你没有读取resp.Body
),所以如果前一个连接的 Body 比较大而又不想读取里面的内容的话,就不要用黑魔法啦。
附上大神的实现代码:1
2
3
4
5
6
7
8
9func Close(body io.ReadCloser) error {
if body == nil {
return nil
}
if _, err := io.Copy(ioutil.Discard, body); err != nil {
return err
}
return body.Close()
}
就是在上面那个PR的基础上加入了判空和错误处理。
]]>啊好难用的博客,我并不会用。
上周一把好吃群解散了,后来 0321 晚上到 0322 凌晨一直折腾新的群组。
其实只不过是好吃的群组大合集而已 www
Telegram: 一定要吃好吃的 ヽ(・∀・)ノ
成立于 20160322 哈哈哈。
Slack: haochideShan8 (@SusuwANjr)
周四开始玩 Slack 的呢,一开始觉得不好用,周五开始觉得挺简单啊而且好好玩。后来就有了 Slack 分部。
Hangouts: Email Address Required.
然后周六当然时不时有在群里说起 Slack 呀,也不断有人加入,虽然很少,但又有什么关系嘛。于是有人说 Hangouts 分部开起来,于是终于我也玩了一次 Hangouts 哈哈哈,好喜欢贴图,太开心惹。
QQ 群: 551623838
没错我们居然有 QQ 群了。我就是开着 QQ 于是我想,整个群吧。
对了我们还有微信群。想来想去,只有一个理由能说服自己,让大家在每个地方都有落脚点。
哈哈哈哈哈 20160328 更新
Google+ 社群: 一定要吃好吃的 ヽ(・∀・)ノ
今天中午群里有在问 G+ 我就截图聊天记录给奇美拉,七妹说我们建 G+ 吧于是就整起来了咯。
呵呵嗒。哈哈哈。
啊真的好难用啊,然后我本来的标题是<偶尔来写个日记吧>哈哈哈,可我想着也就是记录一下各种时间呀,而主要的也就是好吃群了,于是就改了。
本来吧我想着我要各种颜色啊,可是我不会嘛,我就想,那我可以在想要什么颜色的地方,比如这里,打上括号(啊这个我要蓝色的),这样子。
然后他看到应该会帮我弄的 (=゚ω゚)=
不过嘛万一不可以弄呢,啊我怎么知道啦!而且最后写下来其实也没觉得想要颜色吧哈哈哈哈哈哈,话说字可以小点吗,总觉得太大了。貌似也不能吗,啊呀不造。
Ps: 我就是不想直接问他嘛 … hhh 排版丑爆了,不管惹。感谢 Jqs7 www
]]>原作者:MATHIAS BEKE
译文作者:Jqs7
审阅:@SusuwANjr
在这篇文章中,我会带着你用 Caddy 搭建一个安全的 ownCloud 个人云服务,此教程中所使用的平台为 Ubuntu 14 。
更新:Caddy 当前的正式版还没有支持 WebDav ,所以桌面和手机客户端就没法用了 (*゚ー゚) 不过好消息是开发分支是有这个特性的,所以你想爽一把就只能等下个版本或者自己编译了。
给小白们的 ownCloud 介绍(来自维基百科):
ownCloud是一个自由且开源的个人云存储解决方案,包括兩個部分:服务器和客戶端。ownCloud 最早由KDE开发者Frank Karlitschek于2010年一月创建,目标是成为商业云服务提供商的替代。与商业云存储服务不同,ownCloud可以自由获取无需付费,但相應地,使用者必須自行架設 ownCloud的服务器,這需要一點技術。不過也是有商业云存储服务提供商使用 ownCloud 作為服务器,例如奧地利商的 OwnCube。
ownCloud在客户端可通过网页界面,或者安装专用的客户端软件来使用。网页界面当然就是任何能开网页的平台都支持,而客户端软件也支持相当多平台,Windows、Linux、iOS、Android皆有。
除了云存储之外,ownCloud也可用于同步日历、电子邮件联系人、网页浏览器的书签;此外还有多人在线文件同步协作的功能(类似google documents或Duddle等等)。
因为 ownCloud 需要使用到数据库,所以我们先安装一下 MariaDB 。使用以下命令来安装 MariaDB 服务端以及客户端:
$ sudo apt-get install mariadb-server
完成安装后,如果想要提升安装的安全性的话,可以运行以下命令:
$ sudo /usr/bin/mysql_secure_installation
现在运行以下命令并输入密码,就可以使用 root 账号登陆 MySQL 命令行客户端了:
$ mysql -u root -p
然后给 ownCloud 创建一个新数据库:
MariaDB [(none)]> create database owncloud;
新增一名用户:
MariaDB [(none)]> grant usage on *.* to owncloud@localhost identified by 'somepassword';
给新用户赋予方才建立的数据库的权限:
MariaDB [(none)]> grant all privileges on owncloud.* to owncloud@localhost;
现在你就有了一个用户名为 owncloud
密码为 somepassword
,拥有 owncloud
数据库访问权限的的新用户了。
PHP 7 都已经发布了好几个月了,所以我们当然要用新版啦!
首先添加 PHP 7 的 repository :
$ sudo add-apt-repository ppa:ondrej/php$ sudo apt-get update
然后安装 PHP:
$ sudo apt-get install php7.0-fpm
只是安装 PHP 还不够,还需要安装一些 PHP 扩展。
ownCloud 所需要的详细扩展列表可以在这里找到。
一些 PHP 默认没安装,但是必须推荐你装的包:
一次性安装命令:
$ sudo apt-get install php7.0-mysql php7.0-gd php7.0-curl php7.0-intl php7.0-mcrypt
如果你需要预览视频跟文档的话,那还得安装这两个包(非 PHP):
为了更好的兼容 Caddy ,最好把 PHP-FPM 监听从 Unix socket 改成 TCP socket:
在 /etc/php/7.0/fpm/pool.d/www.conf
文件中,把listen = /run/php/php7.0-fpm.socket
改为 listen = 127.0.0.1:9000
安装完所有扩展后,别忘了重启 PHP-FPM:sudo service php7.0-fpm restart
我跟 mholt 和 dprandzioch 一起完成了下面这个 Caddyfile。 这个配置包含了 ownCloud 所需的所有配置且完全支持桌面与手机客户端。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39my-owncloud-site.com {
root owncloud
log owncloud/access.log
errors owncloud/access.log
fastcgi / 127.0.0.1:9000 php {
env PATH /bin
}
rewrite {
r ^/index.php/.*$
to /index.php?{query}
}
# client support (e.g. os x calendar / contacts)
redir /.well-known/carddav /remote.php/carddav 301
redir /.well-known/caldav /remote.php/caldav 301
# remove trailing / as it causes errors with php-fpm
rewrite {
r ^/remote.php/(webdav|caldav|carddav)(\/?)$
to /remote.php/{1}
}
rewrite {
r ^/remote.php/(webdav|caldav|carddav)/(.+)(\/?)$
to /remote.php/{1}/{2}
}
# .htacces / data / config / ... shouldn't be accessible from outside
rewrite {
r ^/(?:\.htaccess|data|config|db_structure\.xml|README)
status 403
}
header / Strict-Transport-Security "15768000"
}
因为 Caddy 内置了 Let’s Encrypt 支持,所以我们的 ownCloud 服务端会自动配置好 HTTPS。访问和错误的 log 会写入到 ownCloud 目录里面,data 目录(以及其他一些特殊文件)也杜绝了被外部访问的可能。
如果你想测试一下 Caddyfile / PHP 的安装,可以在 owncloud
目录下创建一个 phpinfo.php
文件,在上面加上一行:
<?php phpinfo(); ?>
然后用浏览器访问 https://my-owncloud-site.com/phpinfo.php
,看看是不是显示默认的 PHP 信息页面。(当然啦, Caddy 得使用 Caddyfile 配置)
如果一切正常的话,别忘记把这个文件删掉,以免被别人看到你的 PHP 安装配置。
现在终于要开始 ownCloud 的安装了。
下载最新的 ownCloud 版本(目前是 9.0.0
):
$ wget https://download.owncloud.org/community/owncloud-9.0.0.zip
把这个文件解压到 owncloud
目录:
$ unzip owncloud-9.0.0.zip
使用浏览器访问 https://my-owncloud-site.com
。如果配置没错的话,你就会在浏览器成功的看到 ownCloud 的配置页面啦。
另外,你还得创建一个 data
目录,并赋予 ownCloud 读写权限(比如 www-data
用户)。1
2$ mkdir owncloud/data
$ sudo chown -R www-data owncloud
Caddy 的快速开发使得它成为了简单安全地部署 ownCloud 的另一个选择。
这篇文章还发布在了Caddy的官方博客上:Running ownCloud with Caddy
]]>原作者:Ahmed Rizwan
译文作者:Jqs7
审阅:@SusuwANjr
在这篇文章里面我会使用一些库(就我平时用那些),比如使用 Kotlin 和 Rx 来写 Retrofit 和 Realm 。
如果你是个 Retrofit 新手……建议你阅读一下这篇文章——保证亮瞎你狗眼让你眼前一亮!如果你不知道 Rx & Kotlin —— 看这里!
那我们开始吧:Kotlin + Rx + Retrofit + Realm
如果你像我一样,那一定很讨厌一大片的冗余的代码 咳咳 Java 6 。自我开始从事安卓开发以来,经常遇到一些“什么鬼?!”的问题。
在语言上我觉得 Kotlin 大法就是好!像 Retrofit ,Realm 以及 RxAndroid 这些库真心省了不少代码。
蜀黍给你讲个可怕的故事:有次我负责一个项目,里面有 12 个 POJO 类,还有 12 个数据库表类(ORM),还有 12 个数据库模型映射类。如果你数学学得好的话,你就会发现……特喵的有 36 个类!还不只是这样,如果要改一个类,那就意味着 3 个类全都得改。
如果我们不用 ORM 的话,我们还得去写模型到数据库的映射类,真特么得吓尿我!
所以解决办法是啥?对我来说就是 Realm + Retrofit 啦 (^o^)ノ 我还用了 RxAndroid ,因为有了 Rx 生活更美好,当然还有 Kotlin ,因为……
Kotlin 大法好!Kotlin 千秋万代,一统江湖!
把这些东西结合起来,那 36 个类所做的事情只要 12 个类就能完成了 (本来就应该这样嘛 (ゝ∀・))。不光这样,代码也会因为 Kotlin 而变得更加简洁,表达性更为良好!
一个常见的应用场景:从 API 获取数据,把数据存储到数据库并显示出来。
完成品就像下图这样,只是一个很简单的例子……希望可以覆盖到基本的姿势点!
下面就讲讲该怎么把这个 app 给撸出来……
创建完项目我们做的第一件事就是启用 Kotlin ——首先要在 Android Studio 里面装好 Kotlin 插件。
我创建了一个只有一个 MainActivity 的空项目——直接打开 build.gradle 文件,然后打开 action 列表(快捷键 ctrl+shift+a)
吼的!我们现在可以用 Kotlin 来写代码了!
1 | //Realm |
万事俱备!开始写代码啦!
我们使用的 API 地址是:
https://api.github.com/users/ahmedrizwan
服务端返回的内容如下:
1 | { |
在这个例子里面,只要把 _id, name, avatar_url 和 public_repos_ 从返回信息里面提取出来就够了。所以 model 类就像下面这样(同时也是个 realm 类):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
open class Github : RealmObject() {
open var id: Int = 0
open var avatarUrl: String? = null
open var name: String? = null
open var publicRepos: Int? = null
}
小提示:如果你需要JSON返回的所以属性的话,我建议你使用这个网站来生成一个 POJO 类,再像上面说的那样把 Java 代码转成 Kotlin 代码。
我们就用 RealmClass 注解说起吧,在 Kotlin 中需要为 Realm model 类加上这个注解,Realm 才能生成 Realm 代理类,PrimaryKey 也是一个 Realm 注解,表示主键属性(废话),剩下的就是 Gson 注解了……
open关键字正好跟 Java 里面的 final 相反。默认情况下,Kotlin 的类就是 final 的,如果你如果要一个类可以被继承,就必须把它声明为 open。这对于属性也是一样的,比如上面的 model 类里面,name 这个属性,它拥有一个 getter 和 setter,为了使它们可以被重写(因为 Realm 需要),我们就得在属性的前面加上 open 关键字。
因为我们的端点地址是:
https://api.github.com/users/[some_user]
所以接口写出来就是下面这样:
1 | interface GithubService { |
上面我们直接返回了一个 Observable 的 Github,因为 Retrofit 集成了 Rx,简直就是碉堡了!
1 | val gson = GsonBuilder().setExclusionStrategies(object : ExclusionStrategy { |
因为用了 Realm ,所以我们得重新声明一个 Gson 实例,添加排除策略(exclusion strategy)来跳过Realm生成的属性,不然 Gson 对于这个 model 就没什么卯月了。
在下面就是一个加入 RxJavaCallAdapter 工厂类启用 Rx 集成的 Retrofit 实例,我们还加了一个使用了上面创建的 Gson 实例的 Gson 解析器。
1 | githubService.getGithubUser("ahmedrizwan") |
现在我们所做的就是:获取 observable 对象,订阅之,并获取 Github 对象。只有一获取到 Github 对象,就可以很简单的把用户保存到 Realm 数据库里面了。
缓存也是可以有的 (=゚ω゚)=,我们可以一开始就可以获取 Realm 数据库里面的数据(如果存在的话),就像下面这样:
1 | val realm = Realm.getDefaultInstance() |
到这里就已经差不多完成了,接下来就差运行…
Happy coding!
]]><your blog URL>/ghost/
链接就可以登录系统后台管理你的博客内容了。当你进入后台,你就能看到左侧文章列表处列出的这篇文章,右侧就是这篇文章的预览效果。点击预览栏右上角的铅笔图标就能进入内容编辑页面。 Ghost 使用 Markdown 语法书写内容。简单来说,Markdown 就是一种简化的书写格式!
用 Markdown 语法写作是很容易的。在编辑界面的左侧就是你写作的地方。在你认为需要的时候,可以使用以下这些语法来格式化你的内容。例如下面这个无序列表:
还可以是有序列表:
如果要链接其它页面,可以直接把页面的 URL 粘贴过来,例如 http://www.ghostchina.com - 会被自动识别为链接。但是,如果你想自定义链接文本,可以像这样: Ghost 中文网。很简单吧!
插入图片也没问题!前提是你事先知道图片的 URL,然后像下面这样:
如果图片在本地的硬盘里怎么办?也很简单!像下面这样书写就能为图片预留一个位置,然后你可以继续写作,回头再通过拖拽的方式把图片上传到服务器上。
![一张图片]
有些时候我们需要引用别人说的话,可以这样:
Wisdomous - it’s definitely a word.
或许你是个码农,需要贴一些代码到文章里,可以通过两个引号(Tab 键上面的那个键)加入行内代码 <code>
。如果需要加入大段的代码,可以在代码前加 4 个空格缩进,这就是 Markdown 的语法。
.awesome-thing { display: block; width: 100%;}
在任一新行输入 3 个或更多的短横线(减号)就是一条分隔线了。
Markdown 还有一个特别用法,就是在你需要的时候可以直接书写 HTML 代码。
只要掌握了上面的这些介绍,你就已经入门了!继续写作吧!
]]>tmux
升级至2.1
之后,最显著的变化就是mouse-resize-pane
mouse-select-pane
mouse-select-window
mode-mouse
这4个选项被移除掉了,换成了mouse
,所以会造成一些使用了这些选项的配置文件错误,修正方法是把这几行1 | set -g mouse-resize-pane on |
换成1
set -g mouse
就好了,具体的 CHANGES 见这里
]]>1 | call vundle#begin() |
###vundle
必装插件,用来管理所有的vim插件,相当于sublime
中的package control
项目地址:5iPQf
只要在命令行简单的执行一条命令就能完成安装:1
git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim
###fcitx.vim
fcitx输入法辅助插件,在 vim 退出 insert 模式的时候自动将 fcitx 输入法切换为英文,再次进入 insert 模式再切回来,使用方式很简单:install it and forget it
###YouCompleteMe
自动补全神器,谁用谁知道!
###vim-multiple-cursors
sublime 多光标编辑的vim实现,安装后只要记住一个 ctrl+n 键就OK了,选中多行,ctrl+n,或者直接输入 ctrl+n 选中相同的单词,之后就能用i进入插入模式编辑了,不过用起来有些卡……
其实vim下需要用到这个插件的情况也不多…不过插件嘛……反正不嫌多就装着了…
###vimcdoc
一些插件还有 vim 功能的中文文档翻译
###nerdtree
相当于sublime的sidebar,偶尔打开看看目录结构还是不错的,我的配置把开关设置成了F2键
###kien/ctrlp.vim
当前目录模糊查找插件,与sublime的 ctrlp 功能相同
###vim-go
vim 的 go 语言插件,功能叼炸天!完全把 vim 变身成了一个IDE,其实很大程度上也是得益于 go 的工具链非常的完善
###nerdcommenter
注释切换插件,我一般只用一个功能:<leader>c<space>
,切换注释
###dracula
主题,详细样式见:obnWg
###i3-vim-syntax
i3wm 配置文件的语法高亮
###vim-auto-save
自动保存插件,像Intellij
系列 IDE 一样边编辑边保存
###vim-instant-markdown
markdown 实时预览插件,需要使用node.js
安装服务端,详细介绍:gnhJk
###vim-fugitive
vim 的git
插件,神器在手,再也不需要去命令行敲 git 命令啦!详细使用方式:2ws0w
###vim-airline
状态栏增强及美化插件,需要安装 powerline 字体。
###auto-pairs
符号补全插件,自动补全括号,引号等符号。
###vim-surround
修改语句两侧符号或者 tag 的插件,编辑 HTML 和 XML 的时候简直不要太帅!使用介绍:dKTLs
###vim-bufferline
可在 airline 上显示当前有哪些buffer
###tabular
对齐狂魔的福音!使用方式:TsKCN
###goyo.vim
无干扰编辑插件,去除掉七七八八的界面,专心于写作神马神马的……使用方式就是在命令模式输入:Goyo
###vim-markdown
markdown 编辑增强插件(其实我没有用到…)
###tagbar
显示一个代码导航窗口,看比较长的代码的时候非常有用
###vim-easymotion
实现在当前页任意位置跳转
###vim-ctrlspace
实现众多 tab 和 buffer 的快速查看跳转,搭配ctrlp
使用最佳!
我的Vim摆拍(ゝ∀・):
1 | pacman -S zsh git vim |
添加完成后,编辑sudoers文件,使得添加的用户可以使用sudo:
运行命令visudo
,然后把# %wheel ALL=(ALL) ALL
前面的#
删掉
搞定之后logout
,用新用户登入系统
安装显示服务:sudo pacman -S xorg xorg-xinit xterm
,然后无脑回车
安装登陆管理器:1
2sudo pacman -S gdm
sudo systemctl enable gdm
安装桌面环境:sudo pacman -S gnome wqy-microhei
装完重启后应该就能进入GNOME桌面环境了,现在给系统撸上中文:
编辑/etc/locale.gen
,找到zh_CN.UTF-8
这一行,将之前面的#
去掉,保存退出后运行locale-gen
,即:1
2sudo vim /etc/locale.gen
sudo locale-gen
还要在~/.xprofile
文件中添加一行环境变量:export LANG=zh_CN.UTF-8
(文件不存在就创建之)
这样系统重启后就应该是亲切的中文了
安装中文输入法:sudo pacman -S fcitx fcitx-configtool fcitx-im
在~/.xprofile
添加:1
2
3export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS="@im=fcitx"
安装oh my zsh
:1
curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh
安装yaourt
,编辑/etc/pacman.conf
,在文件末尾添加:1
2
3[archlinuxfr]
SigLevel = Optional TrustAll
Server = http://repo.archlinux.fr/$arch
然后运行sudo pacman -Sy yaourt
完成安装
有了yaourt就可以用它来安装Chrome
了:yaourt -S google-chrome
(yaourt无需用sudo)
另外,其他的一些软件也可以很方便的通过pacman
进行安装,比如Go编程环境:sudo pacman -Sy liteide
(Go会作为依赖一起安装上去)
对于UEFI固件,最好使用UEFI+GPT
的分区方案,因为有的UEFI固件不支持UEFI-MBR启动。(o ;′Д`)ノ゛
即分区表采用GPT,再分一个512M的分区类型为EFI系统的分区,并将之挂载在/boot
下:
另外,EFI分区必须格式化成FAT32
,格式化命令为:
mkfs.fat -F32 /dev/xxx
再之后正常安装……直到安装bootloader
的时候,对于UEFI,安装命令为:
pacman -S grub efibootmgr
grub-install –target=x86_64-efi –efi-directory=boot –bootloader-id=arch_grub –recheck
grub-mkconfig -o /boot/grub/grub.cfg
相对于BIOS要多敲了几个字母的命令,搞定后exit
退出,reboot
重启就完成了。
]]>撒花!( ̄▽ ̄)o∠※PAN!=.::’☆.::’★’:*
安装64位的话,就选第一个,32位的话就第二个
回车之后经过一些哗啦啦华丽丽的输出之后,就来到了下面这个画面:
Arch Linux的装逼安装之路也是在这个黑乎乎的窗口开始(ゝ∀・)
先随便ping一个网址,检查下网络的连通性。
如果是用网线连接的话,应该是直接就可以上网的,因为安装程序会自动启用dhcpcd
服务来自动获得IP地址。
如果是通过无线网络连接的话,就需要先运行wifi-menu
命令来连接wifi
连接好网络之后就开始分区,在输入cfdisk
,然后选择dos
,就看到了如下界面:
new
就是新建分区,quit
退出,help
帮助,write
写入,先new
一个分区,下面会让你选择分区大小,删除之,输入200M(作为 /boot
分区)
这里选择primary
,然后就能看到下面这个样子
继续翻译……ヾ(*′▽`*)ノ彡☆bootable
设置为可启动,delete
删除分区,type
分区类型
将刚刚的那个分区设置为bootable
,然后按一下↓
键,继续new
分区,这里分swap
分区,大小视内存而定,一般内存多大就分多大就好啦~
分好后还要继续分一个/
分区,最好还要再分一个/home
,具体大小看自己需要啦,不过最简单粗暴的就是只分一个/
啦ヾ( ̄▽ ̄)
全部分好之后,大概看起来就像下面这个样子:
然后write
,输入yes
,再接着quit
,现在输入lsblk
来看一下现在硬盘的结构:
可以看到,现在我们要安装的硬盘——sda
已经被我们分成了三块了
搞定完分区之后,就要对我们刚刚分的分区进行格式化,首先是搞定swap分区sda2
,因为是swap
分区,所以就用mkswap
然后再swapon
启用就好了
接着是sda1
和sda3
,格式化成ext4
格式,命令如下:
1 | mkfs.ext4 /dev/sda1 |
格式化完成之后,还要把分区挂载到相应的挂载点上去,因为sda2
刚刚已经启用了,所以就不用管他,而sda3
因为是根节点,所以要先挂载,再创建一个文件夹用来挂载/boot
,就像下面的这样:
1 | mount /dev/sda3 /mnt |
挂载完成之后就终于可以开始安装系统啦ε=ε=ε=(~ ̄▽ ̄)~,不过在此之前最好先编辑一下镜像列表文件,把比较快的地址放在前面,这样下载起来会比较快一点,不过不会用vim也不会用nano的话,不编辑也没关系啦,就是下载起来慢一点……编辑命令是vim /etc/pacman.d/mirrorlist
搞定完镜像之后(或者没搞定…),就可以开始安装系统啦~输入下面这条命令:
1 | pacstrap -i /mnt base base-devel |
遇到什么提示(default)之类的,就直接回车就好了
经过一段时间的漫长等待之后……系统终于装好啦 X﹏X
But!!!先别急着重启!!!还有一些很重要的配置要做!
生成fstab,这是为了启动系统的时候能自动挂载分区
1 | genfstab -U -p /mnt >> /mnt/etc/fstab |
chroot到刚刚安装好的系统,进行进一步的配置:
1 | arch-chroot /mnt /bin/bash |
设置时区:
1 | ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime |
设置硬件时间:
1 | hwclock --systohc --utc |
设置主机名:
1 | echo doufu > /etc/hostname |
1 | #<ip-address><hostname.domain.org><hostname> |
设置网络:
有线:1
systemctl enable dhcpcd@xxx.service
上面的xxx通过ip link
命令查看,通常e开头的哪那一个就是
无线:
1 | pacman -S iw wpa_supplicant dialog wpa_actiond |
xxx同样是通过ip link
查看,通常为w开头
设置root密码:
1 | passwd |
安装bootloader
(重要!!!)
1 | pacman -S grub |
配置至此,才算是真正的安装完毕了^_^)y
接着exit
退出chroot环境,再reboot
重启即可
]]>撒花!( ̄▽ ̄)o∠※PAN!=.::’☆.::’★’:*
这个项目收集了各种免费的编程中文书籍以及学习笔记
网页版见这里:siberiawolf.com/free_programming
另一个项目是:ryanzz/LFS-systemd-zh_CN(正在进行中)
对LFS有兴趣或者想知道一个Linux系统是怎么构建出来的童鞋可以关注下哟(ゝ∀・)
]]>只要编辑/etc/X11/xorg.conf.d/50-joystick
文件(没有就创建一个),把手柄控制鼠标功能禁用掉就好了:
1 | Section "InputClass" |
编辑完成之后重启X即可(σ゚∀゚)σ
]]>将默认终端修改为lilyterm1
bindsym $mod+Return exec lilyterm
jkl;
修改为hjkl
1
2
3
4
5
6
7
8
9bindsym $mod+h focus left
bindsym $mod+j focus down
bindsym $mod+k focus up
bindsym $mod+l focus right
bindsym $mod+Shift+h move left
bindsym $mod+Shift+j move down
bindsym $mod+Shift+k move up
bindsym $mod+Shift+l move right
修改工作区名1
2
3
4
5bindsym $mod+1 workspace 1 Terminal&Editor
bindsym $mod+2 workspace 2 Browser
bindsym $mod+Shift+1 move container to workspace 1 Terminal&Editor
bindsym $mod+Shift+2 move container to workspace 2 Browser
添加锁屏、关机
等功能,按$mod+Shift+e
呼出1
2
3
4
5
6
7
8
9
10
11
12
13
14set $mode_system System (l) lock, (e) logout, (s) suspend, (h) hibernate, (r) reboot, (Shift+s) shutdown
mode "$mode_system" {
bindsym l exec --no-startup-id ~/bin/i3exit lock, mode "default"
bindsym e exec --no-startup-id ~/bin/i3exit logout, mode "default"
bindsym s exec --no-startup-id ~/bin/i3exit suspend, mode "default"
bindsym h exec --no-startup-id ~/bin/i3exit hibernate, mode "default"
bindsym r exec --no-startup-id ~/bin/i3exit reboot, mode "default"
bindsym Shift+s exec --no-startup-id ~/bin/i3exit shutdown, mode "default"
# back to normal: Enter or Escape
bindsym Return mode "default"
bindsym Escape mode "default"
}
bindsym $mod+Shift+e mode "$mode_system"
关闭鼠标自动聚焦1
focus_follows_mouse no
绑定截图与文件管理器快捷键1
2bindsym $mod+Print exec xfce4-screenshooter
bindsym $mod+Shift+d exec thunar
关闭Chrome标题栏1
for_window [class="Google-chrome" title="Google Chrome"] border none
进入桌面后启动nitrogen(设置壁纸)和fcitx1
2exec --no-startup-id nitrogen --restore
exec --no-startup-id fcitx
ps:最近又被Google Plus上某黑叔叔 +dagnachew argaw 安利 bspwm …又要启程折腾了>°)))>彡
]]>上了Arch之后,为了装逼高效,自然也就走上了折腾窗口管理器的不归路\(·ω·`)
最终最顺手的窗口管理器还是i3,至于awesome,看到那一长串的rc.lua
……咳咳><
i3的安装方法很简单:1
sudo pacman -Sy i3
然后把i3-wm
i3lock
i3status
全部装上就好了(三个包加起来1M都不到)
为了保持系统的逼格简洁性,我没有使用登陆管理器,所以安装后只要在~/.xinitrc
添加
1 | exec i3 |
就完成了,为了在登陆后不麻烦的去敲startx
,我在.zprofile
中添加了一行:1
[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && exec startx
这样就可以在登陆后自动进入X环境啦(`ヮ´ )
进入i3后,经过一个简单的设置引导就可以开始使用了(此处强烈建议把$mod键修改为mod4)
i3默认按键映射(来自Archwiki):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22$mod + Enter 启动虚拟终端
$mod + A 焦点转义到父窗口上
$mod + S 堆叠布局
$mod + W 标签布局
$mod + E 默认布局
$mod + SpaceBar 焦点在平铺式/浮动式转换
$mod + D 启动 dmenu
$mod + H 水平分割窗口
$mod + V 垂直分割窗口
$mod + J 焦点往左窗口移
$mod + K 焦点往下窗口移
$mod + L 焦点往上窗口移
$mod + ; 焦点往右窗口移
$mod + Shift + Q 杀死当前窗口的进程
$mod + Shift + E 退出 i3
$mod + Shift + C 当场重新加载 i3config, 无需重启
$mod + Shift + R 重启 i3 (还重新加载了 i3config, 又没有退出过程)
$mod + Shift + J 窗口左移
$mod + Shift + K 窗口下移
$mod + Shift + L 窗口上移
$mod + Shift + : 窗口右移
$mod + Shift + SpaceBar 窗口在平铺式/浮动式转换
基本的使用就是这样啦ヾ(′▽`* )ノ~
在i3的世界中,用键盘就可以完成一切的窗口操作,鼠标神马的…扔掉就好了σ(⌒ー⌒)
如果要做其他一些自定义神马的就要编辑~/.i3/config
文件啦
##安利时间
##more
]]>