うまとま君の技術めも

2015年新卒入社した社畜の勉強内容などなど

sequelizeでidに0が入ってしまう

sequelizeでidに0が入ってしまう

環境

概要

SequelizeでdialectとしてMySQLをした際に、 AUTO_INCREMENTカラムを含むテーブルに対してINSERTすると0が入ってしまう可能性がある。

Sequelize

SequelizeはNode.js用のORMである。

まず、下記のようなテーブルがあるとする

CREATE TABLE `Users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

ここで、sequelizeで定義したModelからnameカラムのみを指定してinsertすると 下記のようなSQLが発行される

INSERT INTO `Users` (`id`,`name`) VALUES (DEFAULT,'sequelize');

この時、設定によってはUsersに追加されるレコードは下記のようなものになる場合がある

select * from Users;
+----+-----------+
| id | name      |
+----+-----------+
|  0 | sequelize |
+----+-----------+

NO_AUTO_VALUE_ON_ZERO

MySQLではsql_modeという設定があります。

https://dev.mysql.com/doc/refman/5.6/ja/faqs-sql-modes.html

  1. サーバー SQL モードとは何ですか。
  2. サーバー SQL モードは、MySQL でサポートされる SQL 構文、および実行されるデータ妥当性チェックの種類を定義します。

sql_modeの1つにNO_AUTO_VALUE_ON_ZEROがあり、 これを設定すると、INSERT時にAUTO_INCREMENTカラムに対して0を指定しても自動採番されなくなります。

NO_AUTO_VALUE_ON_ZERO は AUTO_INCREMENT カラムの処理に影響します。通常は、NULL または 0 をカラムに挿入することによって、カラムの次のシーケンス番号を生成します。NO_AUTO_VALUE_ON_ZERO は 0 のこの動作を抑制するため、NULL のみが次のシーケンス番号を生成します。

https://dev.mysql.com/doc/refman/5.6/ja/sql-mode.html

また、MySQLでは数値型の初期値は0なので、DEFAULTは0にります。 https://dev.mysql.com/doc/refman/5.6/ja/data-type-defaults.html

なので、NO_AUTO_VALUE_ON_ZEROを設定している状態でidに対してDEFAULTを指定すると 必ずidに0が入るようになってしまいます。

対応

下記SQLsql_modeを確認、設定することができます。 状況に応じて、適切なsql_modeを設定すれば良さそう?

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';

まとめ

Sequelizeを使うときは、sql_modeの設定を事前に確認しましょう。 MySQLに対しての知識が全然ないので、地味にハマりました。。。

RMagickでフォントが使用できない、unable to read font `(null)'

環境

unable to read font `(null)'

rmagickでテキストを表示しようと思ったら何故かunable to read font(null)'`とかいうエラーが。。。

rmagickで使用できるフォントを見てみると、使用可能なフォントが1つもない。。。

require 'RMagick'

Magick.fonts
=> []

設定ファイルを確認

設定ファイルを確認してみると、--with-gs-font-dir=/usr/local/share/ghostscript/fontsという部分がある。 どうやら、ここにあるフォントを読み取って使用しているらしいが、そんなディレクトリは存在しない。。。

$ cat /usr/local/lib/ImageMagick/config-Q16/configure.xml

ghostscript

無いのであればinstallしてみる

$ brew install ghostscript

無事フォントが読み込まれました

require 'RMagick'

Magick.fonts
=> [#<struct Magick::Font name="AvantGarde-Book", ...]

.btn-groupにtooltipを使うとデザインがズレる

環境

.btn-groupにtooltipを使うとデザインがズレる

.btn-group内のbuttontooltipを使用すると、 tooltipが発動した時に1px程度ズレが生じてしまいます。

f:id:umatomakun:20150321171659g:plain

<div class="btn-group" role="group">
    <button
        type="button"
        class="btn btn-default"
        data-toggle="tooltip"
        title="Tooltip">
        Left
    </button>
    <button type="button" class="btn btn-default">Middle</button>
    <button type="button" class="btn btn-default">Right</button>
</div>

何故ズレが生じているのか?

Bootstrapのソースコードを見てみると、隣接している.btnmargin-left: -1px;が指定されているようです。

github.com

.btn-group {
  .btn + .btn,
  .btn + .btn-group,
  .btn-group + .btn,
  .btn-group + .btn-group {
    margin-left: -1px;
  }
}

しかし、tooltipを使用するとtooltipの対象となっている.btnの後にDOMが追加されてしまい、.btn同士が隣接している状態ではなくなってしまいます。 そうすると、上記cssが適応されないのでレイアウトがズレてしまっているようです。

f:id:umatomakun:20150321203149p:plain

ズレが生じないようにする

対処法としてはdata-containerbody等に設定してあげればOKです。

<div class="btn-group" role="group">
    <button
        type="button"
        class="btn btn-default"
        data-toggle="tooltip"
        data-container="body"
        title="Tooltip">
        Left
    </button>
    <button type="button" class="btn btn-default">Middle</button>
    <button type="button" class="btn btn-default">Right</button>
</div>

参考

Bootstrapの公式サイトにもdata-containerを指定するよう載っていますね。

getbootstrap.com

When using tooltips on elements within a .btn-group or an .input-group, you'll have to specify the option container: 'body' (documented below) to avoid unwanted side effects (such as the element growing wider and/or losing its rounded corners when the tooltip is triggered).

Drawableに設置したpngファイルのオリジナルサイズを取得する

AndroidにてDrawableに設置したpngファイルのサイズを取得しようとしたら、ちょっとはまったのでメモ。

前提

drawableに 50 * 50 px の画像ファイルを設置。
res/drawable/sample.png

画像サイズを取得

リソースIDからBitmapを生成して#getWidth, #getHeightでサイズを取得したら、 実際のサイズとは異なる値が取得される。。。

Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.sample);
int w = b.getWidth();
int h = b.getHeight();
// w => 150; h => 150;

そんな時はBitmapFactory.Optionsから値を取得してあげれば、 オリジナルの画像サイズが取得出来ました( ´ー`)フゥー...

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.sample, options);
int w = options.outWidth;
int h = options.outHeight;
// w => 50; h => 50;

参考

http://stackoverflow.com/questions/8855036/incorrect-image-dimensions-in-android-when-using-bitmap

Enumerable#map and Array#flatten vs Enumerable#flat_map

目的

  • Enumerable#map, Array#flatten と Enumerable#flat_map でどちらがパフォーマンスに優れているかを調べる。

環境

Enumerable#flat_map

flat_map (Enumerable) - APIdock : http://apidock.com/ruby/Enumerable/flat_map

ベンチマーク

require 'benchmark'

n = 1000
array = Array.new(100) { |i| Array.new(i) { |j| j } }

Benchmark::CAPTION
Benchmark.bm do |x|
  x.report('flat_map') { n.times { array.flat_map { |a| a } } }
  x.report('flat map') { n.times { array.map { |a| a }.flatten(1) } }
end
       user     system      total        real
flat_map  0.030000   0.010000   0.040000 (  0.036725)
flat map  0.260000   0.020000   0.280000 (  0.271837)

まとめ

map , flattenではなくflat_mapを使ったほうが速くなっていますね。

ただ、flat_mapでは1段階までしか平坦化されないで使用する際には注意が必要かもしれないです。

irb(main):055:0> [[1, 2], [[3, 4, 5]]].flat_map { |x| x }
=> [1, 2, [3, 4, 5]]

Railsで独自クラスにバリデーションを実装してみる

環境

独自クラスにValidationを実装

独自クラスでRailsのValidationを実装したい場合はActiveModel::Validationsをincludeしてあげればよいらしい。

class Hoge
  include ActiveModel::Validations

  validates :hoge_id, presence: true
  
  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end
end

これで、大丈夫かと思ったがNoMethodErrorが出てしまった。

[1] pry(main)> Hoge.new(hoge_id: 100)
NoMethodError: undefined method `hoge_id=' for #<Hoge:0x007fc71ce47bf0>
from /path/to/hoge.rb:8:in `block in initialize'

なので、attr_accessor :hoge_idを指定してあげればエラーは消えた.

class Hoge
  include ActiveModel::Validations

  attr_accessor :hoge_id # 追加

  validates :hoge_id, presence: true
  
  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end
end

これで、ひとまず独自クラスでもバリデーションを行うことが出来るようになりました。

[5] pry(main)> hoge = Hoge.new(hoge_id: nil)
[6] pry(main)> hoge.valid?
=> false
[7] pry(main)> hoge.errors.full_messages
=> ["Hogeを入力してください。"]

参考

CoffeeScriptのファットアローって何だ?

ファットアロー

jQueryのコールバック関数で呼ばれた関数でthisを使うと関数内のjQueryオブジェクトを指しているため、
that = @等と関数外で別の変数に置き換えて使用することがあると思います。

coffee

class User
  constructor: (first_name, last_name) ->
    @full_name = "#{first_name} #{last_name}"
    
    that = @
    $("#action-hello").click ->
      alert "My name is #{that.full_name}."

js

var User;

User = (function() {
  function User(first_name, last_name) {
    var that;
    this.full_name = "" + first_name + " " + last_name;
    that = this;
    $("#action-hello").click(function() {
      return alert("My name is " + that.full_name + ".");
    });
  }

  return User;

})();

そんな時、アロー->ではなくファットアロー=>を使ってあげると
@に対してスコープ外のthisへと自動的に置き換えを行ってくれます。

coffee

class User
    constructor: (first_name, last_name) ->
    @full_name = "#{first_name} #{last_name}"
    
    $("#action-hello").click =>
        alert "My name is #{@full_name}."

js

var User;

User = (function() {
  function User(first_name, last_name) {
    this.full_name = "" + first_name + " " + last_name;
    $("#action-hello").click((function(_this) {
      return function() {
        return alert("My name is " + _this.full_name + ".");
      };
    })(this));
  }

  return User;

})();

感想

ファットアローなんてものがあったんですね、知らなかった自分が情けないです。。。