会社のrubyのバージョンが2.4.0以降にあげられないので `Array#-` と同等のスニペットをおいておく
irb(main):010:0> class Array irb(main):011:1> def - other irb(main):012:2> copy = self.dup irb(main):013:2> other.each do |other_array_item| irb(main):014:3* copy.delete other_array_item irb(main):015:3> end irb(main):016:2> copy irb(main):017:2> end irb(main):018:1> end => :- irb(main):019:0> a = [1, 2, 3] => [1, 2, 3] irb(main):020:0> b = [1, 2, 4] => [1, 2, 4] irb(main):021:0> a - b => [3]
参考
自作クラスのテスト用にComparableモジュールと宇宙船演算子を使って同じオブジェクトか比較しやすくする
rubyでちょっとした処理を担当する自作クラスのテストをするとき、同じオブジェクトがどうかの比較する際、
assert_equal expect_obj.hash_value, actual_obj.hash_value # hash_valueはオブジェクトのidではないオブジェクトに特有の値
というような式をよく書いていました。ですが、そんなに広く使われないクラスであることがわかっているなら ==
演算子の挙動をいじってしまって、
assert_equal expect_obj, actual_obj
とかけるようにしちゃっていいかも、ということでその方法をメモ。
class MyClass include Comparable # 宇宙船演算子の内容を元に `<=`, `>=` を使えるようにしてくれる def <=> other # 宇宙船演算子を使って、`<`, `==`, `>` 用の動作を定義 return nil unless other.is_a? MyClass hash_value <=> other.hash_value # MyClassのオブジェクトが同一かどうか判断するための比較に、hash_valueの比較を使う end def hash_value # テストでも作りやすい値を元にハッシュ値を計算する end end
参考
- 作者: Peter J. Jones,arton,長尾高弘
- 出版社/メーカー: 翔泳社
- 発売日: 2015/01/09
- メディア: 大型本
- この商品を含むブログ (13件) を見る
rubyでライブラリなどを書くとき、Rakefileと相対パスのrequireを使ってクラスの取得やテストをやりやすくする
+---+ lib | +-+ some_module | +-+-- a_processor | | +-- order.rb | | +-- item.rb | +-- b_processor | | +-- order.rb | | +-- item.rb | +-- composer.rb # composerは a_processor, b_processor 以下のクラスを必要とする +---+ test # ここから一括で回せるようにしたい +-+ some_module +-+-- a_processor | +-- order_test.rb | +-- item_test.rb +-- b_processor | +-- order_test.rb | +-- item_test.rb +-- composer_test.rb
といった構成のライブラリを作るとして、composer
書いたり、 地味にテストを自動実行できるようにどうしたら良いか調べるのがめんどくさかったのでメモ。
追加分
+---+ lib | +-+ some_module | +-+-- a_processor | | +-- order.rb | | +-- item.rb | +-- b_processor | | +-- order.rb | | +-- item.rb | +-- composer.rb | +-- a_processor.rb ** 追加 | +-- b_processor.rb ** 追加 +---+ test + Rakefile ** 追加 +-+ some_module +-+-- a_processor | +-- order_test.rb | +-- item_test.rb +-- b_processor | +-- order_test.rb | +-- item_test.rb +-- composer_test.rb
それっぽく他パッケージにあるクラスを全て読み込む
比較的苦しくない抑えどころに近いんですが、そのパッケージのクラスを丸ごと読み込んでくれるファイル用意して、それを require
すると楽そう。
# a_processor.rb require "#{File.dirname(__FILE__)}/a_processor/order.rb" require "#{File.dirname(__FILE__)}/a_processor/item.rb"
ただ、明示的にクラスの指定どうこうする必要がない場合は、 Dir
クラスなどを使って each で回すと良さそう。
# a_processor.rb をなくしてbuilder.rb 冒頭で Dir.glob("#{File.dirname(__FILE__)}/a_processor/*").each do |file| require file end
test以下のディレクトリのファイル全てをコマンド一つで実行する
今回は test
ディレクトリ以下で rake test
で全部実行できればよかったので、下記のような Rakefile
を test
直下に置く。
Rakefile
はおいたところで Rakefile
に書いたタスクが rake xxx
(xxxなしだと task :default
に定義したものが使われる)実行されるようになるので、書いてから、普通はトップディレクトリにおいて、 ./**/*_test.rb
-> ./test/**/*_test.rb
にしたほうがいいかとも思った。
# Rakefile # coding: UTF-8 require 'rake/testtask' task :default => [:test] Rake::TestTask.new do |test| test.test_files = Dir['./**/*_test.rb'] test.verbose = false end
なお、テストファイルで本番用のファイルを要求するときは、そのライブラリ用のベースクラス的なのを用意して、そこで require
しておくと楽。
# base_test.rb require 'test/unit' Dir.glob("#{File.dirname(__FILE__)}/a_processor/*").each do |file| require file end Dir.glob("#{File.dirname(__FILE__)}/b_processor/*").each do |file| require file end Dir.glob("#{File.dirname(__FILE__)}/**.rb").each do |file| require file end class SomeModule::BaseTest < Test::Unit::TestCase include SomeModule ... # 他のテスト用クラスは require "#{File.dirname(__FILE__)}/path/to/base_test.rb" class SomeModule::SomeTest < SomeModule::BaseTest
参考
gitのローカルリポジトリからお目当のタグを抽出する
今日は時間がないので渾身かも、と思ったコードに数行コメントつけてPOST!
- シェルは便利なんだけどシェルの結果の文字列を気軽に処理したい時、ruby書いたことある人にとってはrubyがとても楽
- rubyだとシェルのコマンドを
%x( command )
などで実行できる ruby xx.rb --params
などのコマンドライン引数はARGV
に配列で入ってる- 一時的にディレクトリを移動したいだけなら
Dir.chdir
にブロック渡すのが便利 - 他の人とディレクトリ構造が違う(ホームディレクトリなどは?) =>
ENV['HOME']
などENV
から取れるよ git fetch remote-repo --tags
で現在のステージの内容などを気にせずにgit tag
の出力だけ更新できるよgit tag
の出力はgit FETCH_HEAD
にあるtag
と一致していて*1、要するにgit
の管理対象のファイルの内容を読み書きしているわけではないので- 作業途中で同期のための雑な
git stash
しなくていいの嬉しい!
latest_tag = Dir.chdir("#{ENV['HOME']}/woshidan-test/sample-repo") do if fetch_remote puts "fetch tags from remote repository ..." %x( git fetch remote-repo --tags ) # ローカルリポジトリを最新にするコマンドを挟むことで自分を信じないことができる puts "done" end %x( git tag ).split("\n").select { |tag| tag.match(/お目当のタグの正規表現/) }.last end
参考
- http://uxmilk.jp/12947
- https://www.xmisao.com/2014/03/01/how-to-execute-a-shell-script-in-ruby.html
- http://blog.codebook-10000.com/entry/20140325/1395673232
*1:このファイルから出力されているわけではないだろうが
AWS CLIの設定ファイルにて名前付きプロファイルを利用して手軽にAWS CLIのIAMユーザを切り替える
普段業務でAWS CLIを使っているため、業務本番用のアカウントとちょっと個人の調査用のアカウントを分けて使いたいわけですが*1、 aws configure
コマンドで設定できるアカウントは一つだけです。
これではちょっと調査をするたびにクレデンシャル情報を引っ張ってきて入力するのがめんどくさくなるのが目に見えているので他の方法を考えます。
aws configure コマンドで何をしているか
aws configure
コマンドで何をしているかというか、 ~/.aws/config
と ~/.aws/credentials
にコマンドで与えた引数の入力を行なっています。
入力結果のファイルは下記のような形です。
~/.aws/config
[default] region = ap-northeast-1
~/.aws/credentials
[default] aws_access_key_id = ABCDEEGHEXAMPLEKEYID aws_secret_access_key = ABCdefgHIjkLM/OPQrstUVwxyzAbcsExampleKey
このファイルに何か見出しつけてもう1組書いてやったらできそうですね。
awsの設定ファイルで名前付きプロファイルを使う
[XXX]
のように見出しをつけて、認証情報などをその見出しごとに管理でき、これを 名前付きプロファイル
と言うそうです。
~/.aws/config
[default] region = ap-northeast-1 [profile user2] // credentialsと見出しが違うので注意 region=us-east-1 output=text // 出力形式も設定できる
~/.aws/credentials
[default] aws_access_key_id = ABCDEEGHEXAMPLEKEYID aws_secret_access_key = ABCdefgHIjkLM/OPQrstUVwxyzAbcsExampleKey [user2] aws_access_key_id=AKIAI44QH8DHBEXAMPLE aws_secret_access_key=je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
このように見出しをつけて、
$ aws ec2 describe-instances --profile user2
のように --profile
オプションをつけて実行するか、
$ export AWS_PROFILE=user2
のように環境変数を設定することでaws cliで利用するユーザーアカウントを切り替えることができます。
現場からは以上です。
参考
AWSのCLIで各種リソースやサービスにアクセスするためにIAMでアクセスキーを発行する
AWSのAthenaクライアント aws-sdk-athena を使ってみようとしたら、Athenaクライアントは aws cli
を利用していて、これを利用するために IAM
でAWSアカウントにアクセスするユーザを作る必要があったので手順メモ。
ちなみに IAM
は Identity and Access Management
だそうです。
事前準備
まず、事前準備としてルートアカウントの多要素認証(MFA)を有効化します。
MFAって多要素認証の略なんですね。なるほどです。
IAMユーザの作成
次にAWSの各種サービスにアクセスするためにIAMユーザを作成します。 IAMユーザを作成するにあたって、Webの画面からアクセス可能か、CLIからアクセスできる可能か、あるいは両方可能かを最初に設定します。
グループの作成・設定
IAMユーザを作成したあとは、そのユーザが所属するグループを設定します。 IAMではユーザーの権限をグループ単位で設定し、グループ単位で権限を設定する際には権限に関する設定が書かれたポリシーをいくつ採用するか、といった方式を用います。
なお、AthenaのCLIで結果をS3のバケットに書き込む場合(必須)、上記以外に書き込み先のS3のバケットへの読み書きの権限もいります(一敗)。
完了
確認画面で内容をもう一度確認したら「ユーザー作成」ボタンを押して完了です。
なお、シークレットアクセスキーはユーザー作成時かアクセスキーID発行*1時しかできないので注意しましょう。
*1:一応これは後からいつでもすぐできます
整形されたJSON文字列を見たい時、minifyしたい時、jqコマンドがとても便利だったという話
昨日頑張って sed
をかじったため
sed -e '1,10s/\([}\]]\),/\1,\ /g' -e '1,10s/:{/&\ /g' -e '1,10s/e,/&,\ /g' test.json
みたいな涙ぐましい事してたんですが、検索してたら jq
ってコマンドで簡単にできるらしいです。
まずは単に整形する
例えばこんなminifyされたJSONがあったとして
{"photo":{"taken_at":1506609841,"created_at":1504609841,"title":"さっちゃん"}}
これをこうじゃ。
$ echo '{"photo":{"taken_at":1506609841,"created_at":1504609841,"title":"さっちゃん"}}' | jq . { "photo": { "taken_at": 1506609841, "created_at": 1504609841, "title": "さっちゃん" } }
この標準出力をファイルへリダイレクトする事で、整形されたJSON文字列をファイルへ書き込むこともできます。
必要な要素だけを取り出してみる
jqコマンドは .キー名
で、そのキーの要素だけを取り出して表示させることが可能です*1。
これを利用すると、
$ echo '{"photo":{"taken_at":1506609841,"created_at":1504609841,"title":"さっちゃん"}}' | jq .photo { "taken_at": 1506609841, "created_at": 1504609841, "title": "さっちゃん" } $ echo '{"photo":{"taken_at":1506609841,"created_at":1504609841,"title":"さっちゃん"}}' | jq .photo.title "さっちゃん"
といったことができます。
配列の中の指定した要素を列挙する
さらに、配列については以下のようなことも可能です。
こういうJSONがあったとして
// sample.json { "order" : { "items" : [ { "id": 1, "name": "野球ボール", "price": 1296, "count": 3 }, { "id": 2, "name": "野球バット", "price": 1600, "count": 1 } ] } }
$ cat sample.json | jq .order.items [ { "id": 1, "name": "野球ボール", "price": 1296, "count": 3 }, { "id": 2, "name": "野球バット", "price": 1600, "count": 1 } ] // []で出力から配列の[]をはぐ $ cat sample.json | jq .order.items[] { "id": 1, "name": "野球ボール", "price": 1296, "count": 3 } { "id": 2, "name": "野球バット", "price": 1600, "count": 1 } // [].キー名で配列の中の要素でそのキーだけを列挙する $ cat sample.json | jq .order.items[].name "野球ボール" "野球バット"
出力から "(ダブルクオート) をはぐ
列挙した要素を他のコマンドの入力にしたい場合など "
が邪魔な場合には -r
オプションではげます
cat sample.json | jq .order.items[].name -r 野球ボール 野球バット
パイプが利用できる
なんと、 jq
の中だけでもパイプを使うことができます。これによって複雑なJSONの整形、要素の抽出や簡単な集計処理が行えます。
$ cat sample.json | jq .order.items[].price | awk '{m+=$1} END{print m;}' 2896
minifyする
-c
オプションを利用することで、整形されているJSON文字列をminifyすることも可能です。
$ jq -c . < sample.json {"order":{"items":[{"id":1,"name":"野球ボール","price":1296,"count":3},{"id":2,"name":"野球バット","price":1600,"count":1}]}}
めっちゃ便利! 現場からは以上です。
なんか awk
もやらねばいけないような気がするけど、明日は簡易自作コマンドの話をする。
参考
- http://leetmikeal.hatenablog.com/entry/20130117/1358423717
- https://qiita.com/takeshinoda@github/items/2dec7a72930ec1f658af
- https://nelsonslog.wordpress.com/2016/08/19/using-jq-to-minify-json/
*1:先ほどの . は全体を表示する、という意味