vueのオブジェクトの並び順について
jsonで値を渡しているのに、並び順が違う!って状態に陥りました。
なんでだ?って思うと、どうもObject.keys()
の並び順に整理されるようですね。
オブジェクトを反復処理するとき、順序は Object.keys() の列挙順のキーに基づいており、全ての JavaScript エンジンの実装で一貫性が保証されていません。
ということで、ソートした状態にするには、 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があるようです。
以上です。
参考
railsで複合uniqueのvalidationをつけてやる
複合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
ってなんぞや?って感じですよね。
プログラミング言語的には、反復子
と呼ばれているようです。
反復するためのものの意味で反復子(はんぷくし)と訳される。繰返子(くりかえし)という一般的ではない訳語もある。
繰り返されるものに対して、呼ばれるようですね。
反復子(反復可能なオブジェクト)に対して、javascriptはfor..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メソッドによってモジュールが他のモジュールやクラスにインクルードされたあとに呼び出されます。引数にはモジュールをインクルードするクラスやモジュールが入ります。
どういうことか、コードで説明します。
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
を使いましょう。
以上です。
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
が見つかりました。
ああ、そういうことかーってなりました。
予約語を使用する場合はバッククォート
でエスケープすればいいです。
今思うと、上のrailsのsqlはきちんとバッククォート
でエスケープしています。
今まで気にしなかったですが、予約語対策をしていたんですね。