【Ruby】ネストしたStructをスマートに書きたい
response.data.solution.status #=> 'ACTIVE'
テストでこんな感じのモックが欲しくて、ネストしたStructをスマートに書く方法がないか調べた
response = Struct.new(:data).new(Struct.new(:solution).new(Struct.new(:status).new('ACTIVE')))
#=> #<struct data=#<struct solution=#<struct status="ACTIVE">>>
response.data.solution.status
#=> "ACTIVE"
普通に書いたら、なにこれひどいなって感じになった
solution = Struct.new(:status).new('ACTIVE')
data = Struct.new(:solution).new(solution)
response = Struct.new(:data).new(data)
変数に入れたら一応読みやすくはなるけど、スマートではない
調査結果 🔗
3種類の書き方が見つかった
1. 標準ライブラリ(ostruct)を使う 🔗
require 'ostruct'
response = OpenStruct.new({ data: { solution: { status: 'ACTIVE' } } })
#=> #<OpenStruct data={:solution=>{:status=>"ACTIVE"}}>
response.data[:solution][:status]
#=> "ACTIVE"
スマートに書けるけど、2階層以降はHashになってる
2. Gem(recursive-open-struct)を追加する 🔗
require 'recursive-open-struct'
response = RecursiveOpenStruct.new({ data: { solution: { status: 'ACTIVE' } } })
#=> #<RecursiveOpenStruct data={:solution=>{:status=>"ACTIVE"}}>
response.data.solution.status
#=> "ACTIVE"
求めてたスマートさで書けた
ただ、Gemを追加しないといけないのが難点
3. JSON.parseとostructを使う 🔗
require 'json'
require 'ostruct'
response = JSON.parse({ data: { solution: { status: 'ACTIVE' } } }.to_json, object_class: OpenStruct)
#=> #<OpenStruct data=#<OpenStruct solution=#<OpenStruct status="ACTIVE">>>
response.data.solution.status
#=> "ACTIVE"
Gemを追加しないで書けるけど、果たしてこれはスマートなのか?ってなった
コピペしやすいようにプロンプトの表示を切り替える
ドキュメントやブログにコマンドを貼り付ける時にいちいちプロンプトの表示を消すのがめんどくさいので、簡単にシンプルなプロンプトの表示($
)に切り替える方法
やり方 🔗
~/.bashrcにプロンプトの表示を切り替える関数(toggleps)を定義する
$ vim ~/.bashrc
function toggleps() {
if [ "$PS1" = "$ " ]
then
PS1=$OLDPS1
else
OLDPS1=$PS1
export OLDPS1
PS1="$ "
fi
}
$ source ~/.bashrc
TAKAGI-no-MacBook-Pro:toggl_to_pixela TAKAGI$ toggleps
$ ls
Gemfile Gemfile.lock README.md app.rb spec support toggl.rb
$ cal
6月 2021
日 月 火 水 木 金 土
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
$ date
2021年 6月 7日 月曜日 23時05分20秒 JST
$ toggleps
TAKAGI-no-MacBook-Pro:toggl_to_pixela TAKAGI$
課題 🔗
事前にコピーすることがわかっている場合は便利だけど、いろいろ作業したあとにコピーする必要が出てきた時はもうどうしようもない
IOSTを毎日積み立てをする場合、何時に買えばいいのか検証
IOSTを毎日積み立てをする場合、何時に買えばいいのか
前に用意したワンライナーである程度データが溜まったので集計してみた
IOSTの価格を取得するワンライナーバッチ
特に知識ないので、単純に時間ごとの平均価格を出すだけ
$ head results.csv
202104200000,7.74429699
202104200100,7.57416832
202104200200,7.71993105
202104200300,7.87145464
202104200400,7.83520595
202104200500,7.88429135
202104200600,7.98213785
202104200700,7.98698289
202104200800,7.81248123
202104200900,7.64619947
$ cat results.csv | grep 0000 | wc -l
46
https://github.com/ytkg/iost_hourly_average/blob/main/results.csv
データとしては46日分用意した
結論 🔗
さっそく結論を言うと、何時に買っても大して変わらない
$ cat results.csv | ./hourly_average.awk | sort
00 5.43181
01 5.42103
02 5.41575
03 5.40975
04 5.40204
05 5.39636
06 5.37479
07 5.39608
08 5.39295
09 5.40997
10 5.39481
11 5.42372
12 5.41233
13 5.3864
14 5.37735
15 5.41897
16 5.39677
17 5.39798
18 5.39341
19 5.38188
20 5.38604
21 5.38065
22 5.35603
23 5.36825
手順 🔗
時間ごとの平均をawkで出してみた
その試行錯誤ログ
NEM(XEM)とSymbol(XYM)のウォレット残高をAPIを使って取得する方法
仮想通貨の総資産を出したいので調べた
取引所はコインチェックとバイナンスを使っているが、NEMとSymbolだけはオプトインの時に個別ウォレットに入れてそのままにしているため
手順 🔗
NEM(XEM) 🔗
$ curl http://133.167.127.119:7890/account/get?address={アドレス}
※ノードのアドレスは https://nemnodes.org/nodes/ から適当に選んだ
$ curl -s http://133.167.127.119:7890/account/get?address=ND4HES3F4GJZPN2TRV3WLXSEUONOIFG2UGZ3NDM4 |jq .
{
"meta": {
"cosignatories": [],
"cosignatoryOf": [],
"status": "LOCKED",
"remoteStatus": "INACTIVE"
},
"account": {
"address": "ND4HES3F4GJZPN2TRV3WLXSEUONOIFG2UGZ3NDM4",
"harvestedBlocks": 0,
"balance": 119950000,
"importance": 0,
"vestedBalance": 119926419,
"publicKey": "da666110fa4e58b6e692ab5dca4238b2145c6d29eb76ac1f36189e2a3fe2085f",
"label": null,
"multisigInfo": {}
}
}
$ curl -s http://133.167.127.119:7890/account/get?address=ND4HES3F4GJZPN2TRV3WLXSEUONOIFG2UGZ3NDM4 |jq .account.balance
119950000
※小数点の計算を避けるために100万倍されている。
Symbol(XYM) 🔗
$ curl 153.127.35.168:3000/accounts/{アドレス}
※ノードのアドレスは https://symbolnodes.org/nodes/ から適当に選んだ
$ curl -s 153.127.35.168:3000/accounts/NATIJRZR3WKPMDUWUNHC2M4PRWOA5WV47T6W3QA |jq .
{
"account": {
"version": 1,
"address": "682684C731DD94F60E96A34E2D338F8D9C0EDABCFCFD6DC0",
"addressHeight": "217397",
"publicKey": "0000000000000000000000000000000000000000000000000000000000000000",
"publicKeyHeight": "0",
"accountType": 0,
"supplementalPublicKeys": {},
"activityBuckets": [],
"mosaics": [
{
"id": "6BED913FA20223F8",
"amount": "118732400"
}
],
"importance": "0",
"importanceHeight": "0"
},
"id": "60B4D5E2EF53F56F638952CC"
}
$ curl -s 153.127.35.168:3000/accounts/NATIJRZR3WKPMDUWUNHC2M4PRWOA5WV47T6W3QA |jq -r .account.mosaics[].amount
118732400
※小数点の計算を避けるために100万倍されている。
【Git】2つ以上前のコミットを修正する
2つ以上前のコミットの間違いを見つけた時の修正方法
自分用結論メモ 🔗
$ git rebase -i HEAD~~
edit 253f7ad Add commit A
pick 779aa5d Add commit B
$ git commit --amend
$ git rebase --continue
手順 🔗
commit_A.txtのcommitをタイポした設定でコミット253f7ad Add commit A
を修正する
commmit A
commit B
$ git log --oneline -p
779aa5d (HEAD -> master) Add commit B
diff --git a/commit_B.txt b/commit_B.txt
new file mode 100644
index 0000000..df9c5fb
--- /dev/null
+++ b/commit_B.txt
@@ -0,0 +1 @@
+commit B
253f7ad Add commit A
diff --git a/commit_A.txt b/commit_A.txt
new file mode 100644
index 0000000..c2bb3c1
--- /dev/null
+++ b/commit_A.txt
@@ -0,0 +1 @@
+commmit A
$ git rebase -i HEAD~~
253f7ad Add commit A
のpickをeditに変更
【Deno】Hello, World!を出力するだけのコマンドを作る
作りたいコマンドができたので、Denoでコマンドを作成する予習
Hello, World!を出力するだけのコマンド作成する
Denoのバージョン 🔗
Dockerでやるのは不都合が出てきたのでMacにHomebrewで直接インストールした
$ brew install deno
$ deno --version
deno 1.10.2 (release, x86_64-apple-darwin)
v8 9.1.269.27
typescript 4.2.2
コマンドの作成 🔗
Hello, World!と出力するだけのファイルを作成
console.log("Hello, World!");
権限も特にいらないのでこれで完成
$ deno run cli.ts
Hello, World!
コマンドのインストール 🔗
インストール 🔗
deno install
でインストールできる
$ deno install cli.ts
もしくはGitHubに上げて
$ deno install --name deno_hello https://raw.githubusercontent.com/ytkg/deno_hello/main/cli.ts
※デフォルトで拡張子を省略したファイル名がコマンド名になるが、mod.ts
やcli.ts
の場合は親ディレクトリ名になるので、--name
オプションでコマンド名の指定をしている
パスを通す 🔗
デフォルトで$HOME/.deno/bin
以下に保存されるのでパスを通してあげる
$ echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile
実行 🔗
$ deno_hello
Hello, World!
動いた
実行ファイルの中身 🔗
#!/bin/sh
# generated by deno install
exec deno run 'https://raw.githubusercontent.com/ytkg/deno_hello/main/cli.ts' "$@"
別のやり方(コンパイル) 🔗
コンパイル 🔗
$ deno compile cli.ts
もしくはGitHubに上げて
【Mac】rbenvでRubyのバージョンを最新に上げる時のメモ
Rubyのバージョンを上げるのに手こずったのでメモしておく
Homebrewでrbenvを入れた前提
バージョン 🔗
作業前のバージョン
$ brew --version
Homebrew 3.1.1
Homebrew/homebrew-core (git revision d7d84be345; last commit 2021-04-14)
$ rbenv --version
rbenv 1.1.2
$ ruby-build --version
ruby-build 20210510
手順 🔗
$ brew update
$ brew upgrade rbenv ruby-build
$ rbenv install --list
2.6.7
2.7.3
3.0.1
jruby-9.2.17.0
mruby-3.0.0
rbx-5.0
truffleruby-21.1.0
truffleruby+graalvm-21.1.0
Only latest stable releases for each Ruby implementation are shown.
Use 'rbenv install --list-all / -L' to show all local versions.
$ rbenv install 2.7.3
$ rbenv global 2.7.3
$ ruby --version
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-darwin19]
バージョン 🔗
バージョン情報も一応載せておく
【Rails】OmniAuthを2系に上げたら、Twitter認証でログインできなくなったので対応した
Twitter認証を導入している個人Railsアプリの話
問題 🔗
omniauth
を2系に上げたら、Twitter認証でログインできなくなってしまった
正確にはomniauth-twitter
を使っていて、内部で使われているomniauthをバージョンを上げた
エラーメッセージ 🔗
Started GET "/auth/twitter" for 192.168.48.1 at 2021-05-06 22:40:45 +0900
Cannot render console from 192.168.48.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
ActionController::RoutingError (No route matches [GET] "/auth/twitter"):
原因 🔗
CSRFの脆弱性(CVE-2015-9284
)の対応に伴う変更らしい
サービスプロバイダの認証画面へのリダイレクトが2系ではPOSTのみ有効になった
対処法 🔗
- CSRFの脆弱性対応用のGemを追加する
- Twitter認証画面へリダイレクトをGETからPOSTに変更する
CSRFの脆弱性対応用のGemを追加する 🔗
omniauth-rails_csrf_protection を追加
gem 'omniauth-twitter'
+ gem 'omniauth-rails_csrf_protection'
Twitter認証画面へリダイレクトをGETからPOSTに変更する 🔗
オプションmethod: :post
を追加
- <%= link_to '/auth/twitter', class: 'btn btn-block btn-primary' do %>
+ <%= link_to '/auth/twitter', method: :post, class: 'btn btn-block btn-primary' do %>
参考 🔗
「Effective Deno」を読みながら、Denoを触ってみる
Zenn にDenoのめちゃくちゃ良さげなテキスト(Effective Deno )が公開されていたので、それを読みながらDenoを触ってみた
いい感じのテーマが思い浮かばなかったのでFizzBuzz問題を実装することにした
「1から100までの数字を画面に表示する。ただし、3の倍数のときは数字の代わりにFizzと表示し、5の倍数のときは数字の代わりにBuzzと表示し、15の倍数のときは数 字の代わりにFizzBuzzと表示する」
Denoの環境 🔗
前に書いた記事(DenoでHello, World!
)同様、Dockerで用意する
.bashrcに記述して、使える状態にした
Denoのバージョンは触った時点で最新の1.9.2を使った
deno () {
docker run \
--interactive \
--tty \
--rm \
--volume $PWD:/app \
--volume $HOME/.deno:/deno-dir \
--workdir /app \
hayd/ubuntu-deno:1.9.2 \
deno "$@"
}
$ deno --version
deno 1.9.2 (release, x86_64-unknown-linux-gnu)
v8 9.1.269.5
typescript 4.2.2
実装 🔗
仮のfizzbuzz関数の実装 🔗
まずはfizzbuzz関数のガワだけを作る
受け取った引数の値を文字列にして返す
export function fizzbuzz(number: number): string {
return String(number);
}
本来、Fizz
,Buzz
,FizzBuzz
はString型で、数値はNumber型で返してあげた方がいい気がするけど、今回はすべてString型で返すことにする
テストの実装 🔗
fizzbuzz関数のガワだけできたので、先にテストを書く
import { assertEquals } from 'https://deno.land/[email protected]/testing/asserts.ts';
import { fizzbuzz } from './fizzbuzz.ts';
Deno.test('fizzbuzz(1)', () => {
assertEquals(fizzbuzz(1), '1');
});
Deno.test('fizzbuzz(3)', () => {
assertEquals(fizzbuzz(3), 'Fizz');
});
Deno.test('fizzbuzz(5)', () => {
assertEquals(fizzbuzz(5), 'Buzz');
});
Deno.test('fizzbuzz(15)', () => {
assertEquals(fizzbuzz(15), 'FizzBuzz');
});
テストの実行 🔗
deno test
で実行できる
もちろんString型にして返すだけなので、1の場合しかテストは通らない
【Mac】Time Machineのバックアップが遅い問題の対処法
ググるといろいろ方法が見つかり、その中で1つだけ試して、改善したのでメモ
問題 🔗
Macの移行ためにTime Machineでバックアップが取っていたが、明らかに遅い
大げさではなく3ヶ月掛かりそうな雰囲気
対処法 🔗
Time Machineの優先度をあげる設定にする
デフォルトでは、他のプロセスの邪魔をしないように控えめになっているらしい
debug.lowpri_throttle_enabledを0にする 🔗
sudo sysctl debug.lowpri_throttle_enabled=0
1GB/分くらいになった
3ヶ月掛かる予定が5時間ほど完了
終わったら1に戻す 🔗
sudo sysctl debug.lowpri_throttle_enabled=1
再起動で元の設定に戻るから、万一戻し忘れても問題は無い気がする
設定値の確認方法 🔗
$ sysctl -a |grep debug.lowpri_throttle_enabled
debug.lowpri_throttle_enabled: 1
参考 🔗
GitHub Actionsで複数のRubyバージョンでテストを実行する
Railsアプリの場合、テストは1つのRubyバージョンだけで問題ないが、複数のRubyバージョンをサポートしているGemの場合はテストも複数のバージョンで確認しておきたい
今回、GitHub Actionsで複数のRubyバージョンでテストを実行するようにしたのでそのメモ
手順 🔗
Ruby 2.5.7のみでテストが実行されるワークフローファイル 🔗
Gemを新規作成(bundle gem gem_name
)した時に自動で作られたワークフローファイル
このファイルを複数バージョンでテストが実行されるように書き換える
name: Ruby
on: [push,pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.5.7
- name: Run the default task
run: |
gem install bundler -v 2.2.7
bundle install
bundle exec rake
複数バージョンでテストが実行するように書き換えた差分 🔗
Rubyバージョン、2.5, 2.6, 2.7でテストが実行されるようにした
jobs:
build:
runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ ruby: [2.5, 2.6, 2.7]
+ name: Ruby ${{ matrix.ruby }}
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
- ruby-version: 2.5.7
+ ruby-version: ${{ matrix.ruby }}
- name: Run the default task
複数バージョンでテストが実行されている 🔗
【Deno】Aleph.jsを試した時にうまく動かなかったバージョンの組み合わせ
※2021年5月6日時点の情報です 🔗
「【Deno】Aleph.jsで作成したアプリをVercelにデプロイする
」を書いた時に試した内容でうまく動かなかったバージョンの組み合わせがあったので記録として残しておく
組み合わせというよりAleph.jsのみの不具合だと思う
まだアルファ版なのでしょうがない
- Deno 1.6.3 と Aleph.js 0.2.28
- Deno 1.9.2 と Aleph.js 0.3.0-alpha.32
Deno 1.6.3 と Aleph.js 0.2.28 🔗
バージョン 🔗
$ aleph --version
aleph.js 0.2.28
deno 1.6.3
v8 8.8.294
typescript 4.1.3
詳細 🔗
Get Started - Aleph.js
で紹介されていたバージョンの組み合わせで、aleph dev
でローカルサーバーを起動した時にコケた
これは最新版のAleph.jsを使うことで解決した
エラーメッセージ 🔗
error: Uncaught (in promise) TypeError: TS2345 [ERROR]: Argument of type 'TransformerFactory<SourceFile>' is not assignable to parameter of type 'TransformerFactory<SourceFile> | CustomTransformerFactory'.
Type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").TransformerFactory<import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").SourceFile>' is not assignable to type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").TransformerFactory<import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").SourceFile>'.
Types of parameters 'context' and 'context' are incompatible.
Type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").TransformationContext' is not assignable to type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").TransformationContext'.
The types of 'factory.createTypeParameterDeclaration(...).parent' are incompatible between these types.
Type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").CallSignatureDeclaration | import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").ConstructSignatureDeclaration | ... 17 more ... | import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").InferTypeNode' is not assignable to type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").CallSignatureDeclaration | import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").ConstructSignatureDeclaration | ... 17 more ... | import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").InferTypeNode'.
Type 'JSDocTemplateTag' is not assignable to type 'CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | ArrowFunction | ... 15 more ... | InferTypeNode'.
Type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").JSDocTemplateTag' is not assignable to type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").JSDocTemplateTag'.
Types of property 'parent' are incompatible.
Type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").JSDoc | import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").JSDocTypeLiteral' is not assignable to type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").JSDoc | import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").JSDocTypeLiteral'.
Type 'JSDoc' is not assignable to type 'JSDoc | JSDocTypeLiteral'.
Type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").JSDoc' is not assignable to type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").JSDoc'.
Types of property 'parent' are incompatible.
Type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").HasJSDoc' is not assignable to type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").HasJSDoc'.
Type 'ImportEqualsDeclaration' is not assignable to type 'HasJSDoc'.
Property 'isTypeOnly' is missing in type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").ImportEqualsDeclaration' but required in type 'import("https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts").ImportEqualsDeclaration'.
if (reactRefresh) transformers.before!.push(reactRefreshTS())
~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/tsc/compile.ts:28:49
'isTypeOnly' is declared here.
readonly isTypeOnly: boolean;
~~~~~~~~~~
at https://cdn.esm.sh/v41/[email protected]/lib/typescript.d.ts:1601:18
関連するIssue 🔗
“Get Started” instructions are broken · Issue #190 · alephjs/aleph.js
【Deno】Aleph.jsで作成したアプリをVercelにデプロイする
Denoで何か作ってみようと思ってフレームワークを探していたら、Aleph.js というReactフレームワーク(Next.jsにインスパイアされたらしい)見つけたので触ってみた
公式ドキュメントの以下を見ながら、Welcome画面をVercelにデプロイするまでをやってみる
バージョン 🔗
うまく動かないバージョンの組み合わせがあるらしく、以下のバージョンでは今回やった部分は動作した
- Mac OS 10.15.7
- Docker 20.10.5
- Docker Compose 1.29.0
- Deno 1.9.2
- Aleph.js 0.3.0-alpha.31
Aleph.jsのアプリケーション作成 🔗
Dockerk開発環境の準備 🔗
- Dockerfile
- docker-compose.yml
FROM hayd/ubuntu-deno:1.9.2
RUN deno install --unstable -A -f -n aleph https://deno.land/x/[email protected]/cli.ts
WORKDIR /app
ADD . /app
version: "3"
services:
web:
build: .
command: "aleph dev"
volumes:
- .:/app
ports:
- "8080:8080"
$ tree .
.
├── Dockerfile
└── docker-compose.yml
アプリ作成 🔗
$ docker-compose run --rm web aleph init .
$ tree .
.
├── Dockerfile
├── api
│ └── counter
│ ├── [action].ts
│ └── index.ts
├── app.tsx
├── components
│ └── logo.tsx
├── docker-compose.yml
├── import_map.json
├── lib
│ └── useCounter.ts
├── pages
│ └── index.tsx
├── public
│ └── logo.svg
└── style
└── index.css
7 directories, 11 files
アプリ起動 🔗
$ docker-compose up
http://localhost:8080/
にアクセスにすると以下のような画面が表示されるはず
S3を画像サーバーにするときのバケットの作り方のメモ
このブログに貼る画像をS3で管理するように変更したので、その時にバケットを作ったメモ
設定項目 🔗
ブロックパブリックアクセス設定 🔗
パブリックアクセスをすべてブロック
のチェックを外す現在の設定により、このバケットとバケット内のオブジェクトが公開される可能性があることを承認します。
にチェックを入れる
バケットポリシー 🔗
takagi.blog
の部分はバケット名
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::takagi.blog/*"
}
]
}
PuppeteerをRubyから操作する
Puppeteer をRubyから操作できるGem(puppeteer-ruby )を見つけたのでやってみた
このブログをスクレイピングをして、タイトル5件だけ出力してみる
手順 🔗
Docker環境の準備 🔗
Dockerfile 🔗
公式でDockerで動かすサンプルが用意されていたの拝借した
Rubyバージョンだけ2.7.3に変更している
puppeteer-ruby-example/Dockerfile
FROM ruby:2.7.3-alpine
WORKDIR /app
COPY Gemfile /app/Gemfile
RUN apk update \
&& apk add --no-cache --virtual build-deps \
build-base \
&& bundle install \
&& apk del build-deps
RUN apk update \
&& apk add --no-cache \
chromium \
font-noto \
fontconfig \
&& wget https://noto-website.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip \
&& mkdir -p /usr/share/fonts/NotoSansCJKjp \
&& unzip NotoSansCJKjp-hinted.zip -d /usr/share/fonts/NotoSansCJKjp/ \
&& rm NotoSansCJKjp-hinted.zip \
&& fc-cache -fv
ENV PUPPETEER_EXECUTABLE_PATH /usr/bin/chromium-browser
Gemfile 🔗
# frozen_string_literal: true
source 'https://rubygems.org'
gem 'puppeteer-ruby'
ビルド 🔗
$ docker build -t puppeteer_ruby_sample .
コード 🔗
# frozen_string_literal: true
require 'puppeteer'
launch_options = {
executable_path: ENV['PUPPETEER_EXECUTABLE_PATH'],
args: ['--no-sandbox']
}
Puppeteer.launch(**launch_options) do |browser|
page = browser.pages.first || browser.new_page
page.goto('https://takagi.blog/')
articles = page.query_selector_all('main article').take(5)
articles.each do |article|
date = article.query_selector('header small')['innerText'].json_value
title = article.query_selector('a')['innerText'].json_value
puts "#{date}: #{title}"
end
end
実行 🔗
$ docker run --rm -it -v ${PWD}:/app puppeteer_ruby_sample ruby app.rb
2021/04/23: Docker環境でgruffのサンプルコードを動かす
2021/04/19: IOSTの価格を取得するワンライナーバッチ
2021/04/16: Ubuntu + RMagickでフォントが無くて画像の出力ができない
2021/04/13: 【Docker】コンテナ内のファイルをホストにコピーする
2021/04/09: SBI証券のポートフォリオのCSVファイルの文字コードをUTF-8に変換する
リポジトリ 🔗
Docker環境でgruffのサンプルコードを動かす
gruff というグラフ描画用GemのサンプルコードをDockerで動かしてみるメモ
手順 🔗
必要なファイルの準備 🔗
Dockerfile、Gemfile、Gemfile.lockを準備する
Gemfile.lockの中身は空で良い
FROM ruby:2.7.3
RUN apt-get update -qq && apt-get install -y ghostscript
WORKDIR /app
COPY Gemfile Gemfile.lock /app
RUN bundle install -j 4
# frozen_string_literal: true
source 'https://rubygems.org'
git_source(:github) {|repo_name| 'https://github.com/#{repo_name}' }
gem 'gruff'
touch Gemfile.lock
$ ls
Dockerfile Gemfile Gemfile.lock
3つのファイルができたらOK
ビルド 🔗
$ docker build -t gruff_sample .
$ docker run --rm gruff_sample ruby -v
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-linux]
サンプルコード 🔗
gruffリポジトリのREADME.md に載っているコード
require 'gruff'
g = Gruff::Line.new
g.title = 'Wow! Look at this!'
g.labels = { 0 => '5/6', 1 => '5/15', 2 => '5/24', 3 => '5/30', 4 => '6/4',
5 => '6/12', 6 => '6/21', 7 => '6/28' }
g.data :Jimmy, [25, 36, 86, 39, 25, 31, 79, 88]
g.data :Charles, [80, 54, 67, 54, 68, 70, 90, 95]
g.data :Julie, [22, 29, 35, 38, 36, 40, 46, 57]
g.data :Jane, [95, 95, 95, 90, 85, 80, 88, 100]
g.data :Philip, [90, 34, 23, 12, 78, 89, 98, 88]
g.data :Arthur, [5, 10, 13, 11, 6, 16, 22, 32]
g.write('exciting.png')
実行 🔗
$ docker run --rm -v "$PWD:/app" gruff_sample ruby app.rb
$ ls
Dockerfile Gemfile Gemfile.lock app.rb exciting.png
exciting.pngという画像ファイルができている
IOSTの価格を取得するワンライナーバッチ
IOSTを毎日自動で買うための検証バッチ
何時頃に安く買えるのか傾向を調べたかった
コインチェックの販売レート取得API(https://coincheck.com/ja/documents/exchange/api#buy-rate )を使った
$ echo `date "+%Y%m%d%H%M"`,`curl -s https://coincheck.com/api/rate/iost_jpy |jq -r .rate`
202104192038,8.14608102
crontab 🔗
1時間ごと動くようにcronにセットした
cronにセットする時は%をエスケープしないといけないっぽい
0 * * * * echo `date "+\%Y\%m\%d\%H\%M"`,`curl -s https://coincheck.com/api/rate/iost_jpy |jq -r .rate` >> /path/to/results.csv
参考 🔗
Ubuntu + RMagickでフォントが無くて画像の出力ができない
問題 🔗
RMagick(正確にはgruff )でグラフ画像を出力しようとしたらフォントが無くてコケた
irb(main):012:0> g.write('exciting.png')
Traceback (most recent call last):
14: from /usr/local/bin/irb:23:in `<main>'
13: from /usr/local/bin/irb:23:in `load'
12: from /usr/local/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
11: from (irb):12
10: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/base.rb:419:in `write'
9: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/base.rb:437:in `to_image'
8: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/line.rb:172:in `draw'
7: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/base.rb:468:in `draw'
6: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/base.rb:495:in `setup_drawing'
5: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/base.rb:557:in `setup_graph_measurements'
4: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/base.rb:792:in `setup_marker_caps_height'
3: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/base.rb:917:in `calculate_caps_height'
2: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/renderer/text.rb:54:in `metrics'
1: from /usr/local/bundle/gems/gruff-0.12.1/lib/gruff/renderer/text.rb:54:in `get_type_metrics'
Magick::ImageMagickError (unable to read font `helvetica' @ error/annotate.c/RenderFreetype/1338)
解決方法 🔗
ghostscriptを入れてあげればいいらしい
【Docker】コンテナ内のファイルをホストにコピーする
やりたいこと 🔗
Dockerコンテナ内で作ったファイルをホストマシン側に移したい
root@02964fcd0552:/# ls exciting.png
exciting.png
手順 🔗
docker cp {CONTAINER ID}:{パス} {ローカルパス}
CONTAINER IDの確認 🔗
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
02964fcd0552 ruby:2.7.2 "bash" 14 minutes ago Up 14 minutes strange_cartwright
コンテナからホストマシンにコピー 🔗
$ docker cp 02964fcd0552:/exciting.png .
参考 🔗
SBI証券のポートフォリオのCSVファイルの文字コードをUTF-8に変換する
問題 🔗
SBI証券のポートフォリオのページからダウンロードしたCSVファイルが文字化けしている
$ cat New_file.csv
"|[gtHIꗗ",
"ꊇ\",
"PTS\",
"F8",
"ÍF1-8",
"y[WF1",
"Miz/aj",
"t@h","t","","擾P","ݒl","O","Oij","v","vij","]z",
"OHteiہ|l`whr@rčiroTOOj","----/--/--",195173,15371,15368,-3,-0.02,-58.55,-0.02,299941.86,
"a|eobW@m`rc`pPOO","----/--/--",9974,26688,28462,-4,-0.01,+1769.38,+6.65,28387.99,
"M(z/a)v",
"]z","܂ݑv","܂ݑvij","O","Oij",
328329.86,+1710.83,+0.52,-62.54,-0.02,
"Miz/݂ NISAaj",
"t@h","t","","擾P","ݒl","O","Oij","v","vij","]z",
"OHteiہ|l`whr@r@ioρj","----/--/--",34045,11749,13636,+106,+0.78,+6424.29,+16.06,46423.76,
"OHteiہ|l`whr@rčiroTOOj","----/--/--",35793,13038,15368,-3,-0.02,+8339.76,+17.87,55006.68,
"OHteiہ|l`whr@r@oXiWYϓ^j","----/--/--",33003,11614,12846,+23,+0.18,+4065.96,+10.61,42395.65,
"OHteiہ|l`whr@r@SEiI[EJg[j","----/--/--",21630,12329,14548,+4,+0.03,+4799.69,+18.00,31467.32,
"OHteiہ|l`whr@r@iCfbNX","----/--/--",22441,14112,16589,-7,-0.04,+5558.63,+17.55,37227.37,
"rah|rahEoK[hEroTOOCfbNXEt@h","----/--/--",20826,13605,14100,-4,-0.03,+1030.88,+3.64,29364.66,
"M(z/݂ NISAa)v",
"]z","܂ݑv","܂ݑvij","O","Oij",
241885.45,+30219.24,+14.28,+410.66,+0.17,
"v",
"]z","܂ݑv","܂ݑvij","l","lij",
570215.32,+31930.08,+5.93,0,0
原因 🔗
文字コードがShift-JISだったから
unknown-8bitはShift-JISのことみたい
$ file --mime New_file.csv
New_file.csv: text/plain; charset=unknown-8bit
解決方法 🔗
UTF-8に変換する
$ iconv -f SHIFT_JIS -t UTF-8 New_file.csv > New_file.utf-8.csv
$ cat New_file.utf-8.csv
"ポートフォリオ一覧",
"一括表示",
"PTS株価表示",
"総件数:8件",
"選択範囲:1-8件",
"ページ:1",
"投資信託(金額/特定預り)",
"ファンド名","買付日","数量","取得単価","現在値","前日比","前日比(%)","損益","損益(%)","評価額",
"三菱UFJ国際−eMAXIS Slim米国株式(S&P500)","----/--/--",195173,15371,15368,-3,-0.02,-58.55,-0.02,299941.86,
"大和−iFreeレバレッジ NASDAQ100","----/--/--",9974,26688,28462,-4,-0.01,+1769.38,+6.65,28387.99,
"投資信託(金額/特定預り)合計",
"評価額","含み損益","含み損益(%)","前日比","前日比(%)",
328329.86,+1710.83,+0.52,-62.54,-0.02,
"投資信託(金額/つみたてNISA預り)",
"ファンド名","買付日","数量","取得単価","現在値","前日比","前日比(%)","損益","損益(%)","評価額",
"三菱UFJ国際−eMAXIS Slim 国内株式(日経平均)","----/--/--",34045,11749,13636,+106,+0.78,+6424.29,+16.06,46423.76,
"三菱UFJ国際−eMAXIS Slim米国株式(S&P500)","----/--/--",35793,13038,15368,-3,-0.02,+8339.76,+17.87,55006.68,
"三菱UFJ国際−eMAXIS Slim バランス(8資産均等型)","----/--/--",33003,11614,12846,+23,+0.18,+4065.96,+10.61,42395.65,
"三菱UFJ国際−eMAXIS Slim 全世界株式(オール・カントリー)","----/--/--",21630,12329,14548,+4,+0.03,+4799.69,+18.00,31467.32,
"三菱UFJ国際−eMAXIS Slim 先進国株式インデックス","----/--/--",22441,14112,16589,-7,-0.04,+5558.63,+17.55,37227.37,
"SBI−SBI・バンガード・S&P500インデックス・ファンド","----/--/--",20826,13605,14100,-4,-0.03,+1030.88,+3.64,29364.66,
"投資信託(金額/つみたてNISA預り)合計",
"評価額","含み損益","含み損益(%)","前日比","前日比(%)",
241885.45,+30219.24,+14.28,+410.66,+0.17,
"総合計",
"評価額","含み損益","含み損益(%)","基準値比","基準値比(%)",
570215.32,+31930.08,+5.93,0,0
参考 🔗
PixelaをActiveRecordのように使う試み
※下書きにずっと放置してあって、記事も実装も中途半端な状態だけど一旦公開しておく 🔗
Pixela
の利用シーンの例として「“日"系列データベース」があったので、その使い方でやるとしたらどうやるか試してみた話。
ActiveRecordみたいに使えたらいいなと思ったのでサクッと作ってみた。
最初に言い訳として雑に作ってある。
とりあえず、findとwhereだけ実装した。
createは力尽きたので断念。
require 'pixela'
class PixelaRecord
attr_accessor :date, :quantity
def initialize(date:, quantity:)
@date = date
@quantity = quantity.to_i
end
class << self
def find(date = Date.today)
response = client.get_pixel(graph_id: graph_id, date: date)
new(date: date, quantity: response.quantity)
end
def where(from: nil, to: nil)
dates = client.get_pixel_dates(graph_id: graph_id, from: from, to: to)
dates.map do |date|
response = client.get_pixel(graph_id: graph_id, date: date)
new(date: date, quantity: response.quantity)
end
end
private
def client
@client = Pixela::Client.new(username: 'takagi', token: 'hogehoge')
end
def graph_id
self.name.downcase
end
end
end
class Distance < PixelaRecord
end
distance = Distance.find(Date.today)
puts "#{distance.date}: #{distance.quantity}km"
#=> 2020-11-21: 1km
distances = Distance.where(from: Date.parse('2020-10-01'), to: Date.parse('2020-10-31'))
distances.each do |distance|
puts "#{distance.date}: #{distance.quantity}km"
end
#=> 2020-10-01: 56km
#=> 2020-10-02: 67km
#=> 2020-10-03: 72km
#=> 2020-10-04: 40km
#=> 2020-10-05: 14km
#=> 2020-10-06: 37km
#=> 2020-10-07: 82km
#=> 2020-10-08: 100km
#=> 2020-10-09: 58km
#=> 2020-10-10: 71km
#=> 2020-10-11: 77km
#=> 2020-10-12: 3km
#=> 2020-10-13: 4km
#=> 2020-10-14: 44km
#=> 2020-10-15: 80km
#=> 2020-10-16: 2km
#=> 2020-10-17: 10km
#=> 2020-10-18: 64km
#=> 2020-10-19: 15km
#=> 2020-10-20: 10km
#=> 2020-10-21: 51km
#=> 2020-10-22: 74km
#=> 2020-10-23: 3km
#=> 2020-10-24: 67km
#=> 2020-10-25: 38km
#=> 2020-10-26: 41km
#=> 2020-10-27: 19km
#=> 2020-10-28: 65km
#=> 2020-10-29: 46km
#=> 2020-10-30: 74km
#=> 2020-10-31: 94km
感想 🔗
N+1問題が発生している
API制限があるのかわからないが、なかったとしてもあまりよろしくないと思う。
Deno Deployを試してみた
Deno Deploy (https://deno.com/deploy
) というDeno用のホスティングサービス?がリリースされたので、早速使ってみた。
詳しいことは公式サイトや、記事最後に載せている参考サイトを見ていただきたい。
サンプルにあった、「Hello World!」を返すだけのものをDeno Deployにデプロイした。
GitHubにコードを上げて連携させるだけでデプロイできる。
成果物 🔗
$ curl https://takagi.deno.dev/
Hello World!
リポジトリ 🔗
感想 🔗
- 簡単すぎて逆にわかりにくかった(これだけでデプロイできるわけがないと思った)
- チュートリアルやサンプルも充実している気がするので良い
- データベースを使ったアプリケーションのデプロイも試してみたい
- ちょうどDeno入門して、デプロイ先を検討していたところだったので良いタイミングでリリースされた
- 料金の発表はまだみたいで、無料でどこまで使えるのかが気になるところ
参考 🔗
初代Nature Remoの中身
スマートリモコン「Nature Remo」が壊れてしまったので、捨てる前に中身を開けてみた。
ハードウェアは専門外なのでまったくわからん。基盤のバージョンは「v1.0.2-gdfaef4b」だった。
Wi-FiモジュールはESP-WROOM-02を使っているみたい。
約5年くらいお世話になった。いろいろ考えた結果、次はSwitchBotのものを使ってみることにした。
参考 🔗
【Mac】画像のファイル形式をHEICからPNGに変換する
DenoでHello, World!
v1.0.0がリリースされた時にも少し触ったけど、復習+記録として。
手順 🔗
Denoの環境構築 🔗
構築といってもDocker
deno () {
docker run \
--interactive \
--tty \
--rm \
--volume $PWD:/app \
--volume $HOME/.deno:/deno-dir \
--workdir /app \
hayd/ubuntu-deno:1.8.1 \
deno "$@"
}
source ~/.bashrc
$ deno --version
deno 1.8.1 (release, x86_64-unknown-linux-gnu)
v8 9.0.257.3
typescript 4.2.2
ファイル作成 🔗
console.log("Hello, World!");
実行 🔗
$ deno run hello.ts
Hello, World!
実質、TypeScriptでHello, World!になった。