うまとま君の技術めも

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

.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;

})();

感想

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

Railsでf.selectにclassが設定できない時

環境

f.select

<%= form_for @user do |f| %>
  ...
  <%= f.select :job, { engineer: "Engineer", ... }, class: "myclass" %>
  ...
<% end %>

ここでセレクトタグに.myclassが設定されていることが期待されるが、 実際に生成されるHTMLは下記のようになる。

<select id="user_job" name="user[job]">
  <option selected="selected" value="engineer">Engineer</option>
  ...
</select>

classが設定できない時の対処法

<%= form_for @user do |f| %>
  ...
  <%= f.select :job, { engineer: "Engineer", ... }, {}, { class: "myclass" } %>
  ...
<% end %>

これでclassが設定されたHTMLが出力されるはずです。

<select class="myclass" id="user_job" name="user[job]">
  <option selected="selected" value="engineer">Engineer</option>
  ...
</select>

参考

Railsでdefault_scopeの解除方法

環境

default_scopeの設定方法

orderwhere文などをdefault_scopeメソッドに渡してあげればおk

class User < ActiveRecord::Base
  ...
  default_scope { order(email: :asc) }
  default_scope { where(deleted_at: nil) }
  ...
end

orderの解除方法

reorderorderのみ解除できる。

User.all
    #=> "SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL  ORDER BY `users`.`email` ASC"
User.reorder(nil)
    #=> "SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL
User.reorder(id: :desc)
    #=> "SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL  ORDER BY `users`.`id` DESC"

特定のscopeの解除方法

exceptまたはunscopeで解除することが出来る。
unscopeの方はexceptよりも細かい設定をすることが出来るようです。

User.all
    #=> "SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL  ORDER BY `users`.`email` ASC"
User.except(:order)
User.unscope(:order)
    #=> "SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL
User.except(:where)
User.unscope(:where)
User.unscope(where: :deleted_at)
    #=> "SELECT `users`.* FROM `users`  ORDER BY `users`.`id` DESC"

感想

今回は環境がRails4だったのでRails3などでは結果が違うかもしれないですね。

参考