[Elixir001]使用tty做一个简单的日记记录
大半年前实践的使用Evernote做知识管理 https://www.zhihu.com/question/20232993/answer/34270710里面的记录日记模块大概长成这样子
用了半年,感觉缺点很明显,表格太占空间,每周不会自动生成新的表单,搜索功能弱爆了。容易忘记写。
所以我决定用Elixir的个终端版本的。开始!
1. 新建项目
mix new lifelog
cd lifelog
生成的项目文档如下
└── lifelog
├── README.md
├── config
│ └── config.exs
├── lib
│ └── lifelog.ex
├── mix.exs
└── test
├── lifelog_test.exs
└── test_helper.exs
全部修改可见: github log: 创建项目
2. 创建Application
在生成的lifelog.ex中使用Application创建一个工作进程LifeLog.Store,后续会使用它来储存所有的数据,使用重启策略为rest_for_one
def start(_type, _args) do
import Supervisor.Spec children =
[worker(LifeLog.Store, [])] Supervisor.start_link(children, strategy: :rest_for_one)
end
因为想使用escript的方式来启动Application。mix自带生成escript命令
mix escript.build
使用这个命令的提示可以
mix help escript.build
Example ┃ defmodule MyApp.Mixfile do
┃ def project do
┃ [app: :myapp,
┃ version: "0.0.1",
┃ escript: escript]
┃ end
┃
┃ def escript do
┃ [main_module: MyApp.CLI]
┃ end
┃ end
全部修改可见 github log: 指定Application启动时进程结构, 并指定生成escript文件方式
要特别注意的是为了让escript支持颜色ANSI显示, 我们在escript中加入了
emu_args: "-noinput -elixir ansi_enabled true"
那些接下来我们就把escript中指定的入口 LifeLog.CLI完成。
3. 完善escript的入口中指定的LifeLog.CLI
defmodule LifeLog.CLI do
def main(_args) do
LifeLog.Watcher.start_link(LifeLog.IO)
:erlang.hibernate(Kernel, :exit, [:killed])
end
end
这个进程只是新启动一个进程gen_event(watcher 稍后我们再完善它)来监控终端的动作(输入或输出),然后自已就hibernate.
我们再来看看LifeLog.IO.ex, 这个gen_event主要是打开一个端口,并监控此端口的输入,和控制它的输出内容
Port.open({:spawn, "tty_sl -c -e"} # 打开端口
只要打开了端口tty_sl后,那么当 tty终端有输入内容时,此IO的gen_event就会收到消息
def handle_info({pid, {:data, data}}, %{pid: pid, buffer: buffer}) do
case translate(data, buffer) do
{:continue, buffer} ->
Store.update(buffer, 0)
{:ok, %{pid: pid, buffer: buffer}}
{:done, buffer} ->
Store.update(buffer, 1)
{:ok, %{pid: pid, buffer: ""}}
end
end
你每按下一个按键都会发触发这个事件,那个我们需要做的就是
3.1. 每个按键都告诉Store(这个是用来储存所有数据的),
3.2. 实现退格 一旦输入del就删除提buffer的最后一字符
3.3. 输入enter完成, 一旦输入enter就表明本条记录已完成,传给Store后进入下一条
全部修改可见: github log:cli负责生成script脚本, IO负责开端口使用tty_sl输入和IO.write到所开的端口输出
接下来我们再完成一个Store中的储存方式。
4. Store的储存逻辑
Store应该为gen_server,他全部责任就是
4.1 把数据从文件中读到state中
4.2 从IO得到应该update的数据,然后更新
4.3 把数据持久化到文件中
所有修改可见: github log: 所有的数据都存在Store中
我们已经完成了把IO输入的消息都传给Store,
接下来关键在于怎么把Store的数据变化再回写到IO上,让我们能看到!
5. 使用Watcher把Store和IO联系起来
在Watcher中把IO进程加到 Store中起到的manager(gen_event)中。
%{manager: manager, diary: diary} = LifeLog.Store.peek
:ok = GenEvent.add_mon_handler(manager, LifeLog.IO, diary)
这样只要Store对着manager发起notify事件,那么IO进程就会收到!,这样我们就可以把Store上的数据变化再通知到IO上。
所有的修改可见: github log: Watcher负责把Store(gen_server)和IO(gen_event)连接起来
按下来,我们再看看我们应该用什么格式都数据存在文件中
6.新建文件file.ex 只负责从文件中读数据,和写数据到文件中
持久化方案选择:
. 可以使用:erlang.term_to_binary/1把map encode to binary写放文件,然后load时使用:erlang.binary_to_term/1来decode文件中的数据
. 把数据转成json数据格式
为了存数据方便,我们使用了方案2: 通用的json格式存数据,所以引放了鼎鼎大名的poison库来处理json.
全部修改可见: github log: file负责持久化数据,把数据从文件中load出来
数据的取存都解决了,那们我们来做一下数据的显示,怎么显示数据到终端。
7. 新建formatter.ex 负责界面绘制
! 不要在IO中加放显示的具体逻辑,让formatter把显示的格式整理好)直接传给IO就行,IO只负责输入和输出,不负责输出的具体逻辑。
所以修改可见: github log: formatter负责界面绘制
到目前为止我们见到的终端界面是这样子的:

是不是很弱。。。。。
其实我们的基本功能都实现啦!
接下来就是我们把一周的数据都显示出来啦!
8.显示一周的数据
为了更好的管理一周的数据,我们定义了几个Daily DailyItem WeekItem struct来管理数据结构。
所有修改可见 github log: 新增struct, 使用Posion.decode!/2序列化
这里使用了posion可以递归的解析struct的特性,所以使用起来非常好理解。
JsonData|>Poison.decode!(as: %Diary{daily_items: [%DailyItem{content: [%Daily{}]}], week_items: [%WeekItem{}]})
接下来就是非常繁锁的一点点调界面显示时每个block的大小。。。
这里有一点要注意的是,我们把所有的数据+格式都调整完成后再一次性给IO来做显示的。(而不是一行一行的写)
所有的修改可见:githhub log: 显示界面绘制 与 调整布局:最后一行统一加下划线
上面的IO.ANSI也是很好玩的一个lib~
这下子就基本能用啦:)
运行:
mix escript.build && ./lifelog

[Elixir001]使用tty做一个简单的日记记录的更多相关文章
- 【Bugly干货分享】一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎
Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言 好吧,说是“粒子引擎”还是大言不 ...
- 使用React并做一个简单的to-do-list
1. 前言 说到React,我从一年之前就开始试着了解并且看了相关的入门教程,而且还买过一本<React:引领未来的用户界面开发框架 >拜读.React的轻量组件化的思想及其virtual ...
- 【 D3.js 入门系列 --- 3 】 做一个简单的图表!
前面说了几节,都是对文字进行处理,这一节中将用 D3.js 做一个简单的柱形图. 做柱形图有很多种方法,比如用 HTML 的 div 标签,或用 svg . 推荐用 SVG 来做各种图形.SVG 意为 ...
- 一起用HTML5 canvas做一个简单又骚气的粒子引擎
前言 好吧,说是"粒子引擎"还是大言不惭而标题党了,离真正的粒子引擎还有点远.废话少说,先看demo 本文将教会你做一个简单的canvas粒子制造器(下称引擎). 世界观 这个简单 ...
- Jmeter初步使用二--使用jmeter做一个简单的性能测试
经过上一次的初步使用,我们懂得了Jmeter的安装与初步使用的方法.现在,我们使用Jmeter做一个简单的性能测试.该次测试,提交的参数不做参数化处理,Jmeter各元件使用将在介绍在下一博文开始介绍 ...
- 用EF DataBase First做一个简单的MVC3报名页面
使用EF DataBase First做一个简单的MVC3报名网站 ORM(Object Relational Mapping)是面向对象语言中的一种数据访问技术,在ASP.NET中,可以通过ADO. ...
- 【 D3.js 入门系列 — 3 】 做一个简单的图表!
图1. 柱形图 1. 柱形图 前几章的例子,都是对文字进行处理.本章中将用 D3 做一个简单的柱形图.制作柱形图有很多种方法,比如用 HTML 的 <div> 标签,或在 SVG 上绘制 ...
- Windows Phone开发(21):做一个简单的绘图板
原文:Windows Phone开发(21):做一个简单的绘图板 其实我们今天要说的就是一个控件--InkPresenter,这个控件并不是十分强大,没办法和WPF中的InkCanvas相比,估计在实 ...
- 使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)
使用Multiplayer Networking做一个简单的多人游戏例子-1/3 使用Multiplayer Networking做一个简单的多人游戏例子-2/3 使用Multiplayer Netw ...
随机推荐
- php 数字格式化
php 数字格式化 1.位数不足前面补0 <?php for($i=1; $i<=17 ;$i++){ $var = sprintf("0%3d",$i); echo ...
- SPOJ Count on a tree(主席树+LCA)
一.题目 COT - Count on a tree You are given a tree with N nodes. The tree nodes are numbered from 1 to ...
- CentOS6系统优化
[root@xuliangwei ~]# cat /etc/redhat-release //系统环境CentOS6.6 CentOS release 6.6 (Final) [root@xulian ...
- OpenCV Hello World
▶ OpenCV 的环境配置与第一个程序 ● 去官网下载安装包 https://opencv.org/releases.html ▶ OpenCL 在Visual Studio 2015 中的配置.注 ...
- Unix高级编程Note2
[Unix Note2] 1.信号屏蔽 2.信号不会排队,即产生同时产生10次,会被合并为1次. 3.sigsuspend,sigsuspend后,进程就挂在那里,等待着开放的信号的唤醒.系统在接收到 ...
- C++:初始化列表
有两种原因需要使用初始化列表: 让我们先看一下第一个原因——必要性.(1)对另一个类成员的初始化,(2)成员是一个常量对象,(3)成员是引用.根本原因:编译器总是确保所有成员对象在构造函数体执行之前( ...
- react-native 打包 出apk
先上步骤: 一. 生成签名文件(my-release-key.keystore文件) Android要求所有应用都有一个数字签名才会被允许安装在用户手机上 1. 在项目目录下运行如下命令: keyt ...
- 使用javascript,jquery实现的图片轮播功能
使用javascript,jquery实现的图片轮播功能本功能采用最基础的javascript和一些简单的jquery技术实现,易理解,以修改使用,代码简易,适合刚开始接触到网站开发的朋友们参考.可以 ...
- sql游标使用
一.游标的作用:Select时,返回的是一个结果集,若需要为结果集返回的过程中,读取到一行数据.需要对此行数据进行处理,比如按读取到的数据作为查询条件返回一个查询结果集等等,应用都需要用到游标.游标可 ...
- [转].NET 4.5+项目迁移.NET Core的问题记录 HTTP Error 502.5
本文转自:http://www.cnblogs.com/ronli/p/5900001.html 这几天试着把目前的开发框架迁移到新的.net core平台,中间遇到的问题在这里简单记录一下. 迁移过 ...