OmniAICreator/Qiita-1.07M huggingface dataset:
Homebrew で入れた Hadoop 3.1.1 で Hadoop Streaming してみる

Homebrew で入れた Hadoop 3.1.1 で Hadoop Streaming しようと思ったときに `hadoop-streaming-3.1.1.jar`のパスが深いところにあって見つかりにくいのでメモ。

```shell

$ hadoop jar /usr/local/Cellar/hadoop/3.1.1/libexec/share/hadoop/tools/lib/hadoop-streaming-3.1.1.jar \
-mapper mapper.py -reducer reducer.py -input sample-text.txt -output out
```
GCPのサービスを使用して冗長化に挑んでいるハナシ

GCPのサービスを技術開発で使用しており、「Cloud DNS、Compute Engine、Kubernetes Engine」のサービスを使用して冗長化に挑んでおります。調べてわかったことと検討していることがありますので情報を発信します。

## 環境の説明

- Cloud DNSは、グローバル ドメイン ネーム システム（DNS）
- Compute Enginは、VMインスタンス
- Kubernetes Engineは、コンテナ化されたアプリケーション管理

Kubernetes Engineでアプリケーションを実行しており、Compute Engineは、Kubernetes Engineへのプロキシとして使用しています。Cloud DNSからCompute Engineを経由して、Kubernetes Engineのアプリケーションへアクセスしています。なお、各サービスへの登録・更新・削除は、シェルクリプトでgcloudコマンドを使用して行なっております。

HTTP（S）Load Balancerを使用せずにCompute Engineをプロキシとして使用している経緯は、アプリケーションでWebSocketを使用しており、[HTTP（S）Load Balancerの仕様でWebSocketのconnection lifetime](https://github.com/kubernetes/ingress-gce/tree/master/examples/websocket)があり、WebSocketの接続が切れると困るため、Compute Engineをプロキシとして使用しています。

## Compute Engineの冗長化設定

Compute Engineは１インスタンスで稼働しており単一障害点となるため、複数台インスタンスを作成してCloud DNSでDNSラウンドロビンの設定を行いました。Cloud DNSのドキュメントには、DNSラウンドロビンの設定方法が載っていないことがわかり、gcloudコマンドでDNSラウンドロビンの設定を行う方法について調べました。

### gcloudコマンドでDNSラウンドロビンの設定を行う方法
- [gcloudのコマンドラインリファレンス](https://cloud.google.com/sdk/gcloud/reference/dns/record-sets/transaction/add)のTXTレコードに複数のデータを登録するコマンドを基にして、以下のコマンドを実行すると、Aレコードに複数台の外部IPアドレスを設定できDNSラウンドロビンの設定を行うことができました。  

```
gcloud dns record-sets transaction add -z=MANAGED_ZONE --name=my.domain. --ttl=1234  --type=A "1.2.3.4" "5.6.7.8"
```

## Kubernetes Engineの冗長化検討

Kubernetes Engineは、ノードバージョンアップやマシンタイプ変更などの計画停止を無停止で行う必要があるため、どのような冗長構成にするか現在検討しております。具体的には、以下のような内容を検討しております。

- アプリケーション用のコンテナを複数作成して冗長化する
  - 構成案１：複数コンテナは、別々のノードに配置して１つのコンテナが停止している場合に片系で動く構成にする
     - クラスタを複数作成して、別々のノードに配置する。もしくは、[本番環境での Kubernetes Engine の準備](https://cloud.google.com/solutions/prep-kubernetes-engine-for-prod?hl=ja)を参考にするとクラスタ1つでマルチゾーン構成にして、別々のノードに配置するという方法でも実現できるかもしれない
  - 構成案２：複数コンテナは、同じノードに配置して順序を保つ構成にする（１つのコンテナが停止して起動まで完了した後に、次のコンテナの停止/起動を行う）

## 最後に

GCPのサービスで冗長構成の環境構築は、さまざまなことを検討する必要があり難しいと感じています。
また、数日前にKubernetesの深刻な脆弱性が発見されて、Kubernetes Engineでノードバージョンアップを実施した際は、[異なるマシンタイプへのワークロードの移行](https://cloud.google.com/kubernetes-engine/docs/tutorials/migrating-node-pool?hl=ja)の手順を参考にアプリケーションを新バージョンのノードに移行する作業を実施して、作業中にアプリケーションが數十分停止したため無停止で行うことの重要性を肌で感じました。

本記事を通して、SRAの社内にGCPのサービスを使用している技術者がいるということを知っていただけると嬉しいと思っております。
Windows10の画面キャプチャができない

皆さんがよく使っているであろうWin10の録画機能
あれ便利ですよね
Win+Gで画面のキャプチャしますかって聞いてくれる

でも突然出てこなくなった時がありましたので復帰方法を書きます

〇手順
1,デスクトップ画面の左下にあるバーのwindowsボタンをクリックします![sfas.PNG](https://qiita-image-store.s3.amazonaws.com/0/262130/059e5edf-3191-b387-e281-5e460cc694c2.png)

↑この四角四つのマークですね


2,次に左側の下から電源マークの上にある歯車のようなマークを押します

そうするとこんな画面になります
![fsadfasdfa.PNG](https://qiita-image-store.s3.amazonaws.com/0/262130/3dca41ea-5579-37c7-002b-15f52599d843.png)

3,右側の上から三番目にある「ゲーム」をクリックします
　こうなります
　![fasdfasdfasdfas.PNG](https://qiita-image-store.s3.amazonaws.com/0/262130/9813b4e1-8d31-2b9a-8272-b1e18017e1b2.png)

4,「ゲームバー」というところののほうに
　「ゲームバーを使ってゲームクリップ、スクリーン、、、する」というところがあるので
　この文字の下にあるボタンをクリックしてオンにします（画像はすでにオンになっています）
　![fasdfasdfasd.PNG](https://qiita-image-store.s3.amazonaws.com/0/262130/e7a2cc2b-9f29-1261-9ca4-7d5c86e1e8dc.png)



以上です
vim . ドットコマンドの使い方


# ドットコマンド
「直前の変更を繰り返す」という強力なコマンド！

## 1文字削除
`x`キーはカーソル位置にある1文字を削除する。
`x`キーのあとに`.`(ドット)コマンドを使うことで、1文字削除を繰り返すことができる。

![bb.gif](https://qiita-image-store.s3.amazonaws.com/0/260131/f3d4e0ee-2658-f9b0-68e0-ea34f1bb6d23.gif)

## 1行削除
`dd`コマンドでカーソルがある行を削除することができる。
`.`コマンドを使うことで、現在の行を削除するコマンドを繰り返す。

![cc.gif](https://qiita-image-store.s3.amazonaws.com/0/260131/cd6466b1-a7b9-22cc-24d2-e1e362cce42b.gif)


## インデント
`>>`コマンドで現在の行のインデントを1段深くする。
`.`コマンドを使うことで、現在の行のインデントを1段深くするコマンドを繰り返す。

![cc.gif](https://qiita-image-store.s3.amazonaws.com/0/260131/eab901e1-2867-e061-8951-a41c25ab7037.gif)


## 行末にセミコロン
`A`キーで行末から挿入モードに入ることができる。
`A;<Esc>`を`.`で繰り返すことによって行のどこにいても行末に`;`を挿入することができる。

![dddd.gif](https://qiita-image-store.s3.amazonaws.com/0/260131/2c4cc662-3e19-6775-5547-81848c47e886.gif)

### 選択範囲全てにドットコマンド

|コマンド|説明|
|---|---|
|`A;<Esc>`|行末にセミコロン|
|`j`|下の行に移動|
|`V`|行選択モードに入る|
|`G`|ファイル末まで選択|
|`:`|コマンドラインモードに入る|
|`normal`|`:[range]normal {commands}`|
|` .`|ドットコマンド|

![fff.gif](https://qiita-image-store.s3.amazonaws.com/0/260131/04f6732a-3cbb-b405-1529-d8b4fa32956b.gif)

上のgifでは1行目に`A;<Esc>`コマンドで行末セミコロンを入力し、
`j`キーで1つ下に移動。

`V`キーで行選択モードに移行。
`G`キーで行末まで選択しています。

vimのvisualモードで範囲を選択中に`:`(コロン)を打つと、コマンドラインモードに移行し、
`normal`コマンドで指定範囲に対して`.`コマンドを実行しています。

`:'<,'>normal .`

`'<,'>`　はvisualモードで選択した範囲。
`normal`　はノーマルコマンドの実行。
`.` はドットコマンド。

`:[range]normal {commands}` という風に使えるみたいです。


## コマンドまとめ
|コマンド|説明|
|---|---|
|.|直前の変更を繰り返す|
|x|カーソル位置にある1文字を削除|
|dd|カーソルがある行を削除すること|
|>>|現在の行のインデントを1段深くする|
|A|行末から挿入モードに入る|
|V|行選択モードに入る|
|:|コマンドラインモードに入る|

# まとめ
まだすごく単純なものにしかドットコマンドを活用できていませんが、
それでもドットコマンドの強力さを感じています。
ドットコマンドを使うことによってキーストロークが1つでも減ると、楽しい気持ちになりますね。



# 参考
[実践Vim 思考のスピードで編集しよう!](https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5Vim-%E6%80%9D%E8%80%83%E3%81%AE%E3%82%B9%E3%83%94%E3%83%BC%E3%83%89%E3%81%A7%E7%B7%A8%E9%9B%86%E3%81%97%E3%82%88%E3%81%86-Drew-Neil/dp/4048916599)
Unity開発するにあたって知っておきたいコンパイラのすゝめ(Part2～IL2CPP編～)

#最初に

この記事はUnityで使用されているコンパイラについてまとめた記事のうちの、C#からC++へコード変換を行う**IL2CPP**について簡単にまとめた記事です。

- [Part0.5〜.NET Frameworkの概要編〜](https://qiita.com/4_mio_11/items/89fd91ef8ede02bfdd46)
- [Part0.7～JITとAOT概要編～](https://qiita.com/4_mio_11/items/f58fedbf2a7721c3df58)
- [Part1～Mono編～](https://qiita.com/4_mio_11/items/716d28add984405a80cc)
- [Part3～Burst編～](https://qiita.com/4_mio_11/items/cda0c8aa4c77a7748418)

## IL2CPPとは？

Unityが開発したコンパイラであり、**ILコードをC++コードに組み立ててくれます**。IL2CPPの使用することによって、パフォーマンスやセキュリティなど向上する可能性があります。IL2CPPならLINQも基本的に使えるようです。

IL2CPPは、

- **AOTコンパイラ**
- VMをサポートするライブラリ

の二つで構成されています。

IL2CPPのAOTコンパイラは**il2cpp.exe**と言う名前になっており、WindowsならEditor\Data\il2cppディレクトリ、MacOSではUnityインストール場所のContents/Frameworks/il2cpp/buildディレクトリ下に格納されています。il2cpp.exeはC＃で作成されており、.NET及びMonoコンパイラを使用してコンパイルされます。

## IL2CPPを使用した場合の流れ

(Unity公式チュートリアルから)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/36899/9cb0be5b-0ecd-9147-56fc-bba0291f2432.png)

1. 最初にC#で書かれたゲームロジックのコードを.NETによってILコードにコンパイルします。
2. プラグインやBCLは「Unused Bytecode Stripper」と言うUnityのツールによって、DLLから使われていないクラスとメソッドを見つけ削除します。これによってコードのサイズを小さくすることができます。
3. その後、ILコードをil2cpp.exeを使用してC++コードに変換します。
4. 変換されたC++コードとIL2CPPのランタイムはネイティブのプラットフォームコンパイラによってコンパイルされます。
5. 最後にコードは対象プラットフォームの実行ファイルまたはDLLのいずれかにリンクされます。


## Monoと比較して

Monoのデメリットとして、

- C/C＋＋に速度などのランタイム性能が追いつかない。
- 23個のプラットフォームとアーキテクチャの保守などに手間がかかっている。
- GC中にアプリがフリーズされる可能性あり。

IL2CPPを使うことでC++の性能とC#の生産性を兼ね備えることができます。ただしビルド時間はMonoよりも長くなります。また、Monoとは異なり、AOTコンパイルのみサポートしています。

## ビルド時間の削減方法

- インクリメントビルドの使用。
- マルウェア対策のソフトウェアを無効にする。
- プロジェクトとBuildフォルダをSSD下に格納。


## IL2CPPの使用方法

ターゲットプラットフォームのPlayerSettingsウィンドウからConfigurationセクションのScripting BackendにIL2CPPを選択しましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/36899/a8a9a2e3-db86-b129-d741-1faff3a31f33.png)






# 参考
- [IL2CPP(Unity公式)](https://docs.unity3d.com/ja/2017.4/Manual/IL2CPP.html)
- [IL2CPPに関する軽い話](https://www.slideshare.net/WooramYang/il2-cpp-wooramyang-49666751)
- [Введение в IL2CPP](https://habr.com/company/plarium/blog/276589/)
- [Unityと.NET](https://www.slideshare.net/AimingStudy/unitynet)
【Ethereum】transactionHashからcontractAddressを調べる方法

コントラクトのアドレスは、トランザクションレシートに記載されています。
JSON-RPCで`eth_getTransactionReceipt`を呼び出すことで、トランザクションレシートを確認できます。

```:JSON-RPC　リクエスト例
{
  "jsonrpc" : "2.0",
  "method" : "eth_getTransactionReceipt",
  "params" : [
    "トランザクションハッシュ"
  ],
  "id" : 1
}
```

```:JSON-RPC　レスポンス例
{
  "jsonrpc" : "2.0",
  "id" : 1,
  "result" : {
    "blockHash" : "...",
    "blockNumber" : "...",
    "contractAddress" : "コントラクトのアドレス",
    "cumulativeGasUsed": "...",
    "from": "...",
    "gasUsed": "...",
    "logs":[],
    "logsBloom": "...",
    "root": "...",
    "to": null,
    "transactionHash": "...",
    "transactionIndex": "..."
  }
}
```
ImageMagick の convert オプションが覚えられないのでシミュレータを JavaScript で作った


ImageMagick の convert コマンドは何十回やっても覚えられないので、動作を確認しながらコマンドオプションを生成するシミュレータを作ってみました。なるべく楽をしたかったのでブラウザで動くように ImageMagick を WASM ( WebAssembly ) で動かしました。

- https://convert-web-simulator.firebaseapp.com/

![demo.png](https://qiita-image-store.s3.amazonaws.com/0/2152/9247e167-8467-649f-ceae-30e419848070.png "demo.png")

## 仕組み

WebAssembly というブラウザ上でバイナリ実行できる仕組みがあるのでそれを利用しています。すでに ImageMagick を WebAssembly 対応にしてくれている素晴らしいものがあるので、今回はそちらを利用しました。

- https://github.com/KnicKnic/WASM-ImageMagick

```javascript
import { call } from 'wasm-imagemagick'

const fetchedSourceImage = await fetch("assets/rotate.png")
const content = new Uint8Array(await fetchedSourceImage.arrayBuffer());
const image = { name: 'srcFile.png', content }

const command = ["convert", "srcFile.png", '-rotate', '90', '-resize', '200%', 'out.png']
const result = await call([image], command)
```

たったのこれだけで使うことができます。

WebAssembly を使えば立地でパフォーマンスの高いオペレーションをブラウザ上で実現できそうですね。素晴らしい。

ソースコードはこちらに公開しているので、もし追加して欲しいオプションなどあればプルリクください。

- https://github.com/zaru/convert-imagemagick-web-simulator
obnizオブナイズで、サーボモーターを動かした

先日、SIerIoTLTでのLTで、朝目覚めるためのハックを発表しました、ななみんです。
http://edge.tsumug.com/entry/iot-home-hack-04

その後のクイズ大会で、なんと、obnizが当たったのです！
実はそれまでobnizを知らなかったのですが、その場で検索してみて「なんか良さそう」と思ったので景品はこれを選んでしまいました！！

そんなきっかけで手にしたobnizさんなのですが、使ってみると、すげーいいじゃん！ということで、もう１個自腹で買いました。
まだ、obnizを開封してからまだ１週間も経っておりませんが、初心者でもここまで出来たよということで、進捗報告をさせていただきたいと思います。

そして実は、このエントリーがななみんの初Qiita投稿です。お手柔らかにお願いしますっ。

#obniz開封

![47297792_496932537462951_6181246284486148096_n.jpg](https://qiita-image-store.s3.amazonaws.com/0/321718/ef2dbc73-b437-aa62-baaa-87d71c65f36e.jpeg)
![47208570_2276581242394221_3835072505865830400_n.jpg](https://qiita-image-store.s3.amazonaws.com/0/321718/9fae9b07-fd24-dbfc-83f8-e6044d649ee8.jpeg)






#まずはLチカ
サンプル通りに動かしたので、コードは割愛

![47386194_272792686771596_8265506506355507200_n.jpg](https://qiita-image-store.s3.amazonaws.com/0/321718/d009fcac-c042-44c5-bfdd-12ca824dc4e6.jpeg)



#赤外線センサで距離を測る
距離センサHC-SR04が家にあったので、理由はなく距離を測ってみたり。
（写真撮り忘れた…）

#サーボモーターを動かしたい
さて準備運動？を終え、本題のサーボモーターですが、obnizを手にしてから、扇風機的なものを作って遠隔でねこをあやしたり、複数の扇風機を家で自在に操る制御ができたら便利かなと思い、まずは実験することにしました。

皆さんご存知かと思いますが、サーボモーターもサンプルコードがあります。


#サーボモーターの二段重ね！行くぞ！
サーボモーターが動かせたので、調子に乗って、二段重ねにしてみましたw。
やはり３次元の制御が出来た方が良いですよね。

付属品の土台にぴったりな、3M社のシールがたまたま家にあったので、アルコール（手ピカジェル）で拭いてきれいに貼って固定しました。


#プログラム
サンプルコードになっている、スライダーでの制御が便利だったので、２つのスライダーでそれぞれ土台と上のサーボモーターの角度を制御できるように書きました。


```
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@1.13.1/obniz.js" crossorigin="anonymous"></script>
</head>
<body>

<div id="obniz-debug"></div>
<h1>obniz instant html</h1>
<button id="on">ON</button>
<button id="off">OFF</button>
<div id="print"></div>

<script>

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@1.13.1/obniz.js"></script>
</head>
<body>

<div id="obniz-debug"></div>
<h1>ServoMotor</h1>
<input id="slider1" type="range"  min="0" max="180" />
<input id="slider2" type="range"  min="0" max="180" />

<script>
  
var obniz = new Obniz("ID");
obniz.onconnect = async function () {
  var servo1 = obniz.wired("ServoMotor", {gnd:0, vcc:1, signal:2});
  var servo2 = obniz.wired("ServoMotor", {gnd:3, vcc:4, signal:5});
  $("#slider1").on('input', function() {
    servo1.angle($("#slider1").val())
  });
  $("#slider2").on('input', function() {
    servo2.angle($("#slider2").val())
  });
};

obniz.onclose = async function(){
   $("#slider1").off('input');
   $("#slider2").off('input');
};

</script>
</body>
</html>
```
![47384482_730647210634863_8154917459330072576_n.jpg](https://qiita-image-store.s3.amazonaws.com/0/321718/7e8a1fc9-362c-41b8-2832-d65c8fa02741.jpeg)


#今後の展望など…？
多くの人には「遠隔で複数の扇風機を3次元制御する」ことにメリットは感じないかもしれませんが、外出先からねこに風をあてたり（嫌がられない程度に）、友達を驚かせたりするのに良いかもしれません？

#関係ないが、重要なこと!?
本日（12月7日）はななみんの誕生日なんです。
ということで？、ななみんが連載している、IoTおうちハックも読んでくださると喜びます！！
obnizでのハックも、少しずつ紹介しようかなー。

http://edge.tsumug.com/archive/category/IoTおうちハック 

更新：obnizで作ったIoT扇風機の記事も書きました！（https://edge.tsumug.com/entry/iot-home-hack-08）

おわり。じゃあね。
【ISUCON】iikanjini SpeedUp cheat sheet

## systemctl
※CentOS7では実質`chkconfig`が廃止され，サービスに関連することはsystemctlを使う
### 実行中のサービス一覧
```bash
$ systemctl
```

### サービス起動
```bash
$ systemctl start [service_name].service
```

### サービス停止
```bash
$ systemctl stop [service_name].service
```

### サービス再起動
```bash
$ systectl restart [service_name].service
```

### サービスの自動起動設定[enable/disable]の一覧
```bash
$ systemctl list-unit-files -t service
```

### 個別にサービスの自動起動設定を確認
```bash
$ systemctl status [service_name].service
```

### サービスの自動起動有効化
```bash
$ systemctl enable [service_name].service
```
### サービスの自動起動無効化
```bash
$ systemctl disable [service_name].service
```

### systemctlのログ出力
```bash
$ jornalctl -u [service_name].service
```
`-b` 直近の起動ログ  
`-k` エラーを強調する  
`-f` リアルタイムな表示  

---

## nginx
### 現在設定されているconfファイルの文法チェック
```
# nginx -t
```

### kataribe導入
```
$ apt-get update
$ apt-get install golang
$ mkdir -p ~/gocode && echo 'export GOPATH=$HOME/gocode' >> ~/.bashrc
$ echo 'export PATH=$GOPATH/bin:$PATH' >> ~/.bashrc
$ source ~/.bashrc
$ go get github.com/matsuu/kataribe
```

### kataribe実行
```
# cat /var/log/nginx/access.log | kataribe -f $GOPATH/src/github.com/matsuu/kataribe/kataribe.toml
```

---

## MySQL

### dump mysql
https://qiita.com/PlanetMeron/items/3a41e14607a65bc9b60c

### Find my.cnf
```
$ mysql --help | grep my.cnf
```
### Setup my.cnf
```
innodb_buffer_pool_size=1GB // インスタンスのメモリサイズを超えるとエラーになるので注意
innodb_flush_log_at_trx_commit=2 // 1に設定するとトランザクション単位でログを出力するが 2 を指定すると1秒間に1回ログファイルに出力するようになる
innodb_flush_method=O_DIRECT //データファイル/ログファイルの読み書き方式を指定
```
```
# if error was occuered
rm /var/lib/mysql/ib_logfile0
rm /var/lib/mysql/ib_logfile1
```
### Check Parameters
```
mysql> select @@[param_name];
```
### Add Index
```
ALTER TABLE [table_name] ADD INDEX [index_name]([column_name])
```

### Drop Index
```
ALTER TABLE [table_name] DROP INDEX [index_name]
```

### Show Index
```
SHOW INDEX FROM [table_name];
```

### Show warning
```
SHOW WARNING\G
```

---

### Dump Slow Query
### Command
```
mysql> set global slow_query_log = 1;
mysql> set global long_query_time = 0;
mysql> set global slow_query_log_file = "/tmp/slow.log";
mysql> set global log_queries_not_using_indexes = 1;
```
#### Write config file for log
```
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/mysqld-slow.log
long_query_time = 0
log_queries_not_using_indexes = 1
```
---
### Analyze Slow Query
#### Use mysqldumpslow
```
$ mysqldumpslow -s t /path/to/log > /path/to/output
```
#### Use pt-query-digest
```
$ wget https://repo.percona.com/apt/percona-release_0.1-3.$(lsb_release -sc)_all.deb
$ sudo dpkg -i percona-release_0.1-3.$(lsb_release -sc)_all.deb
$ sudo apt-get install percona-toolkit
$ pt-query-digest /tmp/slow.log > /tmp/digest.txt
```

---

## commands (使えるコマンド集）
### システム情報の表示
```
$ uname -a
```
`-a` コンピュータ（ハードウェア)の種類，ネットワークにおけるホスト名，OSのリリース番号，OSの名称

### CPU情報の確認
```
$ cat /proc/cpuinfo
```

### メモリ情報の確認
```
$ cat /proc/meminfo
```
ABEJA Platform のデータレイクのメタデータについて

この記事は [ABEJA Platform Advent Calendar](https://qiita.com/advent-calendar/2018/abejaplatform) の 7 日目の記事です。今回は ~~地味ですが~~ データレイクのメタデータ機能について紹介していきます。

# データレイクとは
ABEJA Platform は、データの蓄積・アノテーション・学習・デプロイ・再学習と機械学習に必要な機能を提供しています。
ABEJA Platform のデータレイクはデータの蓄積部分にあたる機能で、構造化データ、非構造化データに関わらず保存することが可能です。

# データレイクの概念

まずメタデータ機能の紹介の前にデータレイクで使用される用語を以下に説明します。

- **チャンネル** : データレイクに格納されるファイルのコンテナ
- **ファイル** : データレイクに格納されたデータ
- **メタデータ** : ファイルに付与される情報

# データレイクのメタデータについて

前置きはここまでで本題である **メタデータ機能** について紹介していきます。

データレイクのファイルは API を使用してそのままアップロードすると元のファイル名がなくなり、代わりに `file_id` が与えられるようになっています。

そのため基本的には `file_id` を使用してチャンネル内のファイルを特定することになりますが、 `file_id` は `20181206T000000-aaaabbbb-1234-5678-9012-ccccddddeeee` のような形式で覚えづらく、万が一忘れてしまうとチャンネル内のファイルの一覧を取得して１つ１つ中身を見ていかなければならないような事態になってしまいます。。。

ここで必要になるのがメタデータになります。

データレイクのメタデータ機能は、ファイルに対して **`x-abeja-meta-` というプレフィクスをつけた任意のキーとそれに対応する任意の文字列を保存** することと、 **付与したメタデータを使用してファイルを検索** することを可能にしています。

これを利用して、元のファイル名をメタデータに保存しておき、そのファイル名でチャンネル内からファイルを探すことが可能になります。
（ UI や CLI を使用してファイルをアップロードした際には `x-abeja-meta-filename` というメタデータのキーにファイル名が自動的に保存されるようになっています ）

次に、API を使用した 1. メタデータ付きファイルのアップロード方法と、 2. メタデータでのファイル検索方法を紹介していきます。

## メタデータを付与したファイルのアップロード方法

API を使用して猫の画像に `x-abeja-meta-filename:cat.jpg` というメタデータを付与したファイルをアップロードする方法は以下になります。

以下のようにリクエストヘッダーに `x-abeja-meta-filename: cat.jpg` のような形式で与えることによりメタデータを付与することが出来ます。

```bash
$ ls cat.jpg
$ curl -u ${USER_ID}:${PERSONAL_ACCESS_TOKEN} \
       -H "Content-Type: image/jpeg"          \
       -H "x-abeja-meta-filename:cat.jpg"     \
       --data-binary @cat.jpg                 \
       "https://api.abeja.io/channels/1234567890123/upload" 
```

アップロードに成功すると以下のようなレスポンスが返ってきます。

`metadata` の部分に付与したメタデータが含まれています。
※ メタデータの説明の部分では省略しましたが、 `x-abeja-sys-meta-organizationid` は ABEJA Platform が付与するメタデータになります。

```json:response
{
  "uploaded_at": "2018-12-06T15:01:50Z",
  "metadata": {
    "x-abeja-sys-meta-organizationid": "1230000000000",
    "x-abeja-meta-filename": "cat.jpg"
  },
  "lifetime": null,
  "file_id": "20181206T000000-aaaabbbb-1234-5678-9012-ccccddddeeee",
  "content_type": "image/jpeg"
}
```

## メタデータを使用したファイル検索方法

> ここで説明する検索機能は 2018/10/24以降に作成されたチャンネルにのみ有効となっています

最後に API を使用してメタデータでファイルを検索する方法を解説していきます。

まず、先程アップロードしたメタデータに `x-abeja-meta-filename:cat.jpg` が付与されたファイルの検索は以下になります。
クエリパラメータに `q=` でメタデータに対するクエリを指定する形式となっています。

```bash
$ curl -u ${USER_ID}:${PERSONAL_ACCESS_TOKEN} \
       "https://api.abeja.io/channels/1234567890123?q=x-abeja-meta-filename:cat.jpg"
```

### クエリの仕様

次にメタデータのクエリの仕様を解説していきます。

#### 複数条件検索

`AND` と `OR` がサポートされており、複数の条件を指定してチャンネル内のファイルを絞り込むことが出来ます。

例） `q=x-abeja-meta-label:cat OR x-abeja-meta-lable: dog`

#### 部分一致検索

`x-abeja-meta-filename` のみ部分一致検索がサポートされています。

例） `q=x-abeja-meta-filename:*bcde.json`


## まとめ

今回はデータレイクのメタデータについて紹介しました。

メタデータを活用することでデータレイクのチャンネル内のファイルをより効率的に見つけられるようになります。
また、データレイクの API の仕様は [こちらのドキュメント](https://api-spec.abeja.io/) に記載されているので、詳しくはドキュメントを参照して下さい。

ABEJA Platform のデータレイクには他にも構造化データに対してスキーマを定義してバリデーションを実行する機能もあるので、機会があれば書きたいと思います。
ASP.NET WebAPIについてのメモ



#コントローラー
##ApiController
###戻り値の型
- void
    - 204(No Content)を返す
- HttpResponseMessage
    - HTTPのレスポンスメッセージを直接返す
- IHttpApplicationResult
    - ExecuteAsyncを作成する。HttpResponseMessageレスポンスに変換する
- その他
    - MediaFormatterで戻り値をシリアル化する
    - ContentResultで返すと第２引数でContent-Type、第３引数で文字エンコーディングを指定できる。

##MediaFormatter
### MIMEの種類
- text/plain
- text/html
- text/csv
- image/png
- application/json
- application/octet-stream
- application/pdf
- application/x-www-form-urlencoded
    - POSTメソッドによるフォームデータの送信

### 

## Content-Transfer-Encoding
TransferEncodingを設定する
- Base64
- EightBit
- QuotePrintable
- SavenBit
- Unknown


参考：
https://docs.microsoft.com/ja-jp/dotnet/api/system.net.mime.transferencoding?view=netframework-4.7.2
https://docs.microsoft.com/ja-jp/aspnet/web-api/overview/formats-and-model-binding/media-formatters
https://docs.microsoft.com/ja-jp/aspnet/web-api/overview/formats-and-model-binding/media-formatters
https://ja.wikipedia.org/wiki/Multipurpose_Internet_Mail_Extensions
http://www.atmarkit.co.jp/ait/articles/0907/10/news109_2.html
Space Apps Challenge向けデータ虎の巻（2018年版まとめ）

[Space Apps Japan](https://www.facebook.com/groups/spaceappstokyo/?ref=bookmarks "Space Apps Japan")の[Advent Calendar](https://adventar.org/calendars/3374?fbclid=IwAR1Z4Og-O_yTZUZMRGsVrO4o2Mp2VrFq_7-w9SbSa0R0aAz15bQ9-qy_Yq4 "Advent Calendar")にかこつけて、NASA主催の世界同時オープンデータハッカソン[Space Apps Challenge](https://spaceappschallenge.org "Space Apps Challenge")に使える（かもしれない）データの見つけ方をまとめようと思います。

今年のSpace Apps Challengeでデータメンターとして活動した経験を踏まえて、[以前SlideShareに上げたスライド](https://www.slideshare.net/AkitoKawamura/20180924-saco-preevent)を補足するものです。

##欲しいデータ、使えないデータ
そもそもハッカソン向けのデータとはどういうものでしょうか？
必ずしも詳細なデータは必要ありません。
限られた時間の中で、そのデータ特有の処理を学ぶことは必ずしも有意義なことではありません。
その時間でプロダクトの手触りを上げる方が良いでしょう。

以下のスライド[^1]にオープンデータの俯瞰をまとめました。
オープンデータ（特に科学系）というと何かとややこしい詳細なデータに行き当たりますが、そういうデータは時間のある専門家向けです。特に準備をしていない参加者に紹介するなら一言で説明できるデータの方が適しています。

<img src="https://qiita-image-store.s3.amazonaws.com/0/264339/ac402d0a-657d-3a7d-8f87-f118d313a6e7.png" width=50%>

[^1]: 2018/10/31 Kyoto Science Meetupでの発表スライドより

今年の大阪会場では以下のようなデータのやり取りがありました。

[海氷面積のデータ](https://nsidc.org/data/g02135?fbclid=IwAR2RNIludP9ZfT2V0NiyXEYMr2oTuNOD8Wc2jk3ifb87svdX9cMvQ70fJqI)を紹介したら採用され、そのチームは優秀賞を取りました。

一方で[宇宙の全天観測画像の任意の区画を持ってくるサービス](https://aladin.u-strasbg.fr/aladin.gml)は[JavaScript API](https://aladin.u-strasbg.fr/AladinLite/doc/)とか[サンプルコード](https://aladin.u-strasbg.fr/AladinLite/doc/API/examples/AL-in-responsive-div/?fbclid=IwAR2Gd87jjhWw_rPz4kr53PhTtlEoBGDkJDs-13WHCfrmjDBz7-wABShFwoI)とか揃ってるよと言ったのですが、採用されず。そのチームは適当なハッブルの画像を持ってきて使って最優秀賞取りました。

プレゼン時の補足資料として森林面積の変化を知りたいと言ってきたチームもありました。国連のデータ（具体的にどのデータかは記録に残ってない）を紹介したのですが、出番はなかった様です。

月のデコボコってどのくらい？と訊いてきたチームには[Moon Trek](https://moontrek.jpl.nasa.gov/)を紹介しましたが、プロダクトに組み込むまでには至らず。タッチ＆トライ時にMoon TrekのVR機能を見せるに留まりました。

以上を踏まえると、APIが整っていてもプロトタイプ用に求めているレベルの簡易さに満たないことがあると思います。また、__開発スタートの非常に早いタイミングでデータを渡せたor訊きに来たチームが賞を取る結果になり、「何を使って、どうしたいか」を早く明確化することが良い結果のコツなのかなと思いました。__

##Space Apps向けデータの見つけ方
身も蓋もない言い方をすれば、検索方法を知っているか否かというだけです。
一番簡単なのはGoogle検索です。
検索キーワードには英語で物理量を含めると良いです。（例 "Antarctic Sea Ice area"）
データを持っている組織を知っていれば`site:`コマンドが強力です。`site:*.gov`や`site:*.org`で興味のあるコンテンツを扱う組織を探す手も有効です。
あとは大量のサイトを読む根気です。

<details><summary>オープンデータを持っている組織のドメイン例</summary>
nasa.gov : 宇宙や宇宙から見た地球のデータ、宇宙開発に関する資料
noaa.gov : 地球や宇宙の気象データ
nsidc.gov : NOAAのデータのうち、雪や氷に関するデータに特化
usgs.gov : 地震のデータ
un.org : 世界の人類社会のデータ（人口とか経済とか国土とか）
</detail>

とは言え、キーワードが直ぐに思いつかなかったりして、目的のデータがわからないこともあると思います。そういう時は一先ずSpace Appsのチャレンジのページの右下を見てみてください。大雑把ではありますが、"Example of Resorces"というところにデータ検索のためのポータルへのリンクがあります（本文の最後にあることもあります）。

<img src="https://qiita-image-store.s3.amazonaws.com/0/264339/ab1c7de3-50a2-56c6-d258-e535bdcad4e4.png" width=50%>

Space Apps ChallengeはNASAのイベントなので、関係するNASAのポータルページはここを見れば見つかります。そのポータルが使いやすいかどうかは別ですが。

<br>
そして、最も重要なこと。
###英語がわからないなら[Google先生](https://translate.google.co.jp/ "Google翻訳")に聞けばいいじゃない
専門用語にはまだまだ弱いですが、それ以外は意味が通じるぐらいには翻訳してくれます。
RxJSのof と fromの違い


# [前提] of、fromはobservableを作るもの
ofとfromは、Observableにするメソッドなので、前提としてObservableの概念を学ばなければいけません。
https://tech.recruit-mp.co.jp/front-end/rxjs-intro/

##subscribeの説明
subscribeはobservableに付随するメソッドです。
observableを川の流れと例えると、subscibeは下流に来た値を**流す**処理です。
以下がsubscribeメソッドの中身です。

```typescript

subscribe(
  (x) => console.log(x),                    // onNext
  (error: Error) => console.error(error),   // onError
  () => console.log('Completed!!')          // onComplete
)
```

* 新しい値が取得できたら、onNextメソッド、
* エラーが出たら、onErrorメソッド、
* 全ての値が流れ切った時に、OnCompleteメソッドが発火します、

#[of] 引数の値を,observableにする
引数の値をObservableのデータにします。

```typescript

// numberをobservableにして、引数である、1と２をストリームのデータにします。
 const number = of(1,2);
// subscribeで、1と2を取り出します。
 number.subscribe(val => console.log(val))

=> 1,2
```

#[from] 引数の値を分解した要素を、observableの値にする
fromは、引数の値を分解して、observebleの値にします。


## 配列の場合
```typescript

// 配列を、引数に入れる。fromで、要素が分解されるので、1と2をストリームのデータにします。
 const array = from([1,2])
// subscribeで、1と2を取り出します。
 array.subscribe(val => console.log(val))

=> 1,2
```

## 配列型オブジェクトの場合
配列とほぼ同じです。

```typescript

 const numbers: Number[]
 numbers = [1,2]
 const array = from(numbers)
 array.subscribe(val => console.log(val))
=> 1,2
```

## Stringの時
```typescript

// stringを引数に入れる。stringの文字列が分解されて,"s","t","r","i","n","g"を
 const string = from("string");
// subscribeで、"s","t","r","i","n","g"を取り出す。
  string.subscribe(val => console.log(val))
=> "s","t","r","i","n","g"
```

## Promiseの時
```typescript

function numberOne()  {
 return 1
};

function numberTwo(){
 return 2
};


const promise = new Promise((numberOne, numberTwo) => {
    const one = numberOne();
    const two = numberTwo();
    setTimeout(() => {
        return [one, two]
    });
}



const promise = promise()

const result = from(promise);
result.subscribe(val => console.log(val));
=> 1,2
```


# まとめ
| 　　　　　　          |      of                  |  from        |
|:------------: |:---------------------:|:-------------:|
|       役割           |  Observableにする       |Observableにする |
|       引数の数        |      複数                |一つ |
|     引数にする値     |        値                  |  配列、配列型オブジェクト、Promise,Observable,string    |





##引用
* https://tech.recruit-mp.co.jp/front-end/rxjs-intro/
* https://www.learnrxjs.io/operators/creation/of.html
* https://www.learnrxjs.io/operators/creation/from.html
12-07 Oboe

https://adventar.org/calendars/3353 7日目のエントリーです。

## Oboeとは

OboeはC++環境向けのAndroidプラットフォーム向けのオーディオAPIです。

Androidでは、API Level 9 (Gingerbread) の時にネイティブのオーディオAPIレイヤーとしてOpenSL ESの実装が追加され、API Level 26（Oero）の時に低レイテンシーオーディオ処理をサポートするための新しくAAudio APIが追加されています。OboeはAAudio APIが利用可能な時にはAAudioを利用し、それ以外の時はOpenSL ESを利用する実装になっています。環境に応じて最適なバックエンドを選択する共通APIということになります。

なお、OboeはAPI Level 27以降にのみ含まれている部分のAAudio APIに依存しており、OboeがAAudioを利用する条件はAPI Level 27以降のみとなっています）。また、OpenSL ESドライバー実装も、サポートされているのはオーディオ設定まわりのAPIが追加されたAndroid 16（Jellybeanのひとつ）以降のみです。

Oboeのことを「低レイテンシーに対応したAPI」と紹介しているものがあるとしたら、それはほぼ間違いなくAAudioのことを指しています。Android 8.0以前では無関係な話です。

Oboeの機能は、概ねAAudio APIが提供するものと同様です。OpenSL ESにはAAudioには無いオプション設定がいくつかあるのですが、Android NDKに含まれるOpenSLESの実装ではサポートされていなかったものが多く、実質的にOpenSL ESを選択しなければならないような場面はほとんど無いでしょう。

Oboeは10月にバージョン1.0として正式版が公開され、GoogleのAndroid developers blogでも紹介されている程度には本気度と完成度が高いので、安心して使えると思います。（cmakeのビルドスクリプトではstaticでしかビルドできなかったりしますが…!）

https://android-developers.googleblog.com/2018/10/introducing-oboe-c-library-for-low.html

## シンプルなAPI

OboeはOpenSL ESに比べて極めてシンプルで使いやすいAPI設計になっています。具体例として、わたしがfluidsynth（これについてはまた別の日に書きます）でOpenSL ESを使用しているコードとOboeを使用しているコードを比較してみてください。というか、OpenSL ES APIの「どうしてこうなった」感…（[VST3と似たような回りくどさ](https://qiita.com/hotwatermorning/items/7f1049a7222aa59a425c)ですね）

- OpenSL ES: https://github.com/atsushieno/fluidsynth-fork/blob/e78021fad11e839570d134eebae2f2ecf6435b51/src/drivers/fluid_opensles.c#L142
- Oboe: https://github.com/atsushieno/fluidsynth-fork/blob/8f7c0e4c69e2c881c23d9fc48ae87c288d84b2c4/src/drivers/fluid_oboe.cpp#L159
  - これはC++だから、というわけではありません。わたしがoboe-cというC APIを作って実装を書いた時のコードもまだまだ簡単なので、参考として挙げておきます: https://github.com/atsushieno/fluidsynth-fork/blob/e78021fad11e839570d134eebae2f2ecf6435b51/src/drivers/fluid_oboe.c#L128

## Oboe（あるいはAAudio）の特徴的なオプション

AAudioおよびOboeによって新しく登場したAndroidネイティブオーディオのオプションとしては、次の2つが挙げられます。

(1) 共有モード: オーディオI/Oを排他的に獲得するか否かを指定します。Exclusiveで接続していると、他のアプリケーションから割り込まれたりはしなくなります。結果的にミキサーをひとつ経由しなくなる分レイテンシーが下がりますが、システム等によって割り込まれたりなどしてオーディオ接続が切断される可能性が高まるようです。早目の解放が想定されています。Sharedで接続しているとミキサーをひとつ経由するのでその分遅くなります。以下は公式サイトのドキュメントにある図です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/18593/171f239f-5eb6-e84a-90a2-f007971b326e.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/18593/1fc24528-36f5-277f-ea1e-d49c38bbf340.png)

(2) パフォーマンスモード: LowLatencyモードとPowerSavingモードがあります。前者の低レイテンシーモードでは、バッファサイズを抑えることで、アプリケーションに対するオーディオデータ要求コールバックの頻度が上がり、オーディオの遅延は小さくなることが期待されます。逆にPowerSavingモードでは、コールバックの頻度が下がる分、バッテリーを過剰に使用しなくなります。デフォルトではその折衷的なNoneになっています。

これらをアプリケーションに合わせてうまく調整することで、最善のエクスペリエンスが期待できるでしょう。
KubernetesでDNSもhostsもいじれないときに外部のホストを名前解決する方法

# 目的

Kubernetesの名前解決はKubeDNSやノードのリゾルバを使うのが一般的ですが、外部のホストを名前解決したいときに、諸般の事情により、ノードのhostsやDNS設定をいじれない場合があります。そういった場合にKubeDNSに直接名前解決のための設定を登録することができます。

# 方法

次のようなサービスを登録します。

```yaml
apiVersion: v1
kind: Service
metadata:
  name: ホスト名
spec:
  type: ExternalName
  externalName: IPアドレス
```

すると、任意のPodからホスト名を指定することで外部のIPにアクセスすることができます。ただし、Podとサービスは同じネームスペースにいる必要があります。名前空間が異なる場合は、Podからは``ホスト名.ネームスペース名``のようにネームスペースを指定する必要があります。

以上です。
VaporでWebSocketを使ったチャットアプリを作る。

VaporでももちろんWebSocketを使うことができます。~~機能が少ない~~シンプルなので簡単に使うことができます。
今回はVaporでWebSocketを使い、簡単なチャットアプリを作っていきます。
公式 [Vapor WebSocket](https://api.vapor.codes/websocket/latest/WebSocket/index.html )

Vaporのプロジェクトを作成します。

```bash
$ vapor new websocketTest
$ cd websocketTest
$ vapor -y xcode  // 必要あれば
$ vapor build
$ vapor run
```

`configure.swift`のプロパティにWebSocketを保持する配列を作成します。

```swift:configure.swift
var websocketClients: Dictionary<String, [WebSocket]> = [:]
```

`configure` メソッドの中にWebSocketServerの設定を記述していきます。
`localhost:8080/socket/xxxx` (xxxxはルームの名前)  に送られてきた文字列を、同じルームに接続中のクライアントに転送しています。

```swift:configure.swift
// WebSockets
let wss = NIOWebSocketServer.default()
wss.get("socket", String.parameter) { ws, req in
    let room = try req.parameters.next(String.self)
    if websocketClients[room] == nil {
        websocketClients[room] = []
    } else {
        websocketClients[room] = websocketClients[room]!.filter({
            !$0.isClosed
        })
    }
    websocketClients[room]!.append(ws)
    ws.onText { ws, text in
        for client in websocketClients[room]! {
            if !client.isClosed {
                if ws === client {
                    print("slip sender")
                } else {
                    client.send(text)
                }
            }
        }
    }
}
services.register(wss, as: WebSocketServer.self)
```

デバックのために　wsta というWebSocketのクライアントを使いました。　
[wsta : A CLI development tool for WebSocket APIs
](https://github.com/esphen/wsta)


インストール方法

```bash
$ brew tap esphen/wsta https://github.com/esphen/wsta.git
$ brew install wsta
```

サンダースとダグトリオのシェルでkabigon というルームにアクセスします。ダグトリオの方のシェルで送信した文字列がサンダースのシェルに表示されています。

```bash
$ wsta ws://localhost:8080/socket/kabigon
```

![websocket.gif](https://qiita-image-store.s3.amazonaws.com/0/63855/859a7952-542e-37e4-4335-9f759c016c85.gif)
Pythonでリトライ処理を実装する


## 1. retryパッケージを使う

https://pypi.org/project/retry/

## 2. requestsでリトライの場合

ここら辺を参考に。
https://stackoverflow.com/questions/49121365/implementing-retry-for-requests-in-python

## 3. 自分で実装してみる

こんな感じ？

```Python
"""
例外発生時にリトライするシンプルな実装
常にリトライするのではなく、例外の内容を見てリトライするかどうか決定する
"""

import time


class MyException(Exception):
    def __init__(self, code):
        self.error_code = code


def do_task():
    raise MyException(code=1)


MAX_RETRY = 3


def do_with_retry():
    for i in range(MAX_RETRY + 1):
        try:
            do_task()
            return
        except MyException as e:
            # エラーコードが1のときだけリトライ
            if e.error_code == 1:
                print("エラー発生. retry={}/{}".format(i, MAX_RETRY))
                if i == MAX_RETRY:
                    raise e
                sleep_sec = 2 ** i
                print("sleep {} sec".format(sleep_sec))
                time.sleep(sleep_sec)
            else:
                print("エラー発生")
                raise e


if __name__ == '__main__':
    do_with_retry()

```
Stream APIでStringの配列をオブジェクトの配列に変換する方法

#実装したいことのイメージ
**変換前：**

```
String[] strArray = {"りんご", "みかん", "バナナ"};
```

**変換後：**

```
Fruit ringo = new Fruit("りんご");
Fruit mikan = new Fruit("みかん");
Fruit banana = new Fruit("バナナ");
Fruit[] fruitArray = {ringo, mikan, banana};
```
※FruitはフィールドにString nameを持つ任意のクラスで、コンストラクタにてnameを設定するものと仮定。

#環境
OS：Windows 10
Java：Oracle JDK 11 
　　　（開発・テスト等の用途においては、無償で利用できると解釈しています）

#（参考）普通のfor文の場合
```
String[] strArray = {"りんご", "みかん", "バナナ"};
Fruit[] fruitArray = new Fruit[strArray.length];
for(int i = 0; i < strArray.length; i++) {
    fruitArray[i] = new Fruit(strArray[i]);
}
```

#StreamAPIを使用する場合
```
Fruit[] fruitArray = Arrays.stream(strArray)  // (1)
    .map(Fruit::new)                          // (2)
    .toArray(Fruit[]::new);                   // (3)
```
###軽く説明
(1) String配列からStringストリームへ変換。（String[]⇒Stream\<String\>)

(2) mapメソッドでStreamの要素それぞれに対して、Fruitクラスを生成。
　コンストラクタの引数はString name１つのみなので、この記述でOK。
　（もちろん、ラムダ式 s -> new Fruit(s) でもOK。）

(3) toArrayメソッドで配列へ変換。

#まとめ
慣れない内はfor文の方が分かりやすいし、書きやすいけど、
慣れるとStreamAPIを使った方がコードが短くなるし、楽に書ける。

自分もまだまだStream初心者だから、無理やり使って早く慣れるようにしたい。
amはarray_mergeのエイリアスではない

am関数は、CakePHP3では削除されています。

参考：CakePHP各バージョンのグローバル関数
CakePHP 1.3
https://book.cakephp.org/1.3/ja/The-Manual/Developing-with-CakePHP/Global-Constants-and-Functions.html#am
CakePHP 2.0
https://book.cakephp.org/2.0/ja/core-libraries/global-constants-and-functions.html
CakePHP 3.0
https://book.cakephp.org/3.0/ja/core-libraries/global-constants-and-functions.html

ですので、CakePHPを最新バージョンにするのであれば、am関数は使えなくなります。

置き換えるなら、array_merge関数になるでしょう。
http://php.net/manual/ja/function.array-merge.php

しかし、am関数をarray_merge関数に置き換えるときは、引数に配列以外（特にnull）が入るパターンがないか確認が必要です。

am関数の内容は以下の通り。

```
function am() {
  $r = array();
  $args = func_get_args();
  foreach ($args as $a) {
    if (!is_array($a)) {
      $a = array($a);
    }
    $r = array_merge($r, $a);
  }
  return $r;
}
```

引数が配列でなければ配列に変換する処理があります。
この処理に頼った実装をしていると、array_merge関数に変換したときにエラーとなる可能性があります。
ion-router-outletのmainって何？

この記事は、[Ionic Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ionicframework)の7日目の記事です。

## TL;DR

`ion-menu`を使う場合は

- `main`属性
- `contentId`プロパティ

どちらかの指定が必須です。


## `<ion-menu>`でメニューを作る

Ionic v4で下のようなサイドメニューのある画面を実装する際、

<img src="https://qiita-image-store.s3.amazonaws.com/0/106886/a791f0f5-75a0-c5c3-40ef-deb73c6c4ba2.png" width="320px"> <img src="https://qiita-image-store.s3.amazonaws.com/0/106886/f2da87c4-9c75-8136-8277-eb87ce144514.png" width="320px">

`ion-menu`を使ってこのように作っていくと思います。

```html
<ion-menu>
  <ion-header>
    <ion-toolbar>
      <ion-title>Menu</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content>
    <ion-list>
      <ion-menu-toggle auto-hide="false">
        <ion-item>メニュー1</ion-item>
        <ion-item>メニュー2</ion-item>
      </ion-menu-toggle>
    </ion-list>
  </ion-content>
</ion-menu>
<ion-router-outlet main></ion-router-outlet>
```

おや？

```html
<ion-router-outlet main></ion-router-outlet>
```

`main`とは何でしょう？

Ionicのリファレンスを見ても特に説明がありません。
https://beta.ionicframework.com/docs/api/menu/

気持ち悪いのでとりあえず消してみましょう。

```html
<ion-router-outlet></ion-router-outlet>
```

すると...

<img src="https://qiita-image-store.s3.amazonaws.com/0/106886/31d146ab-4f95-4861-011a-b83d5b4c98b9.png" width=320px>

メニューアイコンが消えてしまいました。これはどういうことでしょうか？

## Ionicのソースコードを追う

`<ion-menu>`のソースコードには、

```tsx:menu.tsx
const el = this.el;
const parent = el.parentNode as any;
const content = this.contentId !== undefined
  ? document.getElementById(this.contentId)
  : parent && parent.querySelector && parent.querySelector('[main]');
```
https://github.com/ionic-team/ionic/blob/master/core/src/components/menu/menu.tsx#L157-L159

のように`querySelector('[main]')`で親要素から`main`という属性を持つ要素を取得している部分があります。

```html
<ion-menu></ion-menu>
<ion-router-outlet main></ion-router-outlet>
```

`main`属性はメニューの表示のために必要だったということですね。

気持ち悪いとか言ってごめんなさい :bow: 

## contentIdを使う

`<ion-menu>`と関連付けを行う方法はもう一つあります。

Ionic v4のAPIリファレンスには「contentId」を指定すると書かれてあります。
https://beta.ionicframework.com/docs/api/menu/

この場合、`ion-router-outlet`に設定したものと同じIDを`ion-menu`に渡します。

```html
<ion-menu contentId="main"></ion-menu>
<ion-router-outlet id="main"></ion-router-outlet>
```

（ ´_ゝ`）。o（サンプルがmain属性を指定するパターンなので見落としそう...）

コードで明示したい場合は`contentId`を使うと良さそうですね。

## おわりに

Ionicは現在ベータ版ですが、完成度が高いので十分期待できると思います。

ドキュメントや日本語情報はまだ十分とは言えないので、必要に応じてソースコードを見るのも良いでしょう。

詳しい話を聞いてみたいな〜という方は、来年1月に「[Angular & Ionic Meetup Fukuoka](https://ionic-jp.connpass.com/event/111912/)」を開催しますので是非ご参加ください（宣伝）。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/106886/9f999514-8567-bd8f-b35e-e52608225a73.png)
https://ionic-jp.connpass.com/event/111912/

明日は @umi_mori さんです！
PHP5.3のcomposerでguzzleのインストールに失敗するようになった話

2018/11/1にcomposerのVersion 1.7.3がリリースされました。
https://github.com/composer/composer/blob/master/CHANGELOG.md
composer.pharをself-updateすると、Version 1.7.3となります。（※2018/12/7現在は1.8.0）

```
$ php composer.phar self-update
Updating to version 1.7.3 (stable channel).
   Downloading (100%)
Use composer self-update --rollback to return to version 1.4.1
```

しかし、この環境でcomposer install を行うと、guzzleライブラリのインストールに失敗するようになりました。（PHP 5.3.27の環境で確認）

```
$ php composer.phar install
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: remove guzzle/guzzle v3.9.3
    - don't install guzzle/guzzle v3.9.3|remove guzzlehttp/guzzle v3.8.1
    - don't install guzzlehttp/guzzle v3.8.1|don't install guzzle/guzzle v3.9.3
    - Installation request for guzzle/guzzle v3.9.3 -> satisfiable by guzzle/guzzle[v3.9.3].
    - Installation request for guzzlehttp/guzzle v3.8.1 -> satisfiable by guzzlehttp/guzzle[v3.8.1].
```

そして、composer updateも失敗。
AWS SDKがguzzleに依存しているので使わないわけには行きません。

```
$ php composer.phar config -g repositories.packagist composer 'https://packagist.jp'
$ php -d memory_limit=4096M composer.phar update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: remove aws/aws-sdk-php 2.8.31
    - Conclusion: don't install aws/aws-sdk-php 2.8.31
    - Conclusion: don't install guzzlehttp/guzzle v3.8.1|keep guzzle/guzzle v3.9.3
    - Conclusion: don't install aws/aws-sdk-php 2.8.31|remove guzzlehttp/guzzle v3.8.1|keep guzzle/guzzle v3.9.3
    - aws/aws-sdk-php 2.4.0 requires guzzle/guzzle ~3.7.0 -> satisfiable by guzzle/guzzle[v3.7.0, … v3.7.4].

    - aws/aws-sdk-php 2.8.29 requires guzzle/guzzle ~3.7 -> satisfiable by guzzle/guzzle[v3.9.3, … v3.9.2].

    - aws/aws-sdk-php 2.0.0 requires guzzle/guzzle 3.0.* -> satisfiable by guzzle/guzzle[v3.0.0, … v3.0.7].
    - don't install guzzlehttp/guzzle v3.8.0|don't install guzzle/guzzle v3.7.0

    - don't install guzzlehttp/guzzle v3.8.0|don't install guzzle/guzzle v3.9.3

    - Conclusion: remove guzzlehttp/guzzle v3.8.1|keep guzzle/guzzle v3.9.3
    - Installation request for aws/aws-sdk-php 2.* -> satisfiable by aws/aws-sdk-php[2.0.0,… 2.8.9].
```

そもそも、guzzleライブラリ自体、v3でPHP5.3の対応を終了しています。
https://github.com/guzzle/guzzle
ですので、PHP5.3で使うなら、guzzle3を使うように設定する必要があります。
https://github.com/guzzle/guzzle3
※Pakagist上では、v3はv3.8.1が最新バージョンのようです。
https://packagist.org/packages/guzzlehttp/guzzle#v3.8.1

PHP5.3を利用している方は、guzzleを使うならcomposerをv1.7.2で止めておいたほうが良いでしょう。

```
php composer.phar self-update 1.7.2
```

さすがに、PHP5.3ではサービスを維持に支障がでてくるようになりましたねぇ。
今月でPHP5.6もサポート終了になるし、早いところアップデートしないと。
ワンラインでフォルダ以下のファイル、フォルダ数を再帰的にカウントするコマンド（備忘録）

#概要
ホワイトリスト型のセキュリティツールが入ったPCでファイル数が多いフォルダを調査する羽目になったので、今後使うかどうかはさておきバッチファイル等を使わずにコマンド一行のみでカウントする手段をメモっておく。

#実行環境
一応Windows7で確認したが、コマンドプロンプトを使用するので多分Windows系OSなら問題なく動くはず。

#実行コマンド
各コマンド、オプションの説明は`dir /?`等で確認できるので割愛。

```bat:コマンドプロンプト
for /f "delims=" %a in ('dir /s /b /ad "パス"') do ( echo %a && dir /b "%a" | find /c /v "" )
```
大まかな処理の流れとしては

- `for /f "delims=" %a in ('dir /s /b /ad "パス"')`で属性がフォルダのものだけを一行ずつ変数(%a)に格納する。
- `echo %a`でパスを表示する。
- `&&`でコマンドを結合して一行で実行できるようにする。
- `dir /b "%a"`で1)で変数に格納したフォルダ以下を取得、`|`で`find /c /v ""`に渡して行数をカウントする。

#実行結果
コマンドプロンプトを起動して上記の`"パス"`のところにサンプルとして用意した`"C:\work\temp"`を入れて実行すると・・・

```bat:実行結果
C:\work\temp\sample1
6
C:\work\temp\sample2
10
C:\work\temp\sample1\sample1_1
5
```
いい感じにフルパスとファイル、フォルダ数が表示されている。

#その他・あとがき
今回は表示さえできれば結果がログに残るからテキスト出力とかしてないけど、必要ならリダイレクトで出力する。
バッチとかが使える環境ならshebang記法でJScriptとかを埋め込んで整形したテキストとかにできるのになぁ・・・(´・ω・`)
ObnizのおかげでLINE BOOT AWARDS 2018でクラッピーチャレンジできた

[Obniz Advent Calendar 2018](https://qiita.com/advent-calendar/2018/obniz) 8日目の記事です。

ObnizをKickstarterで支援した思い入れがあるので、僭越ながら参加。技術的なことは既出の外部ブログへすでに書いている上、技術的な新鮮味はなくてごめんなさい。

# LINE BOOT AWARDS 2018へ応募したネタ

LINE Beaconに近付いたユーザが、Obnizに繋がったおもちゃ（クラッピー）をLINEメッセージ上で動かせるというもの。
![20181007160646.png](https://qiita-image-store.s3.amazonaws.com/0/69418/57ebe8ba-8487-fd4c-34a3-ad2abb9ffb31.png)

詳しい内容は外部ブログに書いてる→[コチラ](http://makoragi.hatenablog.com/entry/2018/10/07/170000)

Obniz周りにフォーカスすると...
- LINEからのpostbackが送られてくるまで待つ。
- postbackを受けたらobniz.jsでサーボモータ（クラッピーに内蔵）を動かす。
たぶんLINE x Obnizのハンズオンで紹介されてる内容だと思う。

# Obnizで良かった

工作作業があったにも関わらず、2日のハッカソンでほとんど作り上げることができたのは、Obnizのおかげ。

何が良かったかというと、
- サーボを簡単に動かせる（別の回路を作らなくて良かった）
- javascript初心者に優しい（コピペでどうにかなった）

自分のようにサクっとネタ的にものづくりしたり、そこまでがっつりプログラミングしない人には最強のサービス（マイコンボード）だ。

# 実現できていないこと

今回の応募は誰かが書いてあることのコピペで出来上がったわけだけど、実現できないこともあった。

「ObnizにLINE Simple Beaconとしての機能と、サーボを動かす機能の両方をさせる」

[ObnizをLINE Simple Beaconとして動かせること](https://qiita.com/wicket/items/e1953f47e33d5d3b265c)は確認されているが、Beaconとしての設定をしたあとにwebhookを待つことが出来るのだろうか。

これが出来るとLINE Beacon単体で用意する必要がないというメリットが。

時間が出来たら試行錯誤してみるつもり。

...ObnizアドベントカレンダーのTOPに書いてあった「失敗談でもOK」という言葉に甘えてしまった。
音ゲーのスコアツールを作ってメンテした話

この記事は[個人開発 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/private-development)の8日目です。

## 作ったもの
[オンゲキスコアツール](https://www.ginjake.net/ongeki/?user=ginjake)
[github](https://github.com/ginjake/ongeki_score)

### どんなアプリ？
SEGAの出している音楽ゲーム「オンゲキ」のスコアを管理、公開出来るツールです。
スコアを登録するには、オンゲキをプレイした後オンゲキNET有料コースに入る必要がありますが、
サンプルとして乗っているcsvをインポートすることで未プレイでも体感することは出来ると思います。
Laravel+Vue.jsで作りました。

### 構成
オンゲキスコアツールは主に4つの機能から構成されています。

#### スコア登録部分
<img src="https://qiita-image-store.s3.amazonaws.com/0/261659/ee342534-15a8-e9dd-d199-22ef22381610.png" width=70% >

ユーザー登録やログイン、csvをアップロードしてスコアテーブルに保存するなど
いわゆる普通の部分です。

#### スコア表示部分
スコアを閲覧、絞り込み出来る部分です。
Vue.js製です。
<img src="https://qiita-image-store.s3.amazonaws.com/0/261659/8b386e35-9517-f2c3-1edf-be3dff8704d4.png" width=70% >


#### スクレイピング部分
オンゲキの公式サイトにログインして、javascriptを実行するとスコアをcsvとして吐き出します。
javascriptはブックマークレット化することで、ワンプッシュでスコアを出力可能です。

<img src="https://qiita-image-store.s3.amazonaws.com/0/261659/e7e63be3-142a-2c06-d530-2f155b5040e7.png" width=70%>

#### マスターデータ登録部分
「曲名」「難易度」など曲の基本データを登録します。
shellからコマンドで実行するため、一般ユーザーの目には見えない部分になります。

# 何故作ろうと思ったのか。
オンゲキの前にSEGAが出した音楽ゲーム「チュウニズム」にまで話は遡ります。
会員サイトにログインすると曲ごとのスコア、プレイ回数が見れたのですが、閲覧出来るのは自分のみ。
また、曲の絞り込みも最低限しかありません。

- うまく出来たからtwitterで自慢する
- 友達とスコアを見せ合い、競い合う
- この曲スコア低いけどプレイ回数少ないしやれば伸びるのでは？みたいな分析

このあたりの機能があればなと思いました。
稼働1週間で簡易的なスコアツールを作り公開したところ
反響が大きく、本腰を入れて作っていきました。
その過程で様々なプレイヤーとも知り合いになり、交友関係も広がりました。
ただし、基幹部分が突貫であり当時の技術力もあわさりゴミコードでした。
また3年ほどの運用で知見も溜まっていきました。

`「SEGAが新しい音ゲーを出すらしいぞ？」`と聞き、今の知識で作ってみたいと思いました。

# 前回の反省を生かして工夫した点
### コードが汚い
githubに公開して、誰で見れるようにすれば恥ずかしいコードは書かないのではと考えました

### 更新が面倒
githubに公開して課題をissue化してもらったりPRをもらえれば、更新頻度は高くなるのではと考えました

### 稼働前から準備する
ロケテストに通い必要な要素をチェックしました。
会員サイト自体はチュウニズムと大きく外れることもないだろうと、必要な機能等予想して事前に作成していました。
突貫で作るとコードが汚くなるのは分かっていたのと、
`「誰よりも最初に作る」`というスピード感を強く意識しました。

### 曲の管理が面倒
チュウニズムでは月に2回ほど新曲が追加されます。
データベースを手動でいじり追加していたのですが、非常に面倒で更新ペースが落ちていきました。
途中からはコマンド化し自動化したら`みんな Happy!!`になったので、今回は最初からそれでいこうと思いました
(現状半手動なのでなんとかしたいです)

### モチベの上がる技術で作る
収入にならない個人開発ではモチベーションが一番大事です。
慣れ親しんだCakePHP+jQueryではなく、あまり知見のないLaravelとVue.jsで作ることで趣味と勉強を両立させました

### API化
Vue.jsで作る過程でAPI化したのですが、簡単にドキュメントを作り誰でも使ってもらえるようにしました。
これについては、SoundVoltexというKONAMIの音ゲーの[スコアツール](http://bluekingdragon.dip.jp/sdvx/) に感銘を受けたのが大きいです。
API化することで、専用のビュワーアプリを別の方が作っていたりコミュニティ全体がかなり盛り上がっていた記憶があります。

# 誤算 1
データベースを見ながら「どのくらい使ってくれるかな？」とにやにやしてましたが、1週間たってもアカウント登録人数は8人。
実際にスコアを登録したのは2人でした。
たったの25%しか使ってくれていません。

### 音ゲー仲間にお願いして使ってもらう
なんらかの原因があるのは明らかです。
でも自分では分からなかったので、何人かに使って意見をもらいました。

 - 「jsをブックマークレットに登録してスコアを抽出」　と言われても分からない。
 - 「どこから登録するのか」
 
など、建設的な意見をもらうことが出来ました。
離脱してしまった6人のユーザーには悪いことをしたなと思いました。

# 誤算 2
上記の対応をしても、予想より全然ユーザーが増えません。チュウニズムのときはもっと早く増えたのに。

チュウニズムのときはプレイさえすれば、誰でも「CHUNITHM.net」でスコアを閲覧できました。
オンゲキは有料サービスに登録しないとスコアを見れないという仕様。正直予想していませんでした。

スコアを見たくなるのは、曲を一通りプレイし苦手曲や得意曲を確認したい時。
無料なら最初から使ってみようとなりますが、有料だと必要なときまで課金しませんよね。

# 最後に
オンゲキも稼働から約3ヵ月経ち、スコアツールの需要も伸びてきました。
ユーザーも70人を超えたため、issue化された要望を実装しようと思っております。
サービス公開はノリと勢いでいけますが、メンテナンスはいかにモチベーションを維持するかに掛かっていると思いました
AWS RDSのPostgreSQLからpg_dumpでデータをダンプする

###AWS RDSのPostgreSQLからデータをダンプする

pg_dumpの実行元のEC2などでpg_dumpのバージョンが低いと下記のようなエラーが出る

```
pg_dump: server version: 9.6.9; pg_dump version: 9.2.24
```

###解決方法

PostgreSQLのサーバに合わせてpg_dumpをバージョンアップ

```
$ sudo rpm -Uvh https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-6.9-x86_64/pgdg-ami201503-96-9.6-2.noarch.rpm

$ sudo yum install postgresql96
```
カーソルを変更する

メリークリスマス！ Emacsのカーソルは好きですか？ 私は慣れました。

あるWindowsのテキストエディタでは、このように3種類の設定ができます。Emacsではどうすればできるでしょうか。

<img width="138" alt="スクリーンショット 2018-12-04 20.38.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/4121/4e741e69-8ae5-cb41-d885-55f866c00357.png">
<img width="140" alt="スクリーンショット 2018-12-04 20.39.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/4121/93e900db-9f17-8c1e-bd2b-025404b8b266.png">
<img width="136" alt="スクリーンショット 2018-12-04 20.39.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/4121/ddd56c4d-f5f3-d0e0-4240-afed48e3a12b.png">

## 設定方法

`init.el`では`default-frame-alist`に設定するのが簡単です。この変数はフレーム(一般的なGUIの用語における「ウィンドウ」)の初期化時のプロパティを設定できます。

```el
(add-to-list 'default-frame-alist '(cursor-type . bar))
```

この設定が動作することは新しいフレームを作成(`M-x make-frame`)したときに確認できます。

![emacs-changes-cursor.gif](https://qiita-image-store.s3.amazonaws.com/0/4121/3a68b976-f695-9752-6cbf-360e11053ffe.gif)

### 簡単な確認方法

`*scratch*`バッファに貼り付けて以下一行づつ`C-x C-e`で実行してみるのがよいです。

```el
(set-frame-parameter (window-frame) 'cursor-type 'box)
(set-frame-parameter (window-frame) 'cursor-type 'bar)
(set-frame-parameter (window-frame) 'cursor-type 'hbar)
(set-frame-parameter (window-frame) 'cursor-type '(hbar . 10))
(set-frame-parameter (window-frame) 'cursor-type 'hollow)
(set-frame-parameter (window-frame) 'cursor-type nil)
```

![emacs-changes-cursor2.gif](https://qiita-image-store.s3.amazonaws.com/0/4121/ba927c5e-5f65-fd25-e7d0-4ea7409482f3.gif)

設定ファイルに書くときは`set-frame-parameter`よりも、先に書いた`(add-to-list 'default-frame-alist ...)`の方がおすすめです。

### 文字の左に縦棒

<img width="84" alt="スクリーンショット 2018-12-06 22.37.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/4121/7df81669-7dce-e7ac-2d80-77bf1fabfba6.png">

```el
(add-to-list 'default-frame-alist '(cursor-type . bar))
```

### 文字に下線

<img width="90" alt="スクリーンショット 2018-12-06 22.38.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/4121/7dc137e2-4db1-2b60-2241-73642f02add1.png">

```el
(add-to-list 'default-frame-alist '(cursor-type . hbar))
```

### 文字の下部にブロック

<img width="91" alt="スクリーンショット 2018-12-06 22.39.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/4121/fd1d3d82-4979-c901-cd06-80539d10ac0f.png">

この設定には数字をそのまま書かなくてはいけません。好きな数字を入れてください。画像は概ねフォントサイズの半分程度です。

```el
(add-to-list 'default-frame-alist '(cursor-type . (hbar . 10))
```

### ボックス(デフォルト)

<img width="89" alt="スクリーンショット 2018-12-06 22.58.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/4121/210d3763-153a-d93a-f9a9-631c439e8f0d.png">

```
(add-to-list 'default-frame-alist '(cursor-type . box))
```

### hollow(枠線)

```
(add-to-list 'default-frame-alist '(cursor-type . hollow))
```

<img width="82" alt="スクリーンショット 2018-12-06 23.55.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/4121/70e2ac2d-ca70-7ebb-ec79-b9bba618def7.png">

## おまけ1: カーソルを点滅しなくするようにするには？

```
(blink-cursor-mode -1)
```

## おまけ2： 端末でカーソルを変更するには?

実はVT100には[DECSCUSR—Set Cursor Style](https://vt100.net/docs/vt510-rm/DECSCUSR.html)なる制御シーケンスがあり、さらにTeraTermやiTerm2などでは独自拡張して縦棒のカーソルも表示できるようになってます。[vim からの制御シーケンスの使用例](https://ttssh2.osdn.jp/manual/ja/usage/tips/vim.html)なども紹介されてるので、なんかいい感じにすれば(Emacs本体に手を入れれば)カーソル変更できるんじゃないですかね。私は調べてないです。
attr_accessorの仕組みが分かった

# 初めに

attr_accessorってrubyの勉強してたら結構出てきますよね。
最近になってやっとattr_accessorがどういった仕組みをしているかがわかりました。

## attr_accessorの使い方

attr_accessorはこんな感じで使いますよね

```ruby:attr_sample.rb
class User
  attr_accessor :age
end

user = User.new
user.age = 18
p user.age
=> 18
```

## attr_accessorの仕組み

まずさっきのコードは

```ruby:attr_sample2.rb
class User
  def age=(value)
    @age = value
  end

  def age
    @age 
  end 
end

user = User.new
user.age = 18
p user.age
=> 18
```


これと同じ


それでattr_accessor :ageの一行でどのように実装しているのかというと


```ruby:attr_code.rb

class Object  

    def self.new_accessor(*args)
        args.each do |attr|
            define_method(attr) do
                instance_variable_get("@#{attr}")
            end 

            define_method("#{attr}=") do |value|
                instance_variable_set("@#{attr}", value)
            end
        end 
    end 
end 

class User 
    new_accessor :age, :name
end 

user = User.new 
user.age = 19
user.name = 'taro'
p user.age
p user.name
=> 19
=> 'taro'
```


みたいな感じです（多分…）

まずnew_accessorというクラスメソッドをObjectクラスをオープンして作っています。

そうすることによってObjectクラスが継承されているクラスすべてで使うことができます。

new_accessorの中身はdefine_methodでattrというメソッドとattr=というメソッドを作っています。

attrではinstance_variable_get で @attr　の値を取得していて

attr=ではinstance_variable_set で @attr　に値をセットしています

（attrは*argから得た値で今回の場合はageとnameです）


*argで不特定の個数の値をとることによって何個でもメソッドが作ることができます。



素晴らしい！！



なにか間違っていることがあるかもしれないのでその際はご指摘いただけたらと思います。