IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

neovimの導入

neovimbrewでインストールします。

https://github.com/neovim/neovim

foo$ brew install neovim/neovim/neovim
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core, homebrew/php).
==> New Formulae
alexjs                        dmtx-utils                    gtef                          httpflow                      llvm@3.9                      teleport

...

==> Renamed Formulae
bash-completion2 -> bash-completion@2                       gsl1 -> gsl@1                                               percona-server56 -> percona-server@5.6

...

==> Tapping neovim/neovim
Cloning into '/usr/local/Homebrew/Library/Taps/neovim/homebrew-neovim'...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.
Checking connectivity... done.
Tapped 1 formula (32 files, 35.2KB)
==> Installing neovim from neovim/neovim
==> Installing dependencies for neovim/neovim/neovim: cmake, libtool, autoconf, automake, pkg-config, jemalloc, libuv, msgpack, unibilium, libtermkey, libvterm
==> Installing neovim/neovim/neovim dependency: cmake
==> Downloading https://homebrew.bintray.com/bottles/cmake-3.7.2.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring cmake-3.7.2.yosemite.bottle.tar.gz
==> Caveats
Emacs Lisp files have been installed to:
  /usr/local/share/emacs/site-lisp/cmake
==> Summary
🍺  /usr/local/Cellar/cmake/3.7.2: 2,143 files, 29.3MB
==> Installing neovim/neovim/neovim dependency: libtool
==> Downloading https://homebrew.bintray.com/bottles/libtool-2.4.6_1.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libtool-2.4.6_1.yosemite.bottle.tar.gz
==> Caveats
In order to prevent conflicts with Apple's own libtool we have prepended a "g"
so, you have instead: glibtool and glibtoolize.
==> Summary
🍺  /usr/local/Cellar/libtool/2.4.6_1: 70 files, 3.7MB
==> Installing neovim/neovim/neovim dependency: autoconf
==> Downloading https://homebrew.bintray.com/bottles/autoconf-2.69.yosemite.bottle.4.tar.gz
######################################################################## 100.0%
==> Pouring autoconf-2.69.yosemite.bottle.4.tar.gz
==> Caveats
Emacs Lisp files have been installed to:
  /usr/local/share/emacs/site-lisp/autoconf
==> Summary
🍺  /usr/local/Cellar/autoconf/2.69: 70 files, 3.0MB
==> Installing neovim/neovim/neovim dependency: automake
==> Downloading https://homebrew.bintray.com/bottles/automake-1.15.yosemite.bottle.2.tar.gz
######################################################################## 100.0%
==> Pouring automake-1.15.yosemite.bottle.2.tar.gz
🍺  /usr/local/Cellar/automake/1.15: 130 files, 2.9MB
==> Installing neovim/neovim/neovim dependency: pkg-config
==> Downloading https://homebrew.bintray.com/bottles/pkg-config-0.29.2.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring pkg-config-0.29.2.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/pkg-config/0.29.2: 11 files, 631KB
==> Installing neovim/neovim/neovim dependency: jemalloc
==> Downloading https://homebrew.bintray.com/bottles/jemalloc-4.5.0.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring jemalloc-4.5.0.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/jemalloc/4.5.0: 16 files, 1.4MB
==> Installing neovim/neovim/neovim dependency: libuv
==> Downloading https://homebrew.bintray.com/bottles/libuv-1.11.0.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libuv-1.11.0.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/libuv/1.11.0: 56 files, 2.3MB
==> Installing neovim/neovim/neovim dependency: msgpack
==> Downloading https://homebrew.bintray.com/bottles/msgpack-2.1.1.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring msgpack-2.1.1.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/msgpack/2.1.1: 662 files, 5MB
==> Installing neovim/neovim/neovim dependency: unibilium
==> Downloading https://homebrew.bintray.com/bottles/unibilium-1.2.0.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring unibilium-1.2.0.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/unibilium/1.2.0: 62 files, 271.3KB
==> Installing neovim/neovim/neovim dependency: libtermkey
==> Downloading https://homebrew.bintray.com/bottles/libtermkey-0.19.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libtermkey-0.19.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/libtermkey/0.19: 32 files, 103.8KB
==> Installing neovim/neovim/neovim dependency: libvterm
==> Downloading https://homebrew.bintray.com/bottles/libvterm-681.yosemite.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libvterm-681.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/libvterm/681: 11 files, 167.8KB
==> Installing neovim/neovim/neovim 
==> Downloading https://github.com/neovim/neovim/archive/v0.1.7.tar.gz
==> Downloading from https://codeload.github.com/neovim/neovim/tar.gz/v0.1.7
######################################################################## 100.0%
==> Downloading https://github.com/luvit/luv/archive/146f1ce4c08c3b67f604c9ee1e124b1cf5c15cf3.tar.gz
==> Downloading from https://codeload.github.com/luvit/luv/tar.gz/146f1ce4c08c3b67f604c9ee1e124b1cf5c15cf3
######################################################################## 100.0%
==> Downloading https://raw.githubusercontent.com/neovim/deps/master/opt/LuaJIT-2.0.4.tar.gz
######################################################################## 100.0%
==> Downloading https://github.com/keplerproject/luarocks/archive/5d8a16526573b36d5b22aa74866120c998466697.tar.gz
==> Downloading from https://codeload.github.com/luarocks/luarocks/tar.gz/5d8a16526573b36d5b22aa74866120c998466697
######################################################################## 100.0%
==> Building third-party dependencies.
==> cmake ../third-party -DUSE_BUNDLED_BUSTED=OFF -DUSE_BUNDLED_GPERF=OFF -DUSE_BUNDLED_LIBUV=OFF -DUSE_BUNDLED_MSGPACK=OFF -DUSE_BUNDLED_UNIBILIUM=OFF -DUSE_BUNDLED_LIBTERMKEY=O
==> make VERBOSE=1
==> Building Neovim.
==> cmake .. -DCMAKE_C_FLAGS_RELEASE=-DNDEBUG -DCMAKE_CXX_FLAGS_RELEASE=-DNDEBUG -DCMAKE_INSTALL_PREFIX=/usr/local/Cellar/neovim/0.1.7 -DCMAKE_BUILD_TYPE=Release -DCMAKE_FIND_FRA
==> make VERBOSE=1 install
==> Caveats
To run Neovim, use the "nvim" command (not "neovim"):
    nvim

After installing or upgrading, run the "CheckHealth" command:
    :CheckHealth

To use your existing Vim configuration:
    ln -s ~/.vim ~/.config/nvim
    ln -s ~/.vimrc ~/.config/nvim/init.vim
See ':help nvim' for more information.

Breaking changes (if any) are documented at:
    https://github.com/neovim/neovim/wiki/Following-HEAD

For other questions:
    https://github.com/neovim/neovim/wiki/FAQ
==> Summary
🍺  /usr/local/Cellar/neovim/0.1.7: 1,333 files, 16.9MB, built in 2 minutes 34 seconds
foo$ nvim
foo$ brew install neovim/neovim/neovim

設定ファイルの場所

~/.config/nvim/init.vim

記載内容は下記を参考にしてdeinを導入します。

qiita.com

導入するプラグインは下記に記述します。

/Users/foo/.dein.toml

[[plugins]]
repo = 'Shougo/dein.vim'

[[plugins]]
repo = 'Shougo/vimproc.vim'
build = 'make'

[[plugins]]
repo = 'Shougo/neosnippet-snippets'

/Users/foo/.dein_lazy.toml

[[plugins]]
repo = 'Shougo/neomru.vim'
on_path = '.*'

[[plugins]]
repo = 'jiangmiao/auto-pairs'
on_i = 1

[[plugins]]
repo = 'Shougo/neoyank.vim'
on_path = '.*'
on_i = 1

[[plugins]]
repo = 'Shougo/neosnippet'
depends = ['neosnippet-snippets']
on_i = 1
on_ft = ['snippet']

[[plugins]]
repo = 'Shougo/neocomplete'
if = 'has("lua")'
on_i = 1

[[plugins]]
repo = 'Shougo/deoplete.nvim'
if = 'has("nvim")'
on_i = 1

[[plugins]]
repo = 'Shougo/unite.vim'
depends = 'neomru.vim'

# [[plugins]]
# repo  = 'begriffs/haskell-vim-now'
# on_ft = ['haskell']

# [[plugins]]
# repo = 'neovimhaskell/haskell-vim'
# on_ft = ['haskell']

[[plugins]]
repo  = 'eagletmt/neco-ghc'
on_ft = ['haskell']

[[plugins]]
repo  = 'eagletmt/ghcmod-vim'
on_ft = ['haskell']

[[plugins]]
repo = 'scrooloose/nerdtree'

nvimを起動するとプラグインがインストールされます。

foo$ nvim ./servant03/MyWebApp1.hs 
[dein] Not installed plugins: ['ghcmod-vim']
[dein] Update started: (2017/04/02 14:45:54)

[dein] Updated plugins:
[dein]   ghcmod-vim(1 change)
[dein] Done: (2017/04/02 14:45:59)
続けるにはENTERを押すかコマンドを入力してください

インストールしたプラグインcacheに取り込まれます。

foo$ ls ~/.cache/dein/repos/github.com/
Shougo/        altercation/   begriffs/      dag/           eagletmt/
jiangmiao/     morhetz/       nanotech/      neovimhaskell/ scrooloose/
vim-scripts/
foo$ ls ~/.cache/dein/repos/github.com/

react-bootstrap-tableでcolumnが動的の場合のテーブル生成

Reactのテーブルコンポーネントはどれがデファクトなのでしょうか。よくわからなかったので、目についたreact-bootstrap-tableを使ってみました。

allenfang.github.io

ライブラリの取り込み

    <script src="https://unpkg.com/react@15/dist/react.js"></script>
    <script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

要素の作成

    <div id="result"></div>

以下のようなデータをAPIで取得できるとします。

{ 
  colomuns : ["col_a", "col_b", "col_c"]
 .resultSet : [
    {"id" : 1, "col_a" : "val_foo_a", "col_b" : "val_foo_b", "col_c" : "val_foo_c"}
   ,{"id" : 2, "col_a" : "val_bar_a", "col_b" : "val_bar_b", "col_c" : "val_bar_c"}
   ,{"id" : 3, "col_a" : "val_bazz_a", "col_b" : "val_bazz_b", "col_c" : "val_bazz_c"}
  ]
}

ライブラリ自体に動的にカラムを生成する機能はありませんが、下記にある通り、少しコードを書けば実現できます。

github.com

ただし、ここでの、やり取りでは述べられていませんが、Reactの仮想DOMの管理上1つのカラムのデータはユニークである必要があり、そのカラムに対してisKey属性で指定する必要があります。

下記がfetchAPIアクセスして、テーブル反映までのコードになります。id列をユニークキーとしています。

function query() {
  fetch('http://127.0.0.1:8081/query', {
    method : 'POST',
    mode   : 'cors',
    headers: new Headers({"Content-Type" : "application/x-www-form-urlencoded"}),
    body   : 'foo=bar'
  }).then(function(response) {
    response.json().then(
      (json) => {
        ReactDOM.render(
          <BootstrapTable data={json.resultSet} striped hover>
             {json.columns.map(
                (name) => <TableHeaderColumn 
                                 dataField = { name } 
                                 isKey     = { name === "id" } >
                              { name }
                          </TableHeaderColumn>
             )}
          </BootstrapTable>,
          document.getElementById('result')
        ); // render
      }); // then
  }).catch(function(err) {
    console.log(err);
  });
}

ReactでHTMLの共通化

SBAdmin2のダッシュボードページにあるカードのUI部分をReactにて共通化してみました。

startbootstrap.com

ここの部分です。

f:id:naotoogawa:20170326160605p:plain

下記が4枚あるカードのコードになります。これをReactを使って共通化します。背景色と表示内容だけが異なり、タグの構造は全く同じです。

            <div class="row">
                <div class="col-lg-3 col-md-6">
                    <div class="panel panel-primary">
                        <div class="panel-heading">
                            <div class="row">
                                <div class="col-xs-3">
                                    <i class="fa fa-comments fa-5x"></i>
                                </div>
                                <div class="col-xs-9 text-right">
                                    <div class="huge">26</div>
                                    <div>New Comments!</div>
                                </div>
                            </div>
                        </div>
                        <a href="#">
                            <div class="panel-footer">
                                <span class="pull-left">View Details</span>
                                <span class="pull-right"><i class="fa fa-arrow-circle-right"></i></span>
                                <div class="clearfix"></div>
                            </div>
                        </a>
                    </div>
                </div>
                <div class="col-lg-3 col-md-6">
                    <div class="panel panel-green">
                        <div class="panel-heading">
                            <div class="row">
                                <div class="col-xs-3">
                                    <i class="fa fa-tasks fa-5x"></i>
                                </div>
                                <div class="col-xs-9 text-right">
                                    <div class="huge">12</div>
                                    <div>New Tasks!</div>
                                </div>
                            </div>
                        </div>
                        <a href="#">
                            <div class="panel-footer">
                                <span class="pull-left">View Details</span>
                                <span class="pull-right"><i class="fa fa-arrow-circle-right"></i></span>
                                <div class="clearfix"></div>
                            </div>
                        </a>
                    </div>
                </div>
                <div class="col-lg-3 col-md-6">
                    <div class="panel panel-yellow">
                        <div class="panel-heading">
                            <div class="row">
                                <div class="col-xs-3">
                                    <i class="fa fa-shopping-cart fa-5x"></i>
                                </div>
                                <div class="col-xs-9 text-right">
                                    <div class="huge">124</div>
                                    <div>New Orders!</div>
                                </div>
                            </div>
                        </div>
                        <a href="#">
                            <div class="panel-footer">
                                <span class="pull-left">View Details</span>
                                <span class="pull-right"><i class="fa fa-arrow-circle-right"></i></span>
                                <div class="clearfix"></div>
                            </div>
                        </a>
                    </div>
                </div>
                <div class="col-lg-3 col-md-6">
                    <div class="panel panel-red">
                        <div class="panel-heading">
                            <div class="row">
                                <div class="col-xs-3">
                                    <i class="fa fa-support fa-5x"></i>
                                </div>
                                <div class="col-xs-9 text-right">
                                    <div class="huge">13</div>
                                    <div>Support Tickets!</div>
                                </div>
                            </div>
                        </div>
                        <a href="#">
                            <div class="panel-footer">
                                <span class="pull-left">View Details</span>
                                <span class="pull-right"><i class="fa fa-arrow-circle-right"></i></span>
                                <div class="clearfix"></div>
                            </div>
                        </a>
                    </div>
                </div>
            </div>

まず、ライブラリを取り込みます。

    <!-- React.js -->
    <script src="https://unpkg.com/react@15/dist/react.js"></script>
    <script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

カードのHTML部分はdiv要素のみにします。

            <div class="row">
                <div id="card1"></div>
                <div id="card2"></div>
                <div id="card3"></div>
                <div id="card4"></div>
            </div>

カードのコードはJSXで書き直します。class属性はclassName属性に書き換えます。そして、カードごとに異なる部分はパラメータとします。{}で囲った部分に値を埋め込みます。

function getCard(prop) {
return <div className="col-lg-3 col-md-6">
    <div className={prop.mainStyle}>
        <div className="panel-heading">
            <div className="row">
                <div className="col-xs-3">
                    <i className="fa fa-comments fa-5x"></i>
                </div>
                <div className="col-xs-9 text-right">
                    <div className="huge">{prop.num}</div>
                    <div>{prop.text}</div>
                </div>
            </div>
        </div>
        <a href="#">
            <div className="panel-footer">
                <span className="pull-left">View Details</span>
                <span className="pull-right"><i className="fa fa-arrow-circle-right"></i></span>
                <div className="clearfix"></div>
            </div>
        </a>
    </div>
</div>;
}

共通化したコードに実引数を与えて、先ほど定義したdiv要素と関連付けさせます。

ReactDOM.render(
  getCard({num : 26, text : "New Comments!", mainStyle : "panel panel-primary"}),
  document.getElementById('card1')
)
ReactDOM.render(
  getCard({num : 12, text : "New Tasks!", mainStyle : "panel panel-green"}),
  document.getElementById('card2')
)
ReactDOM.render(
  getCard({num : 124, text : "New Orders!", mainStyle : "panel panel-yellow"}),
  document.getElementById('card3')
)
ReactDOM.render(
  getCard({num : 13, text : "Support Tickets!", mainStyle : "panel panel-red"}),
  document.getElementById('card4')
)

Fluxとしてイベントやデータバインディングの実装をしないで、単なるUIの共通化として利用するのもありかと思いました。

ServantのCSVファイルダウンロードのサンプル

Servant CSV download sample

$ curl -i http://127.0.0.1:8081/csv/UNdata_Export_pork
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Mon, 20 Jun 2016 16:12:16 GMT
Server: Warp/3.2.9
Content-Type: application/csv

"Country or Area","Year","Unit","Value","Value Footnotes"
"Albania","2013","Mil. USD","3.86207176515949",""
"Albania","2013","Thousand metric tons","0.8",""
"Albania","2012","Mil. USD","3.17974879791774",""
"Albania","2012","Thousand metric tons","0.8",""

HaskellのCSV読み込み

利用ライブラリ

Text.CSV

インポート

Prelude> import Text.CSV

1行読み込み

Prelude Text.CSV> parseCSVTest "aaa,bbb"
[["aaa","bbb"]]
Prelude Text.CSV> parseCSVTest "aaa,bbb,ccc"
[["aaa","bbb","ccc"]]

トリムはしない

Prelude Text.CSV> parseCSVTest "aaa,bbb,  ccc"
[["aaa","bbb","  ccc"]]

複数行読み込み

Prelude Text.CSV> parseCSVTest "aaa,bbb,  ccc\naaa,"
[["aaa","bbb","  ccc"],["aaa",""]]
Prelude Text.CSV> parseCSVTest "aaa,bbb,  ccc\naaa,,bbb"
[["aaa","bbb","  ccc"],["aaa","","bbb"]]

出力

Prelude Text.CSV> printCSV [["1","2","3"],["4","5"],["6"]]
"\"1\",\"2\",\"3\"\n\"4\",\"5\"\n\"6\""

読み込み件数取得

Prelude Text.CSV> parseCSVFromFile "./data/UNdata_Export_pork.csv" >>= \x ->  either (\x -> return 0) (return . length) x
1376

10行目出力

Prelude Text.CSV> parseCSVFromFile "./data/UNdata_Export_pork.csv" >>= \x -> return $ either (\x -> []) (\x -> x !! 10) x
["Albania","2009","Thousand metric tons","0.63",""]

全件出力

Prelude Text.CSV> either (\x -> putStrLn "error") (mapM_ (putStrLn . show)) =<< parseCSVFromFile "./data/UNdata_Export_pork.csv"
["Country or Area","Year","Unit","Value","Value Footnotes"]
["Albania","2013","Mil. USD","3.86207176515949",""]
["Albania","2013","Thousand metric tons","0.8",""]

ServantのFileUploadサンプル

package

servant-multipart: multipart/form-data (e.g file upload) support for servant

$ cabal install servant-multipart 
Resolving dependencies...
Notice: installing into a sandbox located at
foo/.cabal-sandbox
Downloading http-client-0.5.6.1...
Configuring natural-transformation-0.4...
Configuring http-client-0.5.6.1...
Building natural-transformation-0.4...

...

sample

servant file upload sample

client

$ curl -i -X PUT http://127.0.0.1:8081/file -F "upfile1=@MyWebApp_upload.hs" -F "xx=yy"
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Tue, 14 Mar 2017 12:03:05 GMT
Server: Warp/3.2.9
Content-Type: application/json;charset=utf-8

{"msg":"\"upfile doesn't exist\"","len":-1}
$
$ curl -i -X PUT http://127.0.0.1:8081/file -F "upfile=@MyWebApp_upload.hs" -F "xx=yy"
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Tue, 14 Mar 2017 12:03:13 GMT
Server: Warp/3.2.9
Content-Type: application/json;charset=utf-8

{"msg":"uploaded","len":1633}
$ ls -al MyWebApp_upload.hs 
-rw-r--r--  1 foo bar  1633  3 4 11:01 MyWebApp_upload.hs
$ 

server log

*Main> main
FileData {fdInputName = "upfile1", fdFileName = "MyWebApp_upload.hs", fdFileCType = "application/octet-stream", fdFilePath = "/var/folders/hw/p4bp49hd7v9_1j60sjvmhqnr0000gn/T/servant-multipart1974930991404280278.buf"}
not exist
FileData {fdInputName = "upfile", fdFileName = "MyWebApp_upload.hs", fdFileCType = "application/octet-stream", fdFilePath = "/var/folders/hw/p4bp49hd7v9_1j60sjvmhqnr0000gn/T/servant-multipart8933518161505795335.buf"}