kazasiki's blog

プログラミングとかVRゲームとか

Sendagaya.rb #176 の個人的まとめ

sendagayarb.doorkeeper.jp に行ってきました。

主な内容

オブジェクト指向設計実践ガイド を読む

75ページ〜

以下、印象にのこった会話とか

ゴミ箱と化すService層

  1. Fat Controller を避けて Model に処理を移す
  2. Model が Fat になりはじめる
  3. 複数のテーブル(モデル)に跨ぐ処理は、Service層に分ける
  4. 徐々にゴミ箱と化すService層

MVCなWebアプリケーションフレームワークが如何なる標準を提供したところで、私たちは適切なクラスを設計をする義務から逃れることはできない。

api/mypage.json ... うっ頭が...

Restfulに作ろうとしても、アプリ側のAPIアクセスを減らしたいという要求から逃げられず作ってしまうRestfulではないAPIの代表。仕方がないね ¯\_(ツ)_/¯
クライアントとサーバは簡単には分離できない。サーバ側はjsonレンダリングをView層として分離させることで上手く対応するのがベター。model層にjsonレンダリングのロジックを入れると死ぬ。

Rails5.1にwebpackが入るかも

github.com

Railsユーザのwebpack使用率も高そうだし、いい感じなのでは。
rails webpacker:compile をdeploy時に実行するコマンドに追加する必要があるかも。

MVVM(フロントの話)

フロントで持ってるモデル層がサーバー側のコードでかぶったりしないか?という話。 フロント側で持っている責務(UIの遷移や入力/出力)と、サーバ側で持ってる責務(DBのトランザクション管理など)が明確に違うから今のところあんまり設計に悩んだことない。

ドメイン貧血症

mamelog.hatenablog.jp

コードが語らないので、コメントで無理矢理語らせるのです。

以上、読書はあまり進みませんでしたが、有意義な会でした。

非エンジニアがエンジニアの作業品質を管理するためにするべきたった1つのこと

非エンジニアがエンジニアの作業品質を管理するためにするべきことを書きます。

前置き

今回の記事は珍しく非エンジニア向けの記事です。例えば、エンジニアに作業を依頼するもしくは発注する企画職の人を想定しています。
エンジニアの人も知っておいたほうが良いと思います。なぜなら作業品質を依頼者に示すことはお互いが安心して開発をすすめるのに必須の要素だからです。
以下の文章では、非エンジニア側の人間を依頼者と記述します。基本的にコードは全く読めない人間を想定します。

依頼者目線で作業を分解する

作業を細かく分けるのは誰でもやると思います。ただ、このとき注意することが1つ有ります。依頼者が作業の完了を確認できる単位に分解することです。 例えば、DBを構築することは作業の単位になりません。エンジニアではない依頼者は作業の結果を確認できないからです。簡易的だとしても入力フォームを作り、入力結果を表示するところまでセットで1単位としましょう。作業の分割を依頼者による確認方法とセットで必ず考えましょう。

メリット

このように作業を分解することのメリットはいくつか有ります。
まず、進捗が依頼者の目でも確認できます。絶対にエンジニアの自己申告を信用してはいけません。
次に、依頼者が早い段階で品質を確認できます。早い段階で動かせる物を用意してもらえれば依頼者でもバグに気づくことが出来ます。テストは開発の終盤にまとめて行うことが多いですが、進捗の途中であってもバグがない状態を維持するようにお願いしましょう。バグを多く作る作業者になにか対策を施すこともできます。この判断が早めに出来るのは大きなメリットです。

デメリット

もちろんデメリットもあります。それは依頼者が作業の完了を確認できる単位エンジニアが最も効率的に作業できる単位と一致しないことです。
ただ、これは信頼関係を構築できてないエンジニアと協業するためには必要なコストです。許容しましょう。
また、依頼者にも確認するコストがかかりますが、これはそもそも必要なものでしょう。途中経過を見れたにも関わらず確認を怠った人に、最終結果に文句をつける権利はありません。

まとめ

以上に述べた作業の分解手法は、ストーリーポイントと言われる手法の一部分だけ抜き出したもので、どちらかというとアジャイル開発でよくつかわれるアプローチです。
また、エンジニアと依頼者に信頼関係があればこのようなテクニックは不要だと思えるかもしれません。が、私はそんなに贅沢が言える環境ではないので こういったテクニックが必要なのです。 ¯\_(ツ)_/¯

ActiveRecordから理解するgroupの使い方

この記事では主にActiveRecordのgroupメソッドやそれに関連するメソッドをみつつ、仕組みを説明します。あと、ActiveRecordの集計関数ちょっと癖あるよね的な話です。

テーブル作成

まずはじめにこういうテーブルを作ります。

exam_scores

  • 教科(subject) : String
  • 人の名前(name) : String
  • 点数(score) : Integer
bundle exec rails g model exam_scores subject:string name:string score:integer

データ作成

中身のデータは以下のようにします

subject name score
国語 田中 50
社会 田中 70
社会 山田 30
scores = [
  { subject: '国語', name: '田中', score: '50' },
  { subject: '社会', name: '田中', score: '70' },
  { subject: '社会', name: '山田', score: '30' }
]
ExamScore.create! scores

メソッドたち

次に、集計操作に使うメソッドを列挙します。

  • groupを作る条件を指定する
    • group
  • 集計
    • sum
    • average
    • count
  • 集計結果のフィルタリング
    • having

group + sum

まず、groupとsumを指定してみましょう。

subject name score
国語 田中 50
社会 田中 70
社会 山田 30
ExamScore.group(:subject).sum(:score)
=> {"国語"=>50, "社会"=>100}

scoreの合計値をsubject毎に表示できました。しかし、結果が ExamScoreインスタンスではなくハッシュですね?

SQLの出力を見てみましょう。

SELECT SUM("exam_scores"."score") AS sum_score, subject AS subject FROM "exam_scores" GROUP BY "exam_scores"."subject"
-- 50|国語
-- 100|社会

SQLの結果にnameが含まれません。これはexam_scoresテーブルをsubjectでgroup化した時に、nameが特定できないからです。

AcriveRecordは

を原則として設計されています。

これは ActiveRecordパターン という有名なオブジェクト指向設計パターンに由来しています。group + sum を使ったSQLの結果は、そのテーブルの1レコードと等しくなりません。故に、 ExamScore.group(:subject).sum(:score) の結果も ExamScoreインスタンスではなく、只のハッシュとして戻ってきます。

group + average + having

次に、subjectをnameに、sumをaverageに変えてみましょう。

subject name score
国語 田中 50
社会 田中 70
社会 山田 30
ExamScore.group(:name).average(:score)
=> {"山田"=>30, "田中"=>60}
# 実際は数字の部分はBigDecimal型でもどってきますが今回は省略

havingを使ってみましょう。

ExamScore.group(:name).having('avg(score) > 50').average(:score)
=> {"田中"=>60}

山田くんが消えましたね(^-^)
SQLを見てみます。

SELECT AVG("exam_scores"."score") AS average_score, name AS name FROM "exam_scores" GROUP BY "exam_scores"."name" HAVING avg(score) > 50
-- 60.0|田中

結果がハッシュなのは先ほどと同様ですが、今回は2つ注意点が有ります。
1つ目は、havingメソッドが 引数をそのまま文字列としてSQLに入れること です。

例えば、 avg(score)average_score に変えます。

ExamScore.group(:name).having('average_score > 50').average(:score)
=> {"田中"=>60}

とすれば、SQLでも avg(score)average_score に変わります。

SELECT AVG("exam_scores"."score") AS average_score, name AS name FROM "exam_scores" GROUP BY "exam_scores"."name" HAVING average_score > 50
-- 60.0|田中

になります。

2つ目は、havingメソッドを入れる場所は、集計メソッド(sum, average, count)よりも前にする必要があります。 集計メソッドの戻り値はその時点でハッシュにされてしまうからです。

先ほどのrubyコードでhavingメソッドを最後に持ってきてみます。

ExamScore.group(:name).average(:score).having('average_score > 50')

実行結果は以下のようになります。

NoMethodError: undefined method `having' for #<Hash:0x007f5c837ec778>
    from (irb):11
    from /share/git_dir/simple_app/vendor/bundle/ruby/2.3.0/gems/railties-4.2.3/lib/rails/commands/console.rb:110:in `start'
    from /share/git_dir/simple_app/vendor/bundle/ruby/2.3.0/gems/railties-4.2.3/lib/rails/commands/console.rb:9:in `start'
    from /share/git_dir/simple_app/vendor/bundle/ruby/2.3.0/gems/railties-4.2.3/lib/rails/commands/commands_tasks.rb:68:in `console'
    from /share/git_dir/simple_app/vendor/bundle/ruby/2.3.0/gems/railties-4.2.3/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
    from /share/git_dir/simple_app/vendor/bundle/ruby/2.3.0/gems/railties-4.2.3/lib/rails/commands.rb:17:in `<top (required)>'
    from bin/rails:8:in `require'
    from bin/rails:8:in `<main>'

¯\_(ツ)_/¯

まとめ

集計関数は ActiveRecordの原則の外にあるので、ActiveRecordからDBに触り始めた人には馴染みにくく、関係するメソッドにも癖が強いです。 ただ、集計関数こそSQLの醍醐味なので、しっかり使えるようになりましょう。

Effective "if-else" ~分岐に関わる達人の思考~

経験豊かなプログラマの皆さんであればif文はご存知だと思います。
この記事では、様々な言語でのif文の機能や書き方の違いに触れつつ、より安全なif文の書き方を導きます。要点は、静的言語でコンパイルエラーになるような書き方は動的言語でもしないほうが良い、もしくは場所を選ぶということです。
言語によって文だったり、式だったり、関数だったりしますが、この記事では文という表現を主に使います。

機能の違い

if文は言語によって様々な文法が存在します。が、ここでは主にその機能の違いに触れます。

基本(分岐)

一番基本的な書き方は以下のようなものだと思います。rubyっぽい書き方です。

if 条件
  条件がtrueだったら何かする
end
if 条件
  trueの場合に実行する文
else
  falseの場合に実行する文
end

初期化

golangで実装されている機能です。http://golang.jp/effective_go#if

if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

for文のように、if文の中だけで使う変数の初期化を行います。
これは非常に便利な手法なのですが、他の言語での良い代替手段はほぼありません。Cでよくある書き方は

if(  (fp = fopen( filne_name, "r" )) == NULL ){
  printf( "%sファイルが開けません¥n", filne_name );
  return -1;
}

ですが、あんまり綺麗ではありません。結局、変数(fp)のスコープも限定できません。
if文を全てメソッドに分離させれば同じことができますが、現実的ではありません。コンセプトは理解しておくといいでしょう。for文のiのスコープを限定でないとしたらゾッとしますね。(C言語からは目を背けながら)

戻り値

rubyではif文は正確には式であり、戻り値を返します。条件部分がtrueであれば、trueの場合に実行される部分の最後の文の戻り値が返されます。
それを利用して以下のような記述ができます。

# if式の結果を変数に入れる
result =
  if 条件
    trueの場合に実行する文
  else
    falseの場合に実行する文
  end

また、haskellでも同様に戻り値を返します。haskellは静的型言語であるため、戻り値の型が揃っていなければコンパイルエラーになります。

変数の代入検査

一部の言語においてif式が戻り値を返すことを説明しましたが、戻り値を返さない場合でも同じようなことはできます。例えば、Javaでは以下のようになります。

// if文の結果を変数に入れる
int result;
if (条件) {
  result = trueの場合に実行する文;
} else {
  result = falseの場合に実行する文;
}
System.out.print(result);

Javaで面白いのは、例えば以下の記述はコンパイルエラーになります。

int result;
if (条件) {
  result = trueの場合に実行する文;
} else {
  falseの場合に実行する文;
}
System.out.print(result);

これがコンパイルエラーになるのはelseの場合にresultに値が代入されないからです。このコンパイルエラーを回避する方法は大きく2つあります。
1つはresultの宣言時に初期値を入れることです。これはif文の条件がtrueになるのがあくまでも特殊なケースである場合に有効です。基本的には初期値のままで、特殊な場合にif文の中で値が設定されます。ただ、この方法は破壊的代入と言われ、あまり推奨されません。変数の状態が確認しにくくなるからです。
もう1つは元の通りelseの場合に値を入れる方法です。値は初期値と同じかもしれませんが、代入を複数回行わないため、変数の状態が確認しやすくなります。

条件の判定

if文において何がtrueで何がfalseか、です。私の知る範囲においてよくあるのは、0, null, falseがfalseで、それ以外は全てtrueというものです。golanghaskellではtrue/false以外を返す式を条件に入れるとコンパイルエラーになります。

シンタックスシュガー

if文には様々なシンタックスシュガー(書き換え)がありますが、有名どころはやはり三項演算子と後置if文でしょう。

三項演算子(?演算子

C言語Javaのようなif文が戻り値を返さない言語でも、1行で戻り値の代入までを明示することが出来ます。可読性が下がるという理由で嫌われがちですが、80~100文字程度で収まる場面なら使っていいと思ってます。ワンライナー御用達。

result = 条件 ? trueの場合に実行する文 : falseの場合に実行する文

後置if文

RubyCoffeeScriptにあるシンタックスシュガーです。以下のように書きます。

trueの場合に実行する文 if 条件

片方の条件の処理が長いときにif文を閉じるところまで読まずに済むというメリットが有ります。ネストも減ります。

# before
def do_something 
  if condition
    method0
  else
    method1
    method2
    method3
    method4
    method5
    method6
  end
end

# after
def do_something 
  return method0 if condition
  method1
  method2
  method3
  method4
  method5
  method6
end

テクニック

条件式を変数に入れて持ち回る

条件式を変数に入れて持ち回れば、例えばこんな書き換えが出来ます。
あと、条件式に尤もな名前を付けるのは常に良い習慣です。

# before
def do_something(a, b)
  if a > 0 && b > 0 && a > b
    c = a
    d = b
  else
    c = b
    d = a
  end
  puts "c:#{c}, d:#{d}"
end

# after
def do_something(a, b)
  condition = a > 0 && b > 0 && a > b
  c = condition ? a : b
  d = condition ? b : a
  puts "c:#{c}, d:#{d}"
end

等価演算子の変数を右側に書く

等価演算子の==と、代入の=を書き間違えたときにコンパイルエラーにしてくれるための技法です。C言語ではよく書いてました。
読みにくいので皆しなくなりました。条件式にboolean型しか許されない言語では無用な心配です。
ただ、現在においても==と=を間違えるのは比較的ポピュラーなバグであるというのは覚えておいたほうが良いです。

if (1==a) {
  do_something
}

まとめ

冒頭に書いたとおり、静的言語でコンパイルエラーになるような書き方は動的言語でもしないほうが良い、もしくは場所を選ぶの一言です。 それを学ぶためにも動的言語でプログラムを覚えた人は一度はモダンな静的言語のプログラムを覚えるのは良い刺激になると思います。
あと、if文のtrue/falseの時の実行内容を見比べやすくする意識を持つとよいです。

言い訳

検索などをせずに記憶をもとに書いたのでプログラムが間違ってるかもしれません。勿論動作確認もしてません。わかればいいのだ。

Twitterの報告(通報)を押す前に知ってほしい事

あなたがもしTwitterが公開している報告に関するポリシーを熟読したことがあるのであれば、私から特に言うことは特に有りません。ここから先を読む必要も有りません。ぜひ報告(通報)機能を使ってTwitter上の治安を守る助けをしてください。

さて、この文を読んでいるということはあなたは報告に関するポリシーを読んだことがない、もしくは単にこの先の文に興味があるということでしょう。 著作権・ポルノなどについてはこの記事では触れません。この記事ではもっと微妙なケースについて具体例を取り挙げます。それは「自分にとっては迷惑極まりないユーザ及びツイートだったとしても、それを報告すべきでない」というケースです。

例えば、Twitter経由で執拗なナンパを仕掛けるアカウントがあるとします。実際良く見かけます。このアカウントは何かの機会で知り合った女性に1日4-5件のリプライを送ります。単にツイートに対するリアクションだったり「今度一緒に食事に行こう」という露骨な誘いだったり。半匿名なSNSではありがちなことです。 もし、あなたが女性側であればまずブロック機能を使うでしょう。そこまでは問題有りません。ここで議題にしたいのはこのアカウントをTwitterの運営に報告するかです。

Twitterの2016/06/08現在のポリシーに沿って考えれば、このアカウントは報告すべきではありません。該当する条項がないからです。迷惑なアカウントでありますが、それはTwitterの運営の効力をもって解決すべき問題ではありません。

ブロック機能はユーザ側に閉じた機能です(多分)。自由に使えばいいでしょう。しかし、報告機能は運営がその集計によってなんらかの決定(BANとか)を行います。ならば、報告するかどうかの判断は絶対に運営のポリシーに従うべきです。

Twitterでインプレッションを増やす簡単な(汚い)方法

Twitterでインプレッションを増やす簡単な方法を教えます。 一応1週間弱ほど実験して効果があることは確認済みです。

f:id:kazasiki:20160523103905p:plain

5/18に実験開始して2日間だけ異常に増えてるのか分かるかと思います。
5/20が0になってるのは多分計測エラーです。そのあと2日間も10万インプレと以前に比べればそこそこの大きい数字になってるかと思います。

そもそもTwitterにおけるインプレッションとは?

Twitterにおけるインプレッションとはあなたのツイートが他のユーザに表示された表示された回数です。 普通はTLに表示された回数とほぼ同義になります。フォロワーが多かったりRTが多かったりするとそれに伴って大きくなる数字です。

簡単にインプレッションを増やす方法

結論から述べます。方法は以下の手順です。

  1. RT数が伸びてるツイートを見つける。
  2. そのツイートに対してリプライを送る。

細かいは話は順を追って説明します。

この方法でインプレッションが増える理由

まず、何故この方法でインプレッションが増えるのかを説明します。
Twitterの仕様として、ツイートの詳細情報を確認する際にそのツイートへのリプライを併せて表示するという仕様があります。
この仕様はリプライしたユーザとの関係性(フォロワーかどうか)などは全く関係しません。 つまり、10万人に見られるツイートにリプライを送ればそのリプライも10万人に見られるということになります。
実際は単にツイートを見ることとツイートの詳細情報を表示するのには乖離が有りますが、細かいことはいいんだよ。

RT数が伸びてるツイートの見つけ方

さて、理由がわかったところで細かい方法を説明します。RT数が伸びてるツイートをどうやって見つけるのか、です。
私は以下の様なブックマークを作りました。

https://twitter.com/search?f=tweets&q=lang%3Aja%20min_retweets%3A5000

このURLはツイッターの検索機能を使って以下の条件でツイートを検索します

  • "lang:ja" => 日本語
  • "min_retweets:5000" => 5000回以上RTされた

大抵の場合はこれで十分だと思います。 もっと頑張りたい場合はこちらのTwitterアカウントをチェックすると良いかと思います。

twitter.com

リプライの内容

次はどんなリプライを送るか、です。私は以下のことに気をつけています。

  • スパムっぽくしない
  • 礼儀正しく、送り先に失礼のないようにする
  • あんまり気の利いたことは言わず直感に任せる

上記のことに気をつけていれば邪険にされることは有りません(多分)
具体例としてはこんな感じです。

まとめ

ぶっちゃけ実験的な意味合いが強く、インプレッションが増えていいことがあったかと言われると特に無いです。
プロフィールへのアクセスは倍増しましたが、フォロワーは増えてません。アカウントの性質にも寄るかもしれません。
ただ、Twitterにこういう性質というか抜け穴的な仕様が存在することは認識してたほうがなにかと良いかと思って記事にした次第です。
あと、この性質を利用して業者への広告を貼る悪質なアカウントもやはり存在するようです。