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}

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

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

どうやるんじゃろうか。

以上です。

フックメソッドについて

特定のタイミングで実行されるものです。

意味不明だと思うので、解説します。

included

includeではありません、includedです。

なんとなく、見覚えがあったのですが、違いを全く理解していませんでした。

includedメソッドは、includeメソッドによってモジュールが他のモジュールやクラスにインクルードされたあとに呼び出されます。引数にはモジュールをインクルードするクラスやモジュールが入ります。

ref.xaio.jp

どういうことか、コードで説明します。

module Foo
  def self.included(klass)
    puts "#{klass}にincludeされました"
  end
end

class Bar
  include Foo
end
=>Barにincludeされました

includeしたタイミングで上のputsが反応しています。

これを利用して、特異メソッド化する手法がよく使われます。

module Foo
  def self.included(klass)
    puts "#{klass}にincludeされました"
    klass.extend Methods
  end

  module Methods
    def foo
      'foo'
    end
  end
end

class Bar
  include Foo
end
Bar.foo
=> "foo"

extendでMethodsをBarのみに使用できるようにしています。 これでincludeした場合に特異メソッド化ができます。

includedは単なるフックメソッドとして、使用されており、inlucdeとは全く違うものでした。

ここの理解を全くしていなかったな。

特異メソッドでincludeする方法

何個か方法あるので、少しづつ紹介します。

クラス内でincludeする

module Foo
  def hello
    'hello'
  end
end

class Bar
  class << self
    include Foo
  end
end

Bar.hello
=> "hello"

特定のオブジェクトだけincludeする

x = Bar.new
class << x
  include Foo
end

x.hello
=> "hello"

Object#extend

これが一番一般的ですね。

y = Bar.new
y.extend Foo
y.hello
=> "hello"

方法としては、この3つあります。

意外にextendの用法を忘れてしまう・・・orz

クラス変数の注意点

クラス変数なんて、めったに使うことなんてないし、railsで使うならclass_attributeがあるんで、普通は必要ないです。

しかし、よくクラス変数やめろ!なんて言われることが多いと思うので、ここでおさらいしておきます。

クラス変数とは

そもそもクラス変数ってなんぞや?

クラス間で共有出来る変数ですね。

例えば、下記のようなものです。

class Foo
  @@x = 1

  def x
    @@x
  end

  def x=value
    @@x = value
  end
end

pry(main)> a = Foo.new
=> #<Foo:0x007f89982500e8>
pry(main)> a.x = 3
=> 3
pry(main)> b = Foo.new
=> #<Foo:0x007f8998343978>
pry(main)> b.x = 5
=> 5
pry(main)> a.x
=> 5

確かに共有されますね。

ただし、厄介なことがあります。

  • サブクラスにも継承される
  • クラス階層に属している

前者はわかりやすいと思うので、後者について説明します。

先程の続きです。

pry(main)> @@x = 6
(pry):17: warning: class variable access from toplevel
=> 6
pry(main)> b.x
=> 6

上記のように、mainクラスの時に変更したものも値が変更されてしまいます。

こういう罠が多いので、クラス変数は使うなってことになります。

けど、どうしてもクラス間で共有したいって時があります。

そういう場合はどうすればいいのかってことです。

あくまでもrails側の方法です。

class_attribute

railsではclass_attributeがあります。

これは、先程のサブクラスの継承を無しにして、なおかつトップレベルのものは参照されなくなります。

なので、railsでクラス間の値を共有したい場合は、class_attributeを使いましょう。

railsguides.jp

以上です。

mysqlの予約語に気をつけた方がいい

やられました...orz

直接sqlをいじることがなかったのですが、いじった時に気付きました。

mysql> show tables;
+-----------------------------------+
| Tables_in_foo |
+-----------------------------------+
| categories                        |
| comments                          |
| rankings                          |
| reads                             |
| schema_migrations                 |
| supplementals                     |
| taggings                          |
| tags                              |
| users                             |
+-----------------------------------+

こんな感じで表示されています。

ここでreadsテーブルのデータを取得しようとします。

mysql> select * from reads;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'reads' at line 1

なぜ、syntaxエラーになる???

おかしいだろ!ってなりました。

テーブルはきちんと作成されているのか?

reads.frm
reads.ibd

二つともきちんとある。

rails側では値が取得できている。

[1] pry(main)> Read.count
   (0.3ms)  SELECT COUNT(*) FROM `reads`
=> 0

わからん・・・。

試しにデータを投入しても、無事に投入できる。

ここで予約語っていう概念か?と思い調べました。

そうしたらREAD/READSが見つかりました。

ああ、そういうことかーってなりました。

予約語を使用する場合はバッククォートでエスケープすればいいです。

今思うと、上のrailssqlはきちんとバッククォートでエスケープしています。

今まで気にしなかったですが、予約語対策をしていたんですね。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 9.3 予約語