hirapi's blog

ちゃんとしたふりをする

redashの入力フォームで「特定の値 -> 無指定」として扱いたいときのSQL

redash.io

こんなテーブルがあったとして、

mysql> SELECT * FROM users;
+----+-----------+------+----------+
| id | name      | age  | group_id |
+----+-----------+------+----------+
|  1 | hirapi    |   25 |        1 |
|  2 | someone   |  100 |        1 |
|  3 | young man |    6 |        2 |
+----+-----------+------+----------+

redashの入力フォームに「対象グループID」を入力して、そのグループの人の名前を取ってきたいとする。 redashは {{フォーム名}} で入力値を取れるので、普通に書くと

SELECT name FROM users WHERE group_id = {{対象グループID}}

となる。

ここで、例えば「グループ関係無くみんなの名前も取れるようにしたい、クエリは分けたくない」みたいなこと言われたら、 「入力フォームに 0 を入れたらグループ無指定ということで全員の名前を取ってくる」というのはどうだろうということになる。

pythonでif文はさんでクエリ作るのもアリといえばアリだけど面倒くさいなあと思った結果↓↓

SELECT name FROM users WHERE {{対象グループID}} = 0 OR group_id = {{対象グループID}}

対象グループID が0だったら

mysql> SELECT name FROM users WHERE 0 = 0 OR group_id = 0;
+-----------+
| name      |
+-----------+
| hirapi    |
| someone   |
| young man |
+-----------+

対象グループID が1だったら

mysql> SELECT name FROM users WHERE 1 = 0 OR group_id = 1;
+---------+
| name    |
+---------+
| hirapi  |
| someone |
+---------+

クエリチューニングの面から見たら良くないかもしれないけどさくっと作れたし今回はおっけー。
別にredashに限った話ではないけど入力値をそのままクエリに突っ込むのがredashくらいしかなかった。

Prestoで配列の要素数を取得する

6.15. Array Functions and Operators — Presto 0.195 Documentation

cardinality(x) → bigint

Returns the cardinality (size) of the array x.

countとかsizeとかかなあと思ってたら違った、カーディナリティか……

入れ子配列のカラムを指定しても入れ子は展開されずに、なんていうかふつうにカウントされた。

PHPでTreasure Dataにpost2()

会社でTreasure Dataを使ってログの蓄積・集計をしていて、PHPからTreasure Dataにレコードを送ることがある。
そういうときはこのライブラリを使う↓↓

github.com

今まではこのREADMEにあるUsageのとおりこんな感じで送ってた↓↓

<?php

require_once __DIR__.'/vendor/autoload.php';

use Fluent\Logger\FluentLogger;
$logger = new FluentLogger("localhost","24224");
$logger->post("debug.test",array("hello"=>"world"));

ただこの間ちょっとレコード多くなるかもだから集計時間短くなるようにしたいなということがあって、こちらのブログを参考にtimeカラムに ID × 3600 みたいな任意の値を入れたくなった。

yebisupress.dac.co.jp

やってみて気づいたんだけど、こういう配列を前述の例のように post() に渡してもtimeカラムにはunixtimeが入ってしまう。

<?php

require_once __DIR__.'/vendor/autoload.php';

use Fluent\Logger\FluentLogger;
$logger = new FluentLogger("localhost","24224");
$logger->post("debug.test",array("time"=>7200)); // => timeカラムには普通に呼び出し時のunixtimeが入る

GitHubpost() のところを追ってみたところ、timeカラムの値は別途自動で入れられるらしい。

じゃあこのライブラリではtimeカラムに好きな値を入れられないかというと、そういうわけではない。

ここで post2()の出番。
冗談みたいな名前してるけどpublicなメソッド。

post() にはタグと配列を渡すけど、 post2() にはEntityクラスインスタンスを渡す。
post() も中身を見たら同じことをやっている。
Entityクラスのコンストラクタを見ると、第三引数にtimeっぽい値を渡せるとのこと。
post() の中身ではここに何も渡してないからデフォルトでnullになって、結果Entityオブジェクトのtimeプロパティにそのときの時刻が入る、というだけらしい。

というわけで自分でEntityクラスのインスタンスを作って post2() に渡してあげるとtimeカラムにちゃんとその値が入る。

<?php

require_once __DIR__.'/vendor/autoload.php';

use Fluent\Logger\FluentLogger;
use Fluent\Logger\Entity;
$logger = new FluentLogger("localhost","24224");
$entity = new Entity("debug.test",array("hello"=>"world"), 7200); // 第三引数にtimeカラムの値
$logger->post2($entity); // => timeカラムに7200が入る

レビューで先輩に教えてもらったとき何だその名前、と思ったけど合ってる。
どういう名前をつけるのがいいのかなあとちょっとだけ考えてみたけど、そもそもなんで送信用のメソッドを2つ作ったのかにもよるのかもしれない?

名前にちょっとだけウケたから投稿してみた。

Hanami公式入門ガイド日本語訳【5】Getting Started(4)

http://hanamirb.org/guides/1.1/getting-started/#writing-our-first-test

初めてのテストを書く

ブラウザでアプリケーションを開いたときに最初に目にする画面は、ルーティングが何も定義されていないときに表示されるデフォルトページです。

HanamiはWebアプリケーション開発手法としてビヘイビア駆動開発(BDD)を推奨しています。
最初の自作の画面を表示させるために、高次元の機能テストを書きましょう:

# spec/web/features/visit_home_spec.rb
require 'features_helper'

describe 'Visit home' do
  it 'is successful' do
    visit '/'

    page.body.must_include('Bookshelf')
  end
end

注意してほしいのは、Hanamiは最初からBDDの開発フローをサポートしていますが、特定のテスト用フレームワークに縛られているわけでもなければ、特別な統合システムやライブラリを備えているわけでもありません。

ここではMinitest(デフォルト)を使っていきますが、プロジェクトを作る際に --test=rspec オプションをつけることでRSpecを使うこともできます。
そうすればHanamiはRSpecのためのヘルパーやスタブのファイルを生成します。
(※ データベースのURLを微調整する必要があるときは .env.test を確認してください)

このコマンドを実行して、テスト用のデータベースにスキーママイグレーションしなければなりません:

% HANAMI_ENV=test bundle exec hanami db prepare

見ての通り、コマンドを実行する環境を指定するには環境変数 HANAMI_ENV をセットします。

リクエストを受ける

今テストを実行すると、失敗することがわかります:

% bundle exec rake test
Run options: --seed 44759

# Running:

F

Finished in 0.018611s, 53.7305 runs/s, 53.7305 assertions/s.

  1) Failure:
Homepage#test_0001_is successful [/Users/hanami/bookshelf/spec/web/features/visit_home_spec.rb:6]:
Expected "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Not Found</title>\n  </head>\n  <body>\n    <h1>Not Found</h1>\n  </body>\n</html>\n" to include "Bookshelf".

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

それではこのテストを通しましょう。
そのために必要な必要なコードをひとつずつ書き足していきましょう。

初めに足すのはルーティングです:

# apps/web/config/routes.rb
root to: 'home#index'

アプリケーションのルートを home コントローラの index アクションに指定しました(詳しくはルーティングの解説を見てください)。
次にindexアクションを作ります。

# apps/web/controllers/home/index.rb
module Web::Controllers::Home
  class Index
    include Web::Action

    def call(params)
    end
  end
end

これは何のビジネスロジックも実行しない空のアクションです。
それぞれのアクションには対応するビューがあり、ビューはRubyのオブジェクトであり、リクエストを満たすために追加する必要があります。

# apps/web/views/home/index.rb
module Web::View::Home
  class Index
    include Web::View
  end
end

アクションと同様にこのビューも空であり、テンプレートをレンダリングするだけです。
これが、テストを通すために編集する必要のあるファイルです。
先頭に "bookshelf" と追加するだけです。

# apps/web/templates/home/index.html.erb
<h1>Bookshelf</h1>

変更を保存し、テストをまた走らせてください。そうすると今度は通ります。
やったね!

Run options: --seed 19286

# Running:

.


Finished in 0.011854s, 84.3600 runs/s, 168.7200 assertions/s.

1 runs, 2 assertions, 0 failures, 0 errors, 0 skips

Hanami公式入門ガイド日本語訳【4】Getting Started(3)

http://hanamirb.org/guides/1.1/getting-started/#hanami-architecture

Hanamiのアーキテクチャ

Hanamiのアーキテクチャでは、同じRubyのプロセスで別々のHanamiアプリケーション(とRackアプリケーション)をホストすることができます。
これらのアプリケーションは /apps 以下にあります。
各アプリケーションが、ユーザー向けのWebページや管理画面、メトリクス、HTTP APIなどととしてプロダクトを構成することができます。

これらのパーツは、lib 以下にあるビジネスロジックに対する 分娩機構 (訳註:原文では" delivery mechanism ")です。
ここは、Modelが定義され、相互作用してプロダクトが提供する機能を構成するディレクトリです。

Hanamiのアーキテクチャクリーンアーキテクチャに強く影響を受けています。

Hanami公式入門ガイド日本語訳【3】Getting Started(2)

http://hanamirb.org/guides/1.1/getting-started/

前提知識

http://hanamirb.org/guides/1.1/getting-started/#prerequisites
始める前に、いくつかの前提を確認しましょう。
まず、webアプリケーション開発の基礎知識はあるものと仮定します。

また、Bundler、Rake、ターミナル操作、MVCモデルでのアプリケーション構築にも馴染みがあるものとします。

最後に、このガイドではSQLiteを使います。
もしこのガイドに沿って手を動かしたいなら、手元の環境でRuby 2.3以上、SQLite 3以上が動くことを確認しておいてください。

新しいHanamiプロジェクトを作る

http://hanamirb.org/guides/1.1/getting-started/#create-a-new-hanami-project
新しいHanamiプロジェクトを作るには、RubygemsからHanami gemをインストールする必要があります。
そうすると、新しいプロジェクトを作るための hanami コマンドが使えるようになります。

% gem install hanami
% hanami new bookshelf

(※ デフォルトでは、プロジェクトはSQLiteを使うように設定されます。
実際の開発のために、このようにしてデータベースエンジンを指定できます: % hanami new bookshelf --database=postgres

これで、カレントディレクトリに bookshelf という新しいディレクトリが作られます。
そこに何があるか見てみましょう:

% cd bookshelf
% tree -L 1
.
├── Gemfile
├── Rakefile
├── apps
├── config
├── config.ru
├── db
├── lib
├── public
└── spec

6 directories, 3 files

知っておかないといけないことはこちらです、

  • Gemfile は、(Bundlerを使って)Rubygemsの依存関係を定義します
  • Rakefile はRakeタスクを記述します
  • apps は1つ以上のRackアプリケーションを含みます
    ここに、最初に作られた Web と呼ばれるHanamiアプリケーションがあります。
    ここには、コントローラー、ビュー、ルーティング定義、テンプレートがあります。
  • config には設定ファイルがあります
  • config.ru はRackサーバー用です
  • db にはデータベーススキーママイグレーションがあります
  • lib にはビジネスロジックと、entityとrepositoryを含むドメインモデルがあります
  • public にはコンパイル済みの静的ファイルを置きます
  • spec にはテストがあります

先に、Bundlerで必要なgemをインストールしましょう。そうすると開発用のサーバーを立ち上げられるようになります。

% bundle install
% bundle exec hanami server

すると…… http://localhost:2300/ で初めてのHanamiプロジェクトの恩恵にあずかることができます!
こういう画面を見ることができるはずです。(訳註:画像は省略)

Hanami公式入門ガイド日本語訳【2】Getting Started(1)

http://hanamirb.org/guides/1.1/getting-started/

はじめよう

こんにちは。このページを読んでいるということは、あなたはHanamiについてもっと学びたいと思っていることでしょう。
すばらしい、おめでとう!
保守しやすく、安全で、高速で、テストしやすいwebアプリケーションを作る新しい方法を探しているのなら、きっと大丈夫です。

Hanamiはあなたのような人々のために作られています。

あなたがまったくの初心者だろうと経験豊富な開発者だろうと、 この入門はとてもハードかもしれない ということを先にお伝えしておきます。
私たちは時を経て「物事はこうあるはずだ」という予測を確立していきます、それを変えることは苦しいことかもしれません。 ですが、変化無しに挑戦は無く、 挑戦なしに成長は無いのです。

Hanamiの特徴がおかしく思えることもあるでしょうが、それはあなたがおかしいということではありません。
それは習慣の問題かもしれないし、設計に失敗しているのかもしれないし、あるいはバグかもしれません。

コミュニティの全員が、Hanamiをもっと良くするために日々全力を尽くしています。

このガイドでは、初めてのHanamiプロジェクトをセットアップし、簡単な本棚のwebアプリケーションを作ります。
私たちはHanamiの主要なコンポーネントに触れることになります、そしてそれら全てはテストの上に成り立っています。

もし一人ぼっちだと感じたり、くじけそうになっても、あきらめないで、私たちのチャットに加わって助けを求めてください。
きっと、喜んであなたとお話をする人がいます。

楽しんで。
Luca Guidi
Hanami開発者