rubyでrakeの外部コマンドが失敗した時のエラーハンドリング

webpackerを使用していて、webpacker installって出ているのに、installは失敗していた。

これはムカつくからpullreq出してやろうと思ったら、すでにあった。

さすがだ。

fail hard when webpacker:install ./bin/yarn fails by sealocal · Pull Request #149 · rails/webpacker · GitHub

んで、実際のエラーハンドリング方法が思いつかなったので、これは参考になりました。

run './bin/yarn add webpack webpack-merge path-complete-extname babel-loader babel-core babel-preset-env coffee-loader coffee-script compression-webpack-plugin rails-erb-loader glob'
unless $?.success?
  puts set_color 'Failed to install webpack!', :red
  exit $?.exitstatus
end

$?はprocessの実行結果がいます。

これを見て、成功したかどうかを判定して、エラー内容を記述していました。

芸がこまかい。

そして、$?自体初めて知りました。

スレッドで最後の実行結果が入っているのですね。

$?自体がUNIXの実行結果の可否を調べるものなので、それと同じ動きをしていたってことですね。

% cat foo
=>cat: foo: No such file or directory

% echo $?
=>1

この辺はUNIX/Linux使いで、シェルとか作ってる人は分かるのでしょうが、自分は使ってないので、思いつかなかったので、これをきっかけに覚えておく。

variable $? (Ruby 2.4.0)

以上です。

nvmでnodeをインストールしているのに、deployしたら「/usr/bin/env: node: No such file or directory」が出る場合

なんでだよ!

node -v
=> v7.7.4

deploy→/usr/bin/env: node: No such file or directory

確実にインストールはされている。

ということで、シンボリックリンクを貼りましょう。

sudo ln -s "$(which node)" /usr/bin/node

ちなみにdebianのversion

cat /etc/debian_version
→8.3

これより古いと、別のパターンもあるので、、適切なものを選びましょう。

【Node.js】【PM2】 PM2でさくらVPSにNode.jsのアプリケーションをデプロイ - 人生dat落ち

rails5からはcontrollerテスト書くぐらいなら、requestテストを書くのがいいっぽい

controllerテストいらねみたいな流れだったけど、requestテスト書けって話だったのか。

Rails 3 と 4 において、controller spec の assigns は慣用されてきました。 今回の RSpec 3.5 はマイナーリリースであり、私たちは SemVer に準拠する以上、 既存の controller spec を壊さないようにしています。 既存の Rails アプリケーションで assigns を多用しているものについては、 rails-controller-testing gem を Gemfile に追加することで assigns と assert_template を復活させることができます。 RSpec はこの gem とシームレスに連携するため、controller spec は問題なく動作し続けるはずです。

これから新しく作成する Rails アプリケーションについては、 rails-controller-testing gem を追加するのはおすすめしません。 Rails チームや RSpec コアチームとしては、代わりに request spec を書くことを推奨します。 Request spec は一つのコントローラーアクションにフォーカスしますが、 controller spec とは違い、ルーターミドルウェアスタック、Rack リクエストやレスポンスも関与します。 これによって、より現実に近い環境でテストを実行し、controller spec で発生しがちな多くの問題を避けることができます。

RSpec 3.5 がリリースされました!

今までもassingsを使用していたcontrolelrテストだと、下記を入れれば今まで通り使用できます。

GitHub - rails/rails-controller-testing: Brings back `assigns` and `assert_template` to your Rails tests

これを使うぐらいなら、requestテストを書けって流れみたいだ。

なお、requestテストはcontrollerテストよりも早くなっているらしいので、早速試した。

Rails 5 では、request spec が Rails 4までの request spec や controller spec よりもかなり高速になっています。 これは Rails チームの Eileen Uchitelle1 のおかげです。

rails5のrequest

Finished in 0.33271 seconds (files took 5.43 seconds to load)
1 example, 0 failures

controller

Finished in 0.277 seconds (files took 11 seconds to load)
1 example, 0 failures

controllerの方がまだ早いかな・・・

流れ的には、requestテストの方がリアルなデータが取れるので、そっちを使えってことなので、今後はrequestテストを書いていく。

rails5ではrender methodをいたるところで使えるようになっている

rails5にアップデートするときに変更されていて、手を加えて変更したので、備忘録として書いておきます。

rails4.2時代のrenderのやり方

下記のように、requestを強引に擬似るような形でやっていました。

class Renderer
  def renderer
    controller = ApplicationController.new
    controller.request = ActionDispatch::TestRequest.new
    ViewRenderer.new(Rails.root.join('app', 'views'), {}, controller)
  end
end

class ViewRenderer < ActionView::Base
  include Rails.application.routes.url_helpers
  include ApplicationHelper

  def default_url_options
    { host: Rails.application.routes.default_url_options[:host] }
  end
end

Renderer.new.render

こういうやり方ではなく、ActionController::Rendererというクラスメソッドが作られました。

コントローラのアクションの外部で任意のテンプレートでレンダリングするActionController::Rendererを追加。 (Pull Request)

rails/rendering.rb at master · rails/rails · GitHub

ということで、下記のようにすることでrenderをいたるところで呼べるようになりました。

ApplicationController.renderer.render partial: 'topics/image', locals: { url: url, host_name: host_name }

以上です。

vueのオブジェクトの並び順について

jsonで値を渡しているのに、並び順が違う!って状態に陥りました。

なんでだ?って思うと、どうもObject.keys()の並び順に整理されるようですね。

オブジェクトを反復処理するとき、順序は Object.keys() の列挙順のキーに基づいており、全ての JavaScript エンジンの実装で一貫性が保証されていません。

jp.vuejs.org

ということで、ソートした状態にするには、 methodを使ってやるしかないようです。

以上です。

mysqlのorder by fieldで指定のID順に並べる

どういった時に使うんだよwwwって思いましたが、使う感じになりました。

select *
  from icons
 where id in(4, 5, 6)
 order by field(id, 5, 4, 6)

=>
mysql> select * from icons where id in(4, 5, 6) order by field(id, 5, 4, 6);
+----+------------------+-----------------+--------------------+-----------------+---------------------+---------------------+---------------------+
| id | icon_category_id | image_file_name | image_content_type | image_file_size | image_updated_at    | created_at          | updated_at          |
+----+------------------+-----------------+--------------------+-----------------+---------------------+---------------------+---------------------+
|  5 |                1 | 1f606.png       | image/png          |            2277 | 2017-03-13 15:09:17 | 2017-03-13 15:09:17 | 2017-03-19 00:50:29 |
|  4 |                1 | 1f601.png       | image/png          |            2235 | 2017-03-13 15:09:17 | 2017-03-13 15:09:17 | 2017-03-19 00:50:29 |
|  6 |                1 | 1f605.png       | image/png          |            2685 | 2017-03-13 15:09:17 | 2017-03-13 15:09:17 | 2017-03-19 00:50:29 |
+----+------------------+-----------------+--------------------+-----------------+---------------------+---------------------+---------------------+

確かに順番通りに返ってくる。

https://dev.mysql.com/doc/refman/5.6/ja/string-functions.html#function_field

FIELD(str,str1,str2,str3,…)

str1、str2、str3、… リスト内で str のインデックス (位置) を返します。str が見つからない場合は、0 を返します。

FIELD() へのすべての引数が文字列の場合は、すべての引数が文字列として比較されます。すべての引数が数値の場合は、数字として比較されます。それ以外の場合は、引数が倍精度として比較されます。

NULL ではどの値との等価比較にも失敗するため、str が NULL である場合は、戻り値が 0 になります。FIELD() は ELT() の補数です。

FIELD関数を使用しているってことなのね。

railsで使う場合

ids = [5, 4, 6]
Icon.where(id: ids).order("field(id, #{ids.join(',')})")

orderの中で文字列展開をして、使用しているってことですね。

rails5からはキチンとしたmethodがあるようです。

以上です。

参考

qiita.com

yoku0825.blogspot.jp

railsで複合uniqueのvalidationをつけてやる

mysqlrailsの両方に念のためつけます。

複合uniqueなんて、使うことあるのか?って思っていたけど、必要になりました。

忘れないために書いておきます。

まずは、migrationの書き方からです。

class CreateReactions < ActiveRecord::Migration
  def change
    create_table :reactions do |t|
      t.references :icon, index: true, foreign_key: true, null: false
      t.integer :reactionable_id, null: false
      t.string :reactionable_type, null: false
      t.string :user_cookie_value, null: false

      t.timestamps null: false
    end

    add_index :reactions, [:reactionable_id, :reactionable_type]
    add_index :reactions,←ここから
      [:user_cookie_value, :icon_id, :reactionable_id, :reactionable_type],
      unique: true,
      name: :index_reactions_on_uniq_cookie_and_ids
  end
end

そして、rails

class Reaction < ActiveRecord::Base
  belongs_to :icon
  belongs_to :reactionable, polymorphic: true

  validates_uniqueness_of :reactionable_id, scope: [:icon_id, :user_cookie_value, :reactionable_type]←scopeで判定する
end

scopeで値を絞ります。

参考

ActiveRecord::Validations::ClassMethods

以上です。

es2015の分割代入は、rubyのキーワード引数のイメージ

わかりづれー!ってなっていました。

{}←これ何?って感じで意味不明に陥りやすかったです。

function foo({x, y}) {
  return x + y
}

console.log(foo({x: 1, y: 2}));
=>3

x, yのvalueが代入されている。

これってrubyのキーワード引数と同じっぽく感じる。

def foo(x:, y:)
  x + y
end

foo(x: 1, y: 2)
=>3

それだけです。

新しい記法は慣れるのに時間がかかる。

なおかつ、それをほとんど使用しない身としては、とても覚えづらいけど、似た様なものと関連付ければ覚えやすくもなる。

has_manyのhas_manyのcountで効率よくパフォーマンスを出す

counter_cacheはできるだけ使いたくないんですよ。

無駄なカラムを持ちたくないということで、頑張りました。

対策としては、最初で全てのcountを手にするのが一番いいですね。

countは重たいので、先に何とかして値を入手したい・・・。

今回のサンプルでは、Blogがあって、Commentをたくさん持っていて、さらにCommentに対して複数のReactionをつけれるようなものです。 それのgroup化したReactionの数が欲しいという状況でした。

サンプル

class Blog < ActiveRecord::Base
  has_many :comments, dependent: :destroy
  has_many :comment_reactions, through: :comments

  def comment_reactions_count_map
    comment_reactions.group(:comment_id, :reaction_id).count←これが今回の肝
=> {[1, 1]=>7,
 [1, 315]=>1,
 [1, 332]=>1,
 [1, 708]=>1,
 [1, 825]=>1,
 [1, 864]=>1,
 [1, 1038]=>1,
 [1, 1044]=>1,
 [1, 1182]=>1,
 [1, 1229]=>1,
 [1, 1288]=>1,
 [2, 57]=>1,
 [2, 82]=>1,
 [2, 220]=>1,
  end
end
class Comment < ActiveRecord::Base
  belongs_to :blog
  has_many :comment_reactions
  has_many :reactions, through: :comment_reactions
end
class CommentReaction < ActiveRecord::Base
  belongs_to :comment
  belongs_to :reaction
end
class Reaction < ActiveRecord::Base
  has_many :comment_reactions
  has_many :comments, through: :comment_reactions
end

controller側

def show
    @blog = Blog.find(params[:id])
    @count_map = @blog.comment_reactions_count_map←これでIDをセットする。
end

view側はこんな感じになりますね。

サンプルなんで、適当です。

show.html.slim

  = render partial: "blog/comments", collection: @blog.comments.includes(:reactions), as: :comment

_blog/comments.html.slim

  - if comment.reactions.present?
    - comment.reactions.group(:id).each do |reaction|
      = image_tag reaction.image.url
      = @count_map[[comment.id, reaction.id]]←これでcountの値をもらう

こんな感じでどうでしょうか。

パフォーマンスはexplainでコツコツ攻略するのも大事ですが、可能ならば、sqlのクエリを減らすのがいいですね。

ES2015のIteratorとGeneratorについて

最近はVue.jsを個人で使用しているので、javascriptについても学んでいます。

ES2015の機能を少しづつ勉強しています。

基礎能力がないと結局開発の仕方がわからないなってのがあるので、勉強がてらに記事を書きます。

Iteratorって何?

そもそもIteratorってなんぞや?って感じですよね。

イテレータ - Wikipedia

プログラミング言語的には、反復子と呼ばれているようです。

反復するためのものの意味で反復子(はんぷくし)と訳される。繰返子(くりかえし)という一般的ではない訳語もある。

繰り返されるものに対して、呼ばれるようですね。

反復子(反復可能なオブジェクト)に対して、javascriptfor..ofを使って処理ができるようです。

ということは・・・

配列

配列は反復可能なオブジェクトになります。

const programming_languages = ["javascript", "ruby", "php"]

for (let language of programming_languages) {
  console.log(language)
}
=>javascript
=>ruby
=>php

なるほど。

ただし、Iteratorオブジェクトではありません。

Iteratorはメソッドのnextを使用することができます。

Iteratorプロトコル

Iteratorであるためには、「Iteratorプロトコル」を実装する必要があります。

Symbol.iterator()

const programming_languages = ["javascript", "ruby", "php"]

const it = programming_languages[Symbol.iterator]()
console.log(it.next())
=>Object {value: "javascript", done: false}
console.log(it.next())
=>Object {value: "ruby", done: false}
console.log(it.next())
=>Object {value: "php", done: false}
console.log(it.next())
=>Object {value: undefined, done: true}

doneの真偽値で次があるかどうかがわかるんですね。

独自のIteratorを作成する

Logクラスを作成して、試してみます。

class Log {
  constructor() {
    this.messages = []
  }

  add(message) {
    const now = Date.now();
    console.log(`ログ追加:${message}(${now})`)
    return this.messages.push({ message, time: now })
  }

  [Symbol.iterator]() {
    let i = 0
    const messages = this.messages
    return {
      next: () => i >= messages.length ?
        { value: undefined, done: true } : { value: messages[i++], done: false }
    }
  }
}

const log = new Log()
log.add("foo")
log.add("bar")
log.add("baz")
console.log(log)
for (let l of log) {
  console.log(l)
=>Object {message: "foo", time: 1487771847145}
=>Object {message: "bar", time: 1487771847146}
=>Object {message: "baz", time: 1487771847147}
}

Iteratorを返すだけなら、既存のメソッドを使用した方が楽なので、既存のメソッドで書き換えます。

  [Symbol.iterator]() {
    return this.messages[Symbol.iterator]()
  }

結果は同じになります。

Generatorについて

ここでGeneratorが出てきます。

Generatorってなんぞや?って感じですよね。

関数の一種と捉えることができますが、通常の関数とは違います。

  • 関数は制御(と値)を任意の場所から呼び出し側に戻すことができる
  • Generatorを呼び出すときにはすぐには実行されず、まずはIteratorが戻される。そのあとで、Iteratorのメソッドnextを呼び出す度に実行が進む。

意味がわからないって感じなので、実際のコードと動きを見ていきます。

定義方法

Generatorを使用するにはfunctionの後に*を付けます。

呼び出し側はyieldを使用します。

rubyでもあるのに、ここでもあるのか!って思いました。

rubyの場合はブロックを返してくれるやつでしたね。

function* frame_work() {
  yield 'ruby on rails'
  yield 'vue'
  yield 'react'
}

const it = frame_work()
console.log(it.next())
=>Object {value: "ruby on rails", done: false}
console.log(it.next())
=>Object {value: "vue", done: false}
console.log(it.next())
=>Object {value: "react", done: false}
console.log(it.next())
=>Object {value: undefined, done: true}

確かにIteratorオブジェクトになっとる。

双方向コミュニケーション

yieldは式なので、評価の結果、何らかの値になります。

では、どんな値なのでしょうか。

next呼び出し時の引数の値になります。

どういうことだ?ってことで例をみます。

function* introduction() {
  const name = yield '名前は何ですか?'
  const hobby = yield '趣味は何ですか?'
  return `名前は${name}です。趣味は${hobby}です。`
}

const it = introduction()
console.log(it.next())
=>Object {value: "名前は何ですか?", done: false}
console.log(it.next('javascript'))
=>Object {value: "趣味は何ですか?", done: false}
console.log(it.next('プログラミング'))
Object {value: "名前はjavascriptです。趣味はプログラミングです。", done: true}

一個前の値を保持していますね。

この辺の遅延評価を利用して、非同期処理を行えるようですね。

どうやるんじゃろうか。

以上です。