diff --git a/README.md b/README.md index 0d91d615..88618e40 100644 --- a/README.md +++ b/README.md @@ -22,20 +22,26 @@ https://github.com/shylinux/context-bin/raw/master/bench-windows-amd64.exe https://github.com/shylinux/context-bin/raw/master/bench-darwin-amd64 ### 1.1 context源码安装 -#### 1.1.0 golang开发环境安装 +#### 1.1.0 golang安装 +* mac安装 https://github.com/shylinux/context-bin/raw/master/go1.6.2.darwin-amd64.pkg +* windows安装(推荐使用git的shell) https://github.com/shylinux/context-bin/raw/master/Git-2.12.2-32-bit.exe +* windows安装 https://github.com/shylinux/context-bin/raw/master/go1.6.2.windows-386.msi +* Linux安装 集成在了开发环境的安装包中,先安装好git即可 + +#### 1.1.1 golang开发环境安装 * 下载:git clone https://github.com/shylinux/context-dev * 安装:cd context-dev && ./install.sh -#### 1.1.1 context源码安装 +#### 1.1.2 context源码安装 * 下载:git clone https://github.com/shylinux/context * 编译:cd context && go install src/example/bench.go ## 2 context使用 ### 2.0 应用示例--启动WEB服务器 ```sh - $ bench - > ~web -  > serve ./ ':9090' +$ bench +> ~web +> serve ./ ':9090' ``` 在shell中,运行命令bench,启动应用,进入到一个类似于shell的环境中。 @@ -44,7 +50,7 @@ https://github.com/shylinux/context-bin/raw/master/bench-darwin-amd64 打开浏览器输入"http://localhost:9090" ,即可看一个静态WEB服务器已经启动。 ### 2.1 常用命令 -#### 2.1.1 cache: 缓存管理 +#### 2.1.1 缓存管理cache ```sh web> cache address(:9090): 服务地址 @@ -54,7 +60,7 @@ protocol(http): 服务协议 输入"cache"命令,即可查看当前模块的缓存数据,通过这些缓存数据,就可以了解模块的当前各种运行状态。 如"address",代表web模块监听的网络地址为"0.0.0.0:9090"。 -#### 2.1.2 config: 配置管理 +#### 2.1.2 配置管理config ```sh web> config logheaders(yes): 日志输出报文头(yes/no) @@ -68,7 +74,7 @@ logheaders(no): 日志输出报文头(yes/no) ``` 输入"config logheaders no"命令,修改logheaders的值为no,即不在日志中输出报文头。 -#### 2.1.3 command: 命令管理 +#### 2.1.3 命令管理command ```sh web> command serve: serve [directory [address [protocol]]] @@ -81,7 +87,7 @@ route: route directory|template|script route content 参数"address",代表服务器监听的网络地址。 参数"protocol",代表服务器使用的网络协议。 -#### 2.1.4 context: 模块管理 +#### 2.1.4 模块管理context ```sh web> context web(ctx:cli:aaa::): start(:9090) 应用中心 @@ -108,7 +114,7 @@ file2(nfs::aaa:root:root): start(etc/init.shy) 扫描文件 输入"context ctx"命令,切换ctx模块为当前模块。输入"context"命令,即可查看当前模块及子模块的基本信息。 ctx为根模块,所以可以查看到所有模块的基本信息。 -#### 2.1.5 message: 消息管理 +#### 2.1.5 消息管理message ```sh tcp> message requests: @@ -124,7 +130,7 @@ historys: 输入"message"命令,查看当前模块收发的所有消息。 其中"requests"是收到的长消息,如有一条ctx模块发送给tcp模块的编号为9消息。 其中"sessions",是发送出的长消息,这里为空,所以tcp模块没有发送长消息。 -其中"historys"是模块收到的所有消息,包括长消息和短消息。显示中除了显示当前消息,还会显示消息的子消息。 +其中"historys"是模块收到的所有消息,包括长消息和短消息。显示中除了显示当前消息,还会显示消息的子消息。如这里,编号为4358的消息就有三条子消息4359、4361、4363。 ```sh tcp> message 9 message: 0 @@ -133,7 +139,7 @@ message: 0 输入"message 9"命令,查看编号为9的消息的具体信息。 ### 2.2 web模块的命令 -web模块,提供web服务。目前有两条命令serve主机管理,route路由管理。 +web模块,提供web服务。目前有两条命令:主机管理serve,路由管理route。 ```sh web> command serve: serve [directory [address [protocol]]] @@ -141,30 +147,33 @@ route: route directory|template|script route content /demo: /demo ``` -#### 2.2.1 serve主机管理 +#### 2.2.1 主机管理serve ```sh web> command serve serve [directory [address [protocol]]] 开启应用服务 ``` +serve命令会监听网络端口,并启动web服务器。 * directory服务目录 * address服务地址(ip:port) * protocol服务协议(http/https) -#### 2.2.2 route路由管理 +#### 2.2.2 路由管理route ```sh web> command route route directory|template|script route content 添加应用内容 ``` +route命令会向指定的URI添加响应内容。响应内容可以是文件内容,可以是golang的模板,可以是脚本执行结果。 参数route代表http请求的uri地址,参数content代表响应回复的内容,不同类型的服务有不同的意义。 + * directory静态服务 ```sh web> route directory /p pkg ``` 命令"route diretory /p pkg",当web模块接收到请求uri为"/p/"时把目录"pkg"中的内容作为响应回复。 - content代表路径,即web服务请求此route路径时,回复的内容为content指定的目录或文件。 + * template模板服务 ```sh web> route template /t LICENSE @@ -172,7 +181,9 @@ web> route template /t LICENSE 命令"route template /t LICENSE",当web模块接收到请求uri为"/t"时把文件"LICENSE"中的内容作为响应回复。 * script脚本服务 - +```sh +web> route script /s echo.shy +``` content代表脚本的文件名,即web服务请求此route路径时,回复的内容为content指定的脚本运行后输出的内容。 ## 3 context开发 @@ -207,6 +218,7 @@ func init() { ctx.Index.Register(Index, nil) } ``` + 在context目录下,打开src/example/bench.go文件,添加一行 _ "example/demo",引入新添加的模块 。 ```go package main @@ -235,6 +247,7 @@ func main() { ctx.Start(os.Args[1:]...) } ``` + 在context目录下,编译安装bench.go,启动bench进入新模块,执行新添加的命令。 ```sh $ go install src/example/bench.go @@ -251,6 +264,7 @@ func init() { } ``` 在模块初始化时,向ctx模块注册当前模块,即当前模块为ctx的子模块。 + ```go var Index = &ctx.Context{Name: "demo", Help :example demo", Caches: map[string]*ctx.Cache{}, @@ -260,6 +274,8 @@ var Index = &ctx.Context{Name: "demo", Help :example demo", ``` Index即为模块的数据结构,Name为模块的名字,Help为模块的简介,Caches为模块的缓存项, Configs为模块的配置项,Commands为命令项。 +每个模块都需要初始化这五个属性,通过这些属性向外提供标准接口。 + ```go type Cache struct { Name string @@ -285,9 +301,15 @@ type Command struct { Hand func(m *Message, c *Context, key string, arg ...string) } ``` +三种标准接口的定义如上,Cache为缓存项,模块的内部数据可以定义成Cache,外部通过查看这些Cache就可以了解模块当前运行的各种状态。 +Config为配置项,模块内的一些常量或是可配置的数据,就可以定义成Config,外部通过对这些配置的设置,就可以改变模块的运行环境,控制模块的功能。 +Command的命令项,模块向外提供的各种API接口或是CLI接口都统一定义为command,即可以被其它模块在代码中直接调用,也可以在命令行实时调用。 +通过这种方式直接解除了模块的依赖关系,每个模块都可以独立编译,独立运行。 +通过脚本或是配置把各种模块组合在一起,完成复杂的功能。这样大大降低了代码的重复性,提高了代码的通用性。 Cache为缓存项的定义,Name为缓存项的名字,Value为缓存项的值,Help为缓存项的帮助信息,Hand为缓存项读写函数,可选。 Config为配置项的定义,Name为配置项的名字,Value为配置项的值,Help为配置项的帮助信息,Hand为配置项读写函数,可选。 Command为命令项的定义,Name为命令项的名字,Help为命令项的帮助信息,Hand为命令项执行函数。 + ```go Commands: map[string]*ctx.Command{ "echo": &ctx.Command{Name: "echo word", Help: "echo something", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { @@ -295,7 +317,10 @@ Commands: map[string]*ctx.Command{ }}, }, ``` -命令Hand函数,是消息驱动的。 +echo命令,按照缓存项format的格式输出配置项default的内容。 +这样可以从命令行查看缓存项format的值,就可以知道echo命令输出的格式。 +这样可以从命令行设置配置项default的值,就可以改变echo命令输出的内容。 +命令Hand函数,是消息驱动的。所以各种通过消息m就可以调用到各种函数。 m.Cap()读写当前模块的某个缓存项。 m.Conf()读写当前模块的某个配置项。 m.Echo()输出命令执行结果。 @@ -332,8 +357,7 @@ func (m *Message) Log(action string, ctx *Context, str string, arg ...interface{ 参数ctx指定模块, 可取nil。 参数str与arg与Printf参数相同。 -### 3.1 context模块开发进阶 -#### 3.1.0 context模块的生命周期与继承多态 +### 3.1 context模块的生命周期与继承多态 context结构体封封装了三种标准接口,cache、config、command。 但这些接口只能保存与处理简单的数据。如果需要处理复杂的数据,可以自己定义类型。 自定义的类型必须实现Server接口。 @@ -427,9 +451,19 @@ func init() { ctx.Index.Register(Index, demo) } ``` +模块通过Context结构向外提供各种功能调用的接口,通过Server接口向ctx模块提供生命周期的控制函数。 +所以模块可以被复制,被删除,就有了生命周期。 +并且每个模块都会有父模块与子模块,所以就会形成一个动态的树状态结构,不断的有新加的模块,也会有消失的模块。 + +每个模块在被加载与注册时生成一个静态模块,Index即是静态模块的Context,在init()函数中会给创建模块的Server,并将Context与Server绑定到一起,同时向父模块注册当前模块。 + +当需要创新新模块时,会调用Spawn()从当前模块复制出来一个新的模块,作为当前模块的子模块。并调用Begin()对新模块进行初始化操作。 +当需要模块启动一个服务时,会调用Start(), 当需要模块关闭一个服务时,会调用Close()。服务的实现方式可以是一次性操作的,也可以是一直运行的。 + 新定义了一个结构体Demo,可以向其添加各种自定义的数据。ctx.Context指向绑定的模块。 并实现了四个生命周期函数。Spawn()在创建新模块时会添加一个新缓存项。创建新的自定义结构体,并与新模块绑定。 init()创建新的自定义结构体,并与静态模块绑定。 + ```sh $ go install src/example/bench.go $ bench @@ -455,6 +489,7 @@ one: hello go world 新模块中有format缓存项,则不会调用父模块的format。 新模块中没有default配置项,则会调用父模块的配置项。 从而实现了模块间的继承与多态。 + ```sh one> del one> context demo @@ -464,9 +499,10 @@ two(demo:cli:aaa:root:root): start() new module ``` 在one模块下调用del,则会删除当前模块。调用context demo切换到父模块,则看到one模块已经被删除。 -#### 3.1.1 message消息驱动的开发模型 -为了降低模块间的信赖关系,除了定义三种标准接口外,还有模块间的通信使用消息机制。 -所以大多函数中都会有参数m代表当前的消息,arg消息的请求行参数。 +### 3.2 message消息驱动的开发模型 +无论是Context中的三种标准接口,还是Server中的四个生命周期接口,都是基于消息调用的。 +即模块的所有操作都是消息驱动的。 所以大多函数中都会有参数m代表当前的消息,arg消息的请求行参数。 + ```go Commands: map[string]*ctx.Command{ "send": &ctx.Command{ @@ -480,11 +516,15 @@ Commands: map[string]*ctx.Command{ }, ... ``` -如上,再给demo模块添加一条send命令。 -m.Find()会查找当前模块的子模块,并创建一个由当前模块发送给子模块的消息。 -msg.Cmd()设置请求行,并执行命令。 -msg.Result()取出响应行的数据。 -m.Echo()设置当前命令的执行结果,即当前命令的响应行。 +如上,消息的使用示例。 再给demo模块添加一条send命令。 +消息是有发起模块与接收模块,所以首先要指定接收模块创建一个新的消息。 +然后设置请求行与请求头的数据,并发送消息。 +最后从响应行与响应头中取出执行的结果。 + +这里从参数arg中取出接收模块的名字,调用Find()查找此模块,并创建新消息。 +然后调用Cmd()发送消息,通过参数设置了请求行。消息会发送给对应模块并调用模块的echo命令。 +最后调用Result()取了响应行的数据,并调用Echo()设置了send命令的执行结果。 + ```sh $ go install src/example/bench.go $ bench @@ -495,7 +535,7 @@ one: hello go world ``` 重新编译并运行bench,在demo模块下,创建一个新的模块one,并切换回demo模块向one模块发送一条消息。 -* 消息创建 +#### 3.2.1 消息创建 ```go func (m *Message) Search(key string, root ...bool) []*Message func (m *Message) Find(name string, root ...bool) *Message @@ -511,13 +551,13 @@ Sess()是对Search()与Find()进行了封装,会把搜索的结果保存下来 参数key为保存名,第二个参数为模块名,第三个参数为搜索方法search或find,第四个参数为搜索起点是否为根模块。 后面三个参数都有默认值,使用方法类似于C++的变参。如果只有一个参数则会直接取出之前的查找结果并创建一个新的消息。 -* 消息发送 +#### 3.2.2 消息发送 ```go func (m *Message) Cmd(arg ...interface{}) *Message ``` 消息发送给目标模块,调用对应的Command接口。参数为命令行,包括命令名,与命令参数。 -* 消息读写 +#### 3.2.3 消息读写 ```go func (m *Message) Detail(index int, arg ...interface{}) string func (m *Message) Detaili(index int, arg ...int) int @@ -581,6 +621,7 @@ Commands: map[string]*ctx.Command{ 然后调用Cmd()执行命令。命令执行完成后调用Result()取出响应行,调用Append()取出响应头。 在echo命令中,调用Option取出请求头,调用Result设置响应行,调用Append设置响应头。 + ```sh $ go install src/example/bench.go $ bench @@ -590,6 +631,63 @@ demo> send one hello world nice ``` +### 3.3 message消息驱动的事件回调 +每个模块都可以运行自己的协程,有自己的消息队列,从而实现并发。为了更简单的使用并发,对Cmd()封装了一个异步接口Call()。 + +```go +func (m *Message) Call(cb func() bool) +func (m *Message) Back() +``` +Call()发送消息,命令执行完成后会调用参数的中的回调函数。 +在回调函数中,如果返回值为true则代表消息执行完成,会删除回调函数。 +如果返回值为false则代表,消息还未处理完成。在其它事件中调用Back()可以再调用回调函数。 + +```go +"send": &ctx.Command{ + Name: "send module", + Help: "send something", + Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { + m.Sess("sub", arg[0], "find", "false") + + msg := m.Sess("sub") + msg.Detail(0, "echo") + msg.Option("key", "hello") + + msg.Call(func() bool { + if msg.Append("get") == "" { + return false + } + + m.Echo(msg.Result(0)) + m.Echo(" ") + m.Echo(msg.Result(1)) + m.Echo(" ") + m.Echo(msg.Append("hi")) + return true + }) + + time.Sleep(2 * time.Second) + }, +}, +"echo": &ctx.Command{ + Name: "echo word", + Help: "echo something", + Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { + m.Result(0, m.Option("key"), "world") + m.Append("hi", "nice") + go func() { + time.Sleep(time.Second) + m.Append("get", "good") + m.Back() + }() + }, +}, +``` +把send与echo修改为异步方式,在send命令中,调用Call()发送消息,当echo命令执行完成后,会调用回调函数,但msg.Append("get")参数还不存在,所以返回false保留消息。 +1s后,echo命令中的协程会向消息中添加了响应头get,调用Back(),回调函数会被再次调用,此时条件满足,则会向后执行。 +为了看到执行结果,在send命令最后加了两秒的睡眼。 +如果去掉send命令中的睡眠,send命令直接就结束。当回调函数再次被调用时,就看不到执行行结果了。 + ## 4 context核心模块详解 ### 4.0 ctx模块中心 ### 4.1 cli命令中心 diff --git a/src/context/cli/cli.go b/src/context/cli/cli.go index 905b604c..61a142ef 100644 --- a/src/context/cli/cli.go +++ b/src/context/cli/cli.go @@ -38,7 +38,7 @@ func (cli *CLI) check(arg string) bool { // {{{ // }}} -func (cli *CLI) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { +func (cli *CLI) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { // {{{ c.Caches = map[string]*ctx.Cache{} c.Configs = map[string]*ctx.Config{} @@ -51,7 +51,8 @@ func (cli *CLI) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server return s } -func (cli *CLI) Begin(m *ctx.Message, arg ...string) ctx.Server { +// }}} +func (cli *CLI) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{ cli.Caches["level"] = &ctx.Cache{Name: "嵌套层级", Value: "0", Help: "嵌套层级"} cli.Caches["skip"] = &ctx.Cache{Name: "跳过执行", Value: "0", Help: "命令只解析不执行"} cli.Caches["else"] = &ctx.Cache{Name: "解析选择语句", Value: "false", Help: "解析选择语句"} @@ -190,7 +191,8 @@ func (cli *CLI) Begin(m *ctx.Message, arg ...string) ctx.Server { return cli } -func (cli *CLI) Start(m *ctx.Message, arg ...string) bool { +// }}} +func (cli *CLI) Start(m *ctx.Message, arg ...string) bool { // {{{ cli.Caches["#"] = &ctx.Cache{Name: "参数个数", Value: fmt.Sprintf("%d", len(arg)), Help: "参数个数"} for i, v := range arg { cli.Caches[fmt.Sprintf("%d", i)] = &ctx.Cache{Name: "执行参数", Value: v, Help: "执行参数"} @@ -252,7 +254,8 @@ func (cli *CLI) Start(m *ctx.Message, arg ...string) bool { return !cli.Pulse.Has("save") } -func (cli *CLI) Close(m *ctx.Message, arg ...string) bool { +// }}} +func (cli *CLI) Close(m *ctx.Message, arg ...string) bool { // {{{ switch cli.Context { case m.Target(): m.Echo(cli.nfs.Cap("return")) @@ -267,6 +270,8 @@ func (cli *CLI) Close(m *ctx.Message, arg ...string) bool { return true } +// }}} + var Pulse *ctx.Message var Index = &ctx.Context{Name: "cli", Help: "管理中心", Caches: map[string]*ctx.Cache{}, @@ -576,26 +581,26 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心", } // }}} }}, "source": &ctx.Command{Name: "source file", Help: "运行脚本, file: 脚本文件名", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if _, ok := m.Target().Server.(*CLI); m.Assert(ok) && !m.Caps("skip") { + if _, ok := m.Target().Server.(*CLI); m.Assert(ok) && !m.Caps("skip") { // {{{ m.Start(fmt.Sprintf("%s%d", key, m.Optioni("level", m.Capi("level")+1)), "脚本文件", arg[0]) - } + } // }}} }}, "return": &ctx.Command{Name: "return result...", Help: "结束脚本, rusult: 返回值", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if _, ok := m.Target().Server.(*CLI); m.Assert(ok) && !m.Caps("skip") { + if _, ok := m.Target().Server.(*CLI); m.Assert(ok) && !m.Caps("skip") { // {{{ m.Add("append", "return", arg[1:]...) - } + } // }}} }}, "if": &ctx.Command{Name: "if exp", Help: "条件语句, exp: 表达式", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if cli, ok := m.Target().Server.(*CLI); m.Assert(ok) { + if cli, ok := m.Target().Server.(*CLI); m.Assert(ok) { // {{{ if m.Optioni("skip", 0); m.Caps("skip") || !cli.check(arg[1]) { m.Optioni("skip", m.Capi("skip")+1) } m.Start(fmt.Sprintf("%s%d", key, m.Optioni("level", m.Capi("level")+1)), "条件语句") - } + } // }}} }}, "elif": &ctx.Command{Name: "elif exp", Help: "条件语句, exp: 表达式", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if cli, ok := m.Target().Server.(*CLI); m.Assert(ok) { + if cli, ok := m.Target().Server.(*CLI); m.Assert(ok) { // {{{ if !m.Caps("else") { m.Caps("skip", true) return @@ -608,15 +613,15 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心", } m.Caps("else", m.Caps("skip", !cli.check(arg[1]))) - } + } // }}} }}, "else": &ctx.Command{Name: "else", Help: "条件语句", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if _, ok := m.Target().Server.(*CLI); m.Assert(ok) { + if _, ok := m.Target().Server.(*CLI); m.Assert(ok) { // {{{ m.Caps("skip", !m.Caps("else")) - } + } // }}} }}, "end": &ctx.Command{Name: "end", Help: "结束语句", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if cli, ok := m.Target().Server.(*CLI); m.Assert(ok) { + if cli, ok := m.Target().Server.(*CLI); m.Assert(ok) { // {{{ if m.Capi("fork") != -2 { m.Spawn(cli.nfs.Target()).Cmd("copy", cli.Name, m.Cap("fork"), m.Option("pos")) } @@ -627,10 +632,10 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心", } else { m.Put("append", "cli", cli.Context.Context()) } - } + } // }}} }}, "for": &ctx.Command{Name: "for exp", Help: "循环语句, exp: 表达式", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if cli, ok := m.Target().Server.(*CLI); m.Assert(ok) { + if cli, ok := m.Target().Server.(*CLI); m.Assert(ok) { // {{{ if m.Capi("loop") != -2 && m.Capi("loop") == m.Optioni("pos")-1 { m.Caps("skip", !cli.check(arg[1])) return @@ -641,18 +646,18 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心", } m.Optioni("loop", m.Optioni("pos")-1) m.Start(fmt.Sprintf("%s%d", key, m.Optioni("level", m.Capi("level")+1)), "循环语句") - } + } // }}} }}, "function": &ctx.Command{Name: "function name", Help: "函数定义, name: 函数名", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if _, ok := m.Target().Server.(*CLI); m.Assert(ok) { + if _, ok := m.Target().Server.(*CLI); m.Assert(ok) { // {{{ m.Optioni("fork", m.Optioni("pos")+1) m.Optioni("skip", m.Capi("skip")+1) m.Start(arg[1], "循环语句") - } + } // }}} }}, "call": &ctx.Command{Name: "call name arg...", Help: "函数调用, name: 函数名, arg: 参数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - fun := m.Find("nfs.file1." + arg[0]) - fun.Target().Start(fun) + fun := m.Find("nfs.file1." + arg[0]) // {{{ + fun.Target().Start(fun) // }}} }}, }, } diff --git a/src/context/ctx.go b/src/context/ctx.go index d81902fa..20ef4017 100644 --- a/src/context/ctx.go +++ b/src/context/ctx.go @@ -470,6 +470,9 @@ type Message struct { target *Context Index int + ncallback int + callback func() bool + Template *Message } @@ -820,12 +823,6 @@ func (m *Message) Find(name string, root ...bool) *Message { // {{{ } // }}} -func (m *Message) Start(name string, help string, arg ...string) bool { // {{{ - return m.Set("detail", arg...).target.Spawn(m, name, help).Begin(m).Start(m) -} - -// }}} - func (m *Message) Sess(key string, arg ...string) *Message { // {{{ if len(arg) > 0 { root := true @@ -856,6 +853,12 @@ func (m *Message) Sess(key string, arg ...string) *Message { // {{{ // }}} +func (m *Message) Start(name string, help string, arg ...string) bool { // {{{ + return m.Set("detail", arg...).target.Spawn(m, name, help).Begin(m).Start(m) +} + +// }}} + func (m *Message) Add(meta string, key string, value ...string) *Message { // {{{ if m.Meta == nil { m.Meta = make(map[string][]string) @@ -1211,6 +1214,8 @@ func (m *Message) Exec(key string, arg ...string) string { // {{{ m.target.Historys = make([]*Message, 0, 10) } m.target.Historys = append(m.target.Historys, m) + + m.Back() }) return m.Get("result") @@ -1292,6 +1297,31 @@ func (m *Message) Cmd(arg ...interface{}) *Message { // {{{ return m } +// }}} +func (m *Message) Call(cb func() bool) { // {{{ + m.callback = cb + m.message.ncallback++ + + m.Wait = nil + m.Cmd() +} + +// }}} +func (m *Message) Back() { // {{{ + if m.callback == nil { + return + } + + if m.callback() { + m.callback = nil + m.message.ncallback-- + } + + if m.message.ncallback == 0 { + m.message.Back() + } +} + // }}} func (m *Message) Confs(key string, arg ...bool) bool { // {{{ diff --git a/src/context/nfs/nfs.go b/src/context/nfs/nfs.go index 91e73156..11190275 100644 --- a/src/context/nfs/nfs.go +++ b/src/context/nfs/nfs.go @@ -1,6 +1,6 @@ -package nfs - -import ( +package nfs // {{{ +// }}} +import ( // {{{ "context" "bufio" @@ -13,6 +13,8 @@ import ( "strings" ) +// }}} + type NFS struct { io io.ReadWriteCloser *bufio.Reader @@ -26,7 +28,7 @@ type NFS struct { *ctx.Context } -func (nfs *NFS) print(str string, arg ...interface{}) bool { +func (nfs *NFS) print(str string, arg ...interface{}) bool { // {{{ switch { case nfs.io != nil: fmt.Fprintf(nfs.io, str, arg...) @@ -38,7 +40,9 @@ func (nfs *NFS) print(str string, arg ...interface{}) bool { return true } -func (nfs *NFS) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { +// }}} + +func (nfs *NFS) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { // {{{ c.Caches = map[string]*ctx.Cache{ "pos": &ctx.Cache{Name: "读写位置", Value: "0", Help: "读写位置"}, "nline": &ctx.Cache{Name: "缓存命令行数", Value: "0", Help: "缓存命令行数"}, @@ -67,7 +71,8 @@ func (nfs *NFS) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server } -func (nfs *NFS) Begin(m *ctx.Message, arg ...string) ctx.Server { +// }}} +func (nfs *NFS) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{ nfs.Context.Master(nil) if nfs.Context == Index { Pulse = m @@ -75,7 +80,8 @@ func (nfs *NFS) Begin(m *ctx.Message, arg ...string) ctx.Server { return nfs } -func (nfs *NFS) Start(m *ctx.Message, arg ...string) bool { +// }}} +func (nfs *NFS) Start(m *ctx.Message, arg ...string) bool { // {{{ if socket, ok := m.Data["io"]; ok { nfs.io = socket.(io.ReadWriteCloser) nfs.Reader = bufio.NewReader(nfs.io) @@ -224,7 +230,8 @@ out: return false } -func (nfs *NFS) Close(m *ctx.Message, arg ...string) bool { +// }}} +func (nfs *NFS) Close(m *ctx.Message, arg ...string) bool { // {{{ switch nfs.Context { case m.Target(): if nfs.in != nil { @@ -237,6 +244,8 @@ func (nfs *NFS) Close(m *ctx.Message, arg ...string) bool { return true } +// }}} + var Pulse *ctx.Message var Index = &ctx.Context{Name: "nfs", Help: "存储中心", Caches: map[string]*ctx.Cache{ @@ -304,7 +313,7 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", // }}} }}, "send": &ctx.Command{Name: "send [file] args...", Help: "连接文件服务, args: 参考tcp模块, dial命令的参数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) { + if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) { // {{{ if m.Has("nrecv") { if len(arg) > 1 && arg[0] == "file" { info, e := os.Stat(arg[1]) @@ -361,10 +370,10 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", m.Recv = make(chan bool) <-m.Recv } - } + } // }}} }}, "recv": &ctx.Command{Name: "recv [file] args...", Help: "连接文件服务, args: 参考tcp模块, dial命令的参数", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) { + if nfs, ok := m.Target().Server.(*NFS); m.Assert(ok) { // {{{ if m.Has("nrecv") { if len(arg) > 1 && arg[0] == "file" { f, e := os.Create(arg[1]) @@ -404,7 +413,7 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", m.Recv = make(chan bool) <-m.Recv - } + } // }}} }}, "open": &ctx.Command{Name: "open file", Help: "打开文件, file: 文件名", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { if m.Has("io") { // {{{ @@ -500,9 +509,9 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", }}, "pwd": &ctx.Command{Name: "pwd", Help: "写入文件, string: 写入内容, pos: 写入位置", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - wd, e := os.Getwd() + wd, e := os.Getwd() // {{{ m.Assert(e) - m.Echo(wd) + m.Echo(wd) // }}} }}, }, Index: map[string]*ctx.Context{