昨天在 Tmux 中使用 gpg 时遇到了一个问题,在别人的回答中看到了 login shell 和 non-login shell 的区别。在我的情况下终端中 bash 或者 zsh 都是能够使用 gpg 签名 git commit 的,但是在 tmux 下却报了错误。所以才有了这样一篇总结。
登录式 shell 是用户使用自己的 user ID 登录交互式 shell 的第一个进程。这里又要提及另外两个概念就是交互式 shell 和非交互式 shell,幸而这两个概念比较好理解。
说完这两个区别,回到主题:
~/.bashrc
for bash, /etc/zshrc
and ~/.zshrc
for zsh)有一种判断 login shell vs non-login shell 的非常快速的方法,使用命令 echo $0
-bash
中 -
表示当前是一个 login shellbash
表示不是 login shell至于为什么会有这么多的模式,是因为不同的模式读取的配置文件有所区别。
login shell 会读取不同的配置文件,比如 bash 会读取 /etc/profile
, ~/.profile
,~/.bash_profile
等配置文件。而 zsh 会是 /etc/zprofile
和 ~/.zprofile
而 non-login shell 只会读取 ~/.bashrc
配置。
Huginn 是一个 Ruby 实现的在线的自动化任务构建系统。它可以用来监控网页,监听时间,实现自定义行为。Huginn 的 Agents 可以创建或消费事件,并通过一个直接的 Graph 对事件进行传播。可以把它认为是一个开源版本的 IFTTT, Zapier。
Huginn 可以做:
使用 Docker 是最方便的了
docker pull huginn/huginn
如果不想自己安装 MySQL 之类的数据库,这个镜像中包含了一个打包的数据库,直接 run 就行
sudo docker run -it --name huginn -p 3000:3000 --rm huginn/huginn
注意命令中的 --rm
当终止命令后容器会被删除,如果不想一次性使用不要使用该参数。如果想要在后台运行使用 -d
参数。
或者使用 docker-compose
假设在本机以及启动了一个 MySQL 实例,不想使用捆绑到镜像中的数据库那么可以在启动时指定环境变量。不过首先需要设置数据库的连接设置和权限,设置 /etc/mysql/mysql.conf.d/mysqld.cnf
设置监听地址为 0.0.0.0
,然后使用 ifconfig
查看 docker0 的 IP 地址,一般为 172.17.0.1
,那么给该 IP 访问数据库的权限,界面或者命令:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'172.17.0.%' IDENTIFIED BY 'pass' WITH GRANT OPTION;
flush privileges;
然后使用 Docker
docker run --name huginn \
-p 3000:3000 \
-e MYSQL_PORT_3306_TCP_ADDR=172.17.0.1 \
-e HUGINN_DATABASE_NAME=huginn \
-e HUGINN_DATABASE_USERNAME=root \
-e HUGINN_DATABASE_PASSWORD=pass \
huginn/huginn
注意下面的 nginx-proxy network,我一直使用它来做自动反代,和自动生成 SSL 证书,
下面的例子中我使用了一个已经存在的 postgresql 容器,名字叫做 db
所以可以直接在数据库连接那边写 db,如果是新环境,那么需要自己创建一个 PostgreSQL 数据库容器。
version: "3"
services:
huginn:
image: huginn/huginn
container_name: huginn
restart: always
environment:
HUGINN_DATABASE_ADAPTER: postgresql
POSTGRES_PORT_5432_TCP_ADDR: db
POSTGRES_PORT_5432_TCP_PORT: 5432
HUGINN_DATABASE_NAME: huginn
HUGINN_DATABASE_USERNAME: YOUR_NAME
HUGINN_DATABASE_PASSWORD: YOUR_PASSWORD
VIRTUAL_HOST: YOUR_DOMAIN
VIRTUAL_PORT: 3000
LETSENCRYPT_HOST: YOUR_DOMAIN
LETSENCRYPT_EMAIL: YOUR_EMAIL
networks:
default:
external:
name: nginx-proxy
数据库:
version: '3'
services:
db:
image: postgres:latest
container_name: postgres
restart: always
environment:
- POSTGRES_USER=YOUR_NAME
- POSTGRES_PASSWORD=YOUR_PASSWORD
volumes:
- postgresql-db:/var/lib/postgresql/data
healthcheck:
interval: 10s
start_period: 30s
volumes:
postgresql-db:
Agent 在 Huginn 中是一个行为动作的抽象。
一系列 Agent 的集合
每一个 Agent 执行一次,输出就是 Event,Agent 输出的 Event 可以给其他 Agent 使用。
在 Agent 标签页会看到一系列的默认 Agent,Agent 可以理解为一系列不同类型的自动化任务,举一个简单的例子,这些 Agent 可以帮助抓取网页,或者读取 API,甚至监听文件变化等等。
每一个 Agent 在创建完成后会有下面一些指标:
Age: 表示这个事件创建了多久
Schedule: 表示的是间隔多长时间执行,从几分钟,到几天,到固定时间都有
Last Check: 表示上次执行时间
Last Event out: 表示上次任务执行输出
Last Event In: 表示上一次外部触发任务,比如输出的,需要由外部调用
Events created: 事件自创建后触发的数目
在创建 Agent 的时候有几个参数可以注意下:
这里将 Options 单独拿出来,这里举一个例子,比如抓取豆瓣高分科幻电影,这个 JSON 的整体格式大致如下:
{
"expected_update_period_in_days": "2",
"url": [
"https://movie.douban.com/tag/%E7%A7%91%E5%B9%BB?type="
],
"type": "html",
"mode": "on_change",
"extract": {
"url": {
"css": "td:nth-child(2) > div > a",
"value": "@href"
},
"title": {
"css": "td:nth-child(2) > div > a",
"value": ".//text()"
},
"cover": {
"css": "img",
"value": "@src"
}
},
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36",
"Host": "movie.douban.com"
}
}
说明:
on_change
在数据更改时才会获取产生事件merge
把新数据和输入数据合并all
获取所有数据extract 中用的语法可以是 css 选择器,或者是 xpath 语法。等创建好,可以点击下面的 Dry Run 来测试是否运行正确。
如果要输出一个网站的 RSS,可能需要需要新建两个 Agent,一个 Agent 来抓取网站内容,一个用来导出数据。
第一步,首先要新建 Agent,然后选择 Website Agent. Website Agent 会去抓取网页,包括 XML 文档,JSON feed 然后根据结果来触发事件。
获取网页中部分信息,可以在 extract
中使用 CSS 选择器,选择网页中想要提取的部分,比如下方提取页面中 url 和 title
Options
{
"expected_update_period_in_days": "2",
"url": "http://wufazhuce.com/",
"type": "html",
"mode": "on_change",
"extract": {
"url": {
"css": ".one-articulo-titulo/a",
"value": "@href"
},
"title": {
"css": ".one-articulo-titulo/a",
"value": "normalize-space()"
}
}
}
第二步新建 Agent,选择,Data Output Agent,这个 Agent 用来输出 RSS 或者 JSON 内容。这个时候需要注意将上一个 Agent 填到 Sources 中,在 Options 中将上一个 Agent 获取到的变量填到对应的位置。
{
"secrets": [
"one"
],
"expected_receive_period_in_days": 2,
"template": {
"title": "ONE RSS",
"description": "RSS ",
"item": {
"title": " - ",
"description": "",
"link": ""
}
},
"ns_media": "true"
}
保存之后,Agent 会暴露一个地址:
https://localhost:3000/users/1/web_requests/:id/:secret.xml
地址中的 :secret
是 Options 中设定的值,结尾可以是 xml
或者 json
。 在 RSS 阅读器中订阅该地址即可。
大致思路和上面 One 一样,找到入口,抓取内容,微信没有公开入口,所以只能送搜狗的入口来
https://weixin.sogou.com/weixin?type=1&s_from=input&query=%E5%8F%8D%E6%B4%BE%E5%BD%B1%E8%AF%84&ie=utf8&_sug_=n&_sug_type_=
从页面获取最新文章链接
{
"expected_update_period_in_days": "2",
"url": [
"http://weixin.sogou.com/weixin?type=1&query= 反派影评 &ie=utf8&_sug_=n&_sug_type_=&w=01019900&sut=2064&sst0=1470553392399&lkt=0%2C0%2C0"
],
"type": "html",
"mode": "on_change",
"extract": {
"title": {
"css": "#sogou_vr_11002301_box_0 > dl:nth-child(3) > dd > a",
"value": ".//text()"
},
"url": {
"css": "#sogou_vr_11002301_box_0 > dl:nth-child(3) > dd > a",
"value": "@href"
}
}
}
然后根据上一步的 url 获取全文内容
{
"expected_update_period_in_days": "2",
"url_from_event": "",
"type": "html",
"mode": "merge",
"extract": {
"title": {
"css": "#activity-name",
"value": "."
},
"fulltext": {
"css": "#js_content",
"value": "."
}
}
}
最后和导出 RSS 一样,将内容部分导出即可。
下面的网站提供了很多 huginn 实现的例子,对刚刚接触 Huginn 的人来说帮助很大。
官网有更多的例子
IFTTT 同类型的网站有很多,上面提及的 Zapier,还有这个开源版本的 Huginn,然后在用的过程中又发现了 integromat
这个站点。
很早就有人推荐 Notion,但是注册了用了一下,本以为就是一个在线的 Google Docs,可昨天在豆瓣看到一篇文章介绍比 Trello 更加智能的代替品,然后一看就是 Notion,于是就再来研究下 Notion。然后发现原来 Notion 可以是 Google Calendar, 可以是 Trello,可以是 Google Docs,可以是 todo list,可以是 Google Excel。甚至可以导入 word,markdown,html,csv。
Notion 最为人称道的地方在于其非线性的编辑器,我们通常的编辑器都是从上倒下的我们通常的编辑器都是从上到下,不管是 Google Doc,或者是 Dropbox Paper 等等,这些是我们非常熟悉的编辑器模式,Notion 强大之处在于它把可编辑区域的概念更加细化了,每一个段落都是一个可编辑的区块,这个区块可以是简单的文字,图片,也可以是非常复杂的多媒体,这样就使得一篇内容组成丰富。
因为受到推荐,所以第一时间找为什么可以作为 Trello 的代替品,于是我在 “New Page” 的页面中找到了 “Board” 的选项,Board 是 “Database with a kanban board view”,点开新建会创建一个 Board View, 这个 View 就和 Trello 很类似。
这个看板虽然第一眼看起来不是很好看,但是功能却一点都不缺。和 Trello 不相同的是 Board 中每个一个 card 都有 property ,每一个 property 都可以定义一定的内容,而这个内容可以作为过滤条件,比如可以新建一个选择属性,然后起名字叫做 Priority,分别有三个属性值,High,Medium,Low,然后可以给每一个卡片赋一个值,那么在右上角就可以按照这个 Filter 来过滤卡片。虽然这个在 Trello 可以用 Q
快捷键来过滤 assign 给自己的卡片,但是却做不到 Board 的这么强的过滤属性。
属性的类型
高级属性
和 Trello 不相同的另外一点就是 Board 可以按照不同的属性来分组,通常情况下 Trello 只能按照 Todo,Doing,Done,来分组,但是如果 Board 增加了一些属性,那么 Group By 就可以按照属性来分组显示,比如按照 Priority 来分组显示。
和 Google Docs 不同的是 Notion 中的 Page 可以插入不同的内容,包括普通的 Header,to-do list 等等,还包括外部的 Github, Video, Bookmark, Code, File, Google Maps, Framer, Invision 等等。
在 New Page 里面有 Calendar, 和 Google Calendar 一样,也可以在 Calendar 上创建新的任务,每一个任务都是 Page,可以在里面增加任何一个上面提到的属性。和上面提到的 Board 一样,过滤,排序等等。
Google Cloud 提供 300 刀的初始优惠,而最近我的 Linode 节点越来越不稳定,时常撞墙,所以不得不再别人强烈推荐下注册了一下 GCP。这里就记录一下遇到的问题,其他具体的细节网上已经够多,就不再赘述。
国内的信用卡不太清楚为什么我试了几个都有报错,网上说把账单地址改成美国,信用卡签名也最好保持一致。
GCP 的 Compute Engine 在不同的机房都有分布,在创建 Compute Engine 的时候可以参考这里 这里非常详细的介绍了各个机房的地理位置,以及该机房拥有的机器类型,特性等。
sudo -i
sudo su
默认情况下 Google 是禁止密码登录 SSH,我一般情况下会创建一个新用户
adduser xxx
然后给该用户提供登录权限
vi /etc/ssh/sshd_config
编辑
PasswordAuthentication yes
然后重启
service sshd restart
最近因为用 HTML 写了一个文档,当想要输出时保存为 PDF,而 Chrome 自带的打印功能,本来就能够快速的保存为 PDF,但是却保留不了页面中的链接,所以找到了这个 wkhtmltopdf.
wkhtmltopdf and wkhtmltoimage are open source (LGPLv3) command line tools to render HTML into PDF and various image formats using the Qt WebKit rendering engine. These run entirely “headless” and do not require a display or display service.
简而言之,wkhtmltopdf 是一个能够把 HTML 文档,或者在线 url 转换为 pdf 文档或者 image 图片的命令行工具。跨平台,支持 Linux,Windows,Mac。
有两种方法,一种是直接使用编译好的版本,下载 安装即可,一种是用源码 编译安装。
验证
wkhtmltopdf -V
查看版本。注意只有 qt 版本才能保留页面中链接。
命令用法
wkhtmltopdf [GLOBAL OPTION]... [OBJECT]... <output file>
将 HTML 文件转换成 PDF
wkhtmltopdf a.html a.pdf
将 URL 保存为 PDF
wkhtmltopdf https://douban.com douban.pdf
把网页转换为图片同理
wkhtmltoimage a.html a.jpg
wkhtmltoimage https://douban.com douban.jpg
全局参数解析
--collate 当输出多个副本时进行校验(这是默认设置)
--no-collate 当输出多个副本时不进行校验
--cookie-jar <path> 从提供的 JAR 文件中读写 cookie 数据
--copies <number> 设置输出副本的数量(默认主 1),其实为 1 就够了
-d, --dpi <dpi> 指定一个要分辨率(这在 X11 系统中并没有什么卵用)
-H, --extended-help 相对 -h 参数,显示更详细的说明文档
-g, --grayscale 指定以灰度图生成 PDF 文档。占用的空间更小
-h, --help 显示帮助信息
--htmldoc 输出程序的 html 帮助文档
--image-dpi <integer> 当页面中有内嵌的图片时,
会下载此命令行参数指定尺寸的图片(默认值是 600)
--image-quality <interger> 当使用 jpeg 算法压缩图片时使用这个参数指定的质量(默认为 94)
--license 输出授权信息并退出
-l, --lowquality 生成低质量的 PDF/PS , 能够很好的节约最终生成文档所占存储空间
--manpage 输出程序的手册页
-B, --margin-bottom <unitreal> 设置页面的 底边距
-L, --margin-left <unitreal> 设置页面的 左边距 (默认是 10mm)
-R, --margin-right <unitreal> 设置页面的 右边距 (默认是 10mm)
-T, --margin-top <unitreal> 设置页面的 上边距
-O, --orientation <orientation> 设置为“风景 (Landscape)”或“肖像 (Portrait)”模式,
默认是肖像模块 (Portrait)
--page-height <unitreal> 页面高度
-s, --page-size <Size> 设置页面的尺寸,如:A4,Letter 等,默认是:A4
--page-width <unitreal> 页面宽度
--no-pdf-compression 不对 PDF 对象使用丢失少量信息的压缩算法,不建议使用些参数,
因为生成的 PDF 文件会非常大。
-q, --quiet 静态模式,不在标准输出中打印任何信息
--read-args-from-stdin 从标准输入中读取命令行参数,后续会有针对此指令的详细介绍,
请参见 **从标准输入获取参数**
--readme 输出程序的 readme 文档
--title <text> 生成的 PDF 文档的标题,如果不指定则使用第一个文档的标题
-V, --version 输出版本信息后退出
FreeMarker is a free Java-based template engine, originally focusing on dynamic web page generation with MVC software architecture. However, it is a general purpose template engine, with no dependency on servlets or HTTP or HTML, and is thus often used for generating source code, configuration files or e-mails. by Wikipedia
Official site:
Since with maven-based project, add these requirement to pom.xml
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
import freemarker.template.*;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
private Configuration configuration;
configuration = new Configuration(new Version(2, 3, 20));
configuration.setClassForTemplateLoading(FreeMarkerTemplateUtils.class, "/templates");
configuration.setDefaultEncoding("UTF-8");
configuration.setLocale(Locale.CHINA);
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
try {
Template template = configuration.getTemplate("user_email.ftl");
String body = FreeMarkerTemplateUtils.processTemplateIntoString(template, userTemplateVariable);
} catch (IOException | TemplateException e) {
log.error("userTemplate error", e);
}
Writer out = new FileWriter(dir + "/freemarker.html");
template.process(root, out);
Starting from freemarker 2.3.7, you can use this syntax :
${(object.attribute)!}
or, if you want display a default text when the attribute is null
:
${(object.attribute)!"default text"}
If you have a list as List<Food> menu
<#list menu as food>
${food.name} ${food.price?string.currency}
</#list>
<#if var == 1>
1
<#elseif var == 2>
2
<#else>
0
</#if>
and switch
<#switch y>
<#case "one">
one
<#break>
<#case "two">
two
<#break>
<#case "three">
three
<#break>
<#default>
zero
</#switch>
<#function fact n>
<#if n == 0>
<#return 1 />
<#else>
<#return fact(n - 1) * n />
</#if>
</#function>
<#list 0..10 as i>
${i}! => ${fact(i)}
</#list>
用 RSS 这么多年,陆陆续续给自己的订阅列表增加,删除,更新了很多的订阅源,也有很多的订阅源陆陆续续的失效,也有很多的新博客陆陆续续的涌现,不过如今的 RSS 早已经不像当年,很多新站点甚至都不会再提供 RSS 输出。
之前偶然间看到过一篇关于 RSS 整理的文章,觉得定期检视一下自己的订阅,更新也好,删除也好,整理让这些订阅源继续存在的理由也好,或者分享给更多的人也好,都是一件不错的事情。
我的所有订阅源都从 Google Reader 迁移到了 InoReader,订阅列表好几百项就不单独一一例举,只是将我平时经常查阅的,我觉得非常值得关注的列举一下,也欢迎大家推荐高质量的订阅源。我的部分订阅已经整理成了 InoReader 的 Bundle,具体内容可以参考下方。
这一个分类是更新不频繁,但是只要更新质量有一定保证的源,我会刻意选择不让每天更新的数量有 999,所以这是一个经常清零的高质量源。
这个分类中是一些关注的个人博客输出,因为有些博客更新并不是那么频繁,所以这个分类下的未读数一直都不是很高,这个目录下我存放一些有必要关注(可能是内容,可能是标题),但是又不需要非常及时关注的内容,比如说技术博客,常用工具博客等等
这个分类建了很久了,知道我的人肯定知道我不用 weibo,也把很多国内的社交网站删的一干二净了,但是有些时候总是有些信息源只更新 weibo,比如有些同学的,比如有些资源号,所以没办法我只能用工具 把 Weibo 的内容转成 RSS 输出到 InoReader 以便于存档和及时关注。至于 Instagram 同样,因为不怎么常打开,所以反而 RSS 看起来比较舒服。
这个分类创建之后只有特殊需求的时候才会看一下,大部分情况下都只是做个参考,这个分类收录了一些更新 VPS 优惠活动的站点,每当我想要购买 VPS 的时候会从中搜索一下这个提供商,看看历史记录里面的评价好坏。
这个目录下是一些图书,Kindle 技巧,图书优惠的站点,本人看的也不是非常多,只是有需要的时候,一方面作为检索,另一方面作为备份。
这个目录放了一些推荐软件和应用专题的站点,很早再用 Windows 的时候关注了一批就顺手关注了,但是现在看的很少了。
这个目录有关注一些播客制作的博客,有一些混音的博客,很少看。
记录了商品的优惠价格,以作备份。
每日必读
Kindle 相关
软件及应用
又到一年的年末,和前几年 一样,总需要总结下今年,再畅想一下明年,前些年有些时候会用 Google Photos 来存书的封面,也会记录下书的名字,今年就直接用了豆瓣的豆列 来记录了。
相较于前两年对东野圭吾的狂热,今年刻意地减少了虚构文学的数量,一方面也是因为东野圭吾的小说已经看遍,再没有另外一个小说家能够如此吸引我。所以看今年的书单,除了《造彩虹的人》《白金数据》外,只有特德姜的《你一生的故事》等少数几本。
人文哲学或许是今年最大的改变,这都要从哈耶克的《通往奴役之路》开始,因为这本书听了一档人文通识解读的播客,自此之后看着一类的书籍再也不会感到无聊,再就是今年国内形势明显发生了改变,所以这一类的文学作品才又被重新认识。然后因为哈耶克所以又读 了哈耶克的《我为什么写作》,很多人认识哈耶克仅仅因为《通往奴役之路》然而这本杂文集却从另外一个维度让我认识了哈耶克,以及他的思想从何而来,甚至前几篇文章都可以作为自传来看,在别国维和的所见所闻至今让我印象深刻。
之后又看了 [[独裁者手册]] 这也是一本让我震惊的书,一本让我觉得每一个段落都值得摘抄的书,一本让我一读就停不下的书。不像有些书写得像学术论文,这本手册虽然也是两位教授的研究性报告,但却有条有理,甚至会让我觉得有些“八股”,抛出一个论点,辅以一些论证,每一章节,每一个观点都非常充分的表达了。
再到《人类简史》一本走到哪里都是放在畅销书架的大热门,而实话说,虽然一定程度上和作者的一些观点一致,但并没有让我觉得这本书的预见性。阅读这本书倒是让我学习了很多人类进化的历史过程。
最后还有一部不得不提的杂文集《野火集》,知道龙应台也已经好久,因为在看《大江大海 1949》的时候看到这本《野火集》就将其加到了待看列表,一本写在 1985 年的书,却预言了几十年后发生的事情,做坏事的方法不管在哪个年代都一样,也是非常值得推荐一读的。
今年最多的可能就是技术类书籍了,也一定程度上达成了之前几年定下的目标,在看这些书的过程中,我也学会了粗读和精读,有些书因为出版年份较久,而技术发展是出版物无法跟随的,但是有些书中的理念和核心观点是不会变化的,就像年初看的 Docker 虽然有些技术细节发生了很大的变化,但是 Docker 的内在机制是没有发生大的变化的,再到 Python ,虽然很多书基于 2.x 版本,但是其实原理是相通的。
就像我在一条豆瓣书评中说的那样,个人对传记类的书籍并不是很关心,想来也只在大学的时候读过金庸的传记,甚至连我最喜欢的 Verne,都没有特意去寻找他的传记,我觉得一个好的作家只需要在作品中表现自己即可,但是今年的一个契机,让我看了一些人物传记,这其中包括巴菲特,万达的王健林,再到腾讯传。看一个人物的成功历程,或许有些鸡汤,也能看出一些美化的痕迹,然而却都少不了这些人物之所以成功的理由,抛开那些勤奋,努力,天资,我看到的更多是这些人在那个时代超前的视野,对于时代变化的敏感,而这又离不开曾经的勤奋和努力。
这一个条目在 Trello 的代办事项中已经躺了快两周,期间一直在寻找合适的任务提醒 app 能够来帮助初期养成一定的习惯,没想到寻寻觅觅最后竟然又回到了 Google Calendar 的怀抱。
个人原来就有一习惯,对于需要长期规划的有固定时间的事情一般都会记录在 Google Calendar 中,比如周期性长时间的课程,或者一次性的旅程时间安排等等,而对于非周期性任务用 Trello 来管理,所以一直想要寻找一个能够周期性提醒,关键是要手机通知栏提醒,但是使用起来又比较方便(可以一键 mark as done,定制任务快捷)的应用,下面是我总结出来的一些此类习惯培养 app,我认为必须匹配的一些功能:
最开始的时候想用 Google Calendar 来定义周期性定时任务,这本来非常简单,但是 Google Calendar 在提醒的同时并不能 mark done,这就有些不便,并且对于短期,比如签到此类的任务总不能定义 5min 的任务吧。
其次又想到了日常天天在用的 Trello,然而 Trello 适合任务管理,对周期性任务支持也并不友好。
然后又在 iOS 上找到了番茄 todo,Tiny Routines,小目标,都非常精致,但都不是理想中的那么方便。并且这些 app 都参杂了太多的商业内容,虽然承认这些应用都很不错,但是也无形中增加了使用成本。
Google Calendar 中创建日程可能会发现如下三种类型
这三种类型在 Google Calendar 中的作用各不相同,通常情况下,使用创建按钮在标题下方可能会看见 Event 和 Reminder 选项
当时如果在周视图,或者月视图中,使用鼠标选择多天,那么在创建的对话框中会出现 Task 选项
但是需要注意的是 Task 只能够选择一天,而事件则能够选择一个时间范围。所以这里就要求我们来具体区分一下这三者的区别。
Event 事件是 Google Calendar 最重要的概念,事件是有发生的时间范围的,从创建事件的选择中就能看到,一个事件可以拥有如下的选项:
总体来说 Event 是 Calendar 的核心,如果是有固定发生时间的事情,那么用 Event 来组织是最好的,在 Google Calendar 中会在视图中以设定的颜色来显示在图中。
Reminder 提醒,提醒相较于 Event 就要简单很多,提醒只能够定义:
所以从他的涵盖范围就能够知道 Reminder 是用来在特定时间来提醒一件比较能够快速完成的事情的,理论上提醒的事情应该能够短则几分钟,长则十分钟就能够完成的事情。而新建 Reminder 也是最最简单的事情,甚至用语音 “reminder me to do something at some time” 就能快速建立的事情。Reminder 生成的内容可以标记 Done,在界面中就会显示为横线删除的样子,表示已做,非常清楚。
Task 是最让人混淆的事情,我个人习惯使用 Trello 来管理我的 Task,可以这么理解,Task 可以是还没有具体时间安排的任务。所以在日历中最上方全天安排点击的时候 Task 的选择才会出现。Task 能够设定的只有:
对于需要办但是却没有安排出具体时间来办的事情就可以使用 Task 来组织管理。Task 则更像一个 to-do list 管理工具,不过这个 to-do list 则会贯穿整个 Google 的产品线,在 Google Calendar 中创建的 Task 会在 Gmail 中出现
在 Google Calendar 的移动版,包括 Android 和 iOS 在点击加号之后可能会发现桌面版没有的一个选项,就是 Goal,目标,这个功能是我发现将 Google Calendar 提升一个境界的地方。在这个 Goal 中可以设定一个目标,比如看一本书,坚持一定时间的锻炼,再比如学习一个技能,包括但不限于语言啊,乐器啊等等,Google Calendar 默认给出了非常好看的一些模板,非常容易上手。在 Goal 中可以定义:
在制定这一系列方案之后 Google Calendar 会自动分析日程安排,将这个目标穿插在日程安排中,以给定的时间范围和频率定时提醒来完成目标。于是这个功能就可以当做习惯培养的绝好工具,不管做任何事情,指定一个目标让 Google Calendar 帮助你安排时间,简直太赞了。
更加重要的一点是,Goal 产生的每一次事件都能够标注是否 done,每一个事件打开都能够看到这个目标前几周的完成情况,以及是否 Mark 这一次 Done,这样就能够很好的督促自己的认真的完成每一次目标。
后来在寻觅的过程中突然发现了移动版的 Google Calendar 点开加号除了常规的 Event,竟然还有 Goal 和 Reminder 两个选项。
Reminder 很好理解,字面意思,提醒,不过 Google Calendar 更加智能的是可以周期性提醒,那这就完美的解决了培养习惯通常开始需要的提醒,并且跨平台,有通知,这就非常方便了。
而 Goal 则是制定一个目标,由 Google Calendar 来自动决定哪个时间段来帮助完成这个目标。
直接在 Chrome 的工具栏中访问 Google Calendar 中内容。
允许在浏览器中追踪花了具体多长时间,和 Calendar 结合就能具体看到做一件事情花费了多久。需要结合 Toggl 这个网站使用,所有的数据都会被导入到该网站。
通常来讲 RuntimeException 是在编码过程中可以被避免的 Exception ,比如说 NullPointerException
, ArrayIndexOutOfBoundException
等。如果每次都在调用前检查 null,就永远不会发生 NullPointerException
,ArrayIndexOutOfBoundException
同理。RuntimeException 不会被编译器检查。
Java 中有两种类型异常,一种是 checked exceptions,一种是 un-checked exceptions。检查的异常必须被显示的代码处理,un-checked exceptions 则不需要。任何一个从 Exception 派生的类都是 checked exception, 而从 RuntimeException 派生的则是 un-checked.
Exception 和 Error 都是继承自 Throwable,Throwable 则是继承 Object。而 RuntimeException 则是继承 Exception。
下面是一些常见的 RuntimeException
AnnotationTypeMismatchException,
ArithmeticException,
ArrayStoreException,
BufferOverflowException,
BufferUnderflowException,
CannotRedoException,
CannotUndoException,
ClassCastException,
CMMException,
ConcurrentModificationException,
DataBindingException,
DOMException,
EmptyStackException,
EnumConstantNotPresentException,
EventException,
IllegalArgumentException,
IllegalMonitorStateException,
IllegalPathStateException,
IllegalStateException,
ImagingOpException,
IncompleteAnnotationException,
IndexOutOfBoundsException,
JMRuntimeException,
LSException,
MalformedParameterizedTypeException,
MirroredTypeException,
MirroredTypesException,
MissingResourceException,
NegativeArraySizeException,
NoSuchElementException,
NoSuchMechanismException,
NullPointerException,
ProfileDataException,
ProviderException,
RasterFormatException,
RejectedExecutionException,
SecurityException,
SystemException,
TypeConstraintException,
TypeNotPresentException,
UndeclaredThrowableException,
UnknownAnnotationValueException,
UnknownElementException,
UnknownTypeException,
UnmodifiableSetException,
UnsupportedOperationException,
WebServiceException