因为各种原因吧…

  • 很早在 ECUG 就接触了 Erlang
    • 但是, 怪异的语法,以及狭小的应用领域,一直没真正使用起来
    • 现在, 发现 Elixir 很好的解决了 OTP 生态的通用应用问题
  • 所以, 开始折腾…
  • 结果, 撞见各种值得嗯哼的事儿…

env.

༄ screenfetch

                 -/+:.          zoomq@ZQ160626rMBP
                :++++.          OS: 64bit Mac OS X 10.12.6 16G2136
               /+++/.           Kernel: x86_64 Darwin 16.7.0
       .:-::- .+/:-``.::-       Uptime: 30d 12h 38m
    .:/++++++/::::/++++++/:`    Packages: 241
  .:///////////////////////:`   Shell: bash
  ////////////////////////`     Resolution: 2560x1440
 -+++++++++++++++++++++++`      DE: Aqua
 /++++++++++++++++++++++/       WM: Quartz Compositor
 /sssssssssssssssssssssss.      WM Theme: Blue
 :ssssssssssssssssssssssss-     CPU: Intel Core i7-5557U @ 3.10GHz
  osssssssssssssssssssssssso/`  GPU: Intel Iris Graphics 6100
  `syyyyyyyyyyyyyyyyyyyyyyyy+`  RAM: 9318MiB / 16384MiB
   `ossssssssssssssssssssss/
     :ooooooooooooooooooo+.
      `:+oo+/:-..-:/+o+/-
  • Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]
  • Elixir 1.9.1 (compiled with Erlang/OTP 20)

problem

使用 Plug 快速开发 Slack 插件…

首先得完成基于 Plug 的 API 服务, 这其中最关键的是从 GET/POST 协议请求中获得 Slack 转发过来的用户指令;

官方文档中 demo 形似:

defmodule Example.Plug.VerifyRequest do
  defmodule IncompleteRequestError do
    @moduledoc """
    Error raised when a required field is missing.
    """

    defexception message: ""
  end

  def init(options), do: options

  def call(%Plug.Conn{request_path: path} = conn, opts) do
    if path in opts[:paths], do: verify_request!(conn.params, opts[:fields])
    conn
  end

  defp verify_request!(params, fields) do
    verified =
      params
      |> Map.keys()
      |> contains_fields?(fields)

    unless verified, do: raise(IncompleteRequestError)
  end

  defp contains_fields?(keys, fields), do: Enum.all?(fields, &(&1 in keys))
end

对应检验请求可以是:

$ curl localhost:8080/upload?content=foo&mimetype=bar

问题是:

...
|> contains_fields?(fields)
  • 提供的函式只是对所有 URI 参数键进行检验, 如果缺少了则报错,
  • 可是, 正确请求时, 如何获得所有参数值?

先使用输出语句来探查 params 是什么数据类型:

IO.inspect(params)

请求触发后终端输出:

%{"cnt" => "foo", "mmt" => "bar"}

是标准 Map 对象.

try

Enum.all?() 是个重要的入口

追查文档, 知道 Enum 有丰富的序列型数据处置支持:

Enum.each 可以针对每个键进行附加处置

所以:

defp echo_vars(keys, fields) do 
    Enum.each(fields, fn(s) -> IO.puts(s) end)
    LOG.info("chk. as #{keys} ;-)")
end

但是, 各种出错, 因为这时, 传入的 keys 是只有请求 Map 中的键;

那么:

Enum.each(params,fn(k) -> IO.puts(Map.get(params,k)) end)
  • 跳出 verified = params … 的处理流水线;
  • 直接追加列印处理?
  • 也不行…
    • 光是看形式就有问题
    • 先对 Map 进行 Enum.each
    • 然后对每一个键, 再进行 Map.get
    • 这简直是对相同的数据进行反复操作

Hummm?!

万万没想到…

无意中在 如何在Elixir中将映射键从字符串转换为原子 | 码农俱乐部 - Golang中国 - Go语言中文社区

看到:

iex(1)> string_key_map = %{"foo" => "bar", "hello" => "world"}

%{“foo” => “bar”, “hello” => “world”}

iex(2)> for {key, val} <- string_key_map, into: %{}, do: {String.to_atom(key), val}
%{foo: "bar", hello: "world"}

虽说这是 Comprehensions 概念的通常使用, 但是, 发现, for 这个操作, 和 Python 中的完全一致哪, 于是:

  defp echo_vars(params) do
    for {key, val} <- params do
      #IO.inspect(key)
      #IO.inspect(val)
      LOG.info("each -> #{key}=>#{val} ;-)")
    end
  end

简直和 Python 中解包行为完全一致…

可是: Learn elixir in Y Minutes 一句也没提及, for 这么 NB 的操作符哪…

refer.

Changelog

  • 200109 .5h 配置模板,重新发布
  • 200108 .5h 输出
  • 200107 2.5h 折腾

Comments


comments powered by Disqus

© Copyright 2014 by Zoom.Quiet
Content licensed under the Creative Commons attribution-noncommercial-sharealike License.
Contact me via , mail ,github or gitlab . Tip me via Buy me a coffeeBuy me a coffee || (feed)