かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Ruby Stylusでnibを使いたい。

前回 Ruby(sinatra)でStylusを使えるようにしました。
しかし、このままではSassで言うところのCompassにあたるnibを使うことができていませんでした。nibを使えるようにした際のメモです。

nibはnode(npm)でインストールしておく必要がある

$ npm install nib

Rubyでnibを使う

RubyファイルにStylus・nibを使用する記述をする

# main.rb
require 'stylus'
require 'stylus/tilt'

# nibを使用する
Stylus.use :nib

# オプション
# min化する
Stylus.compress = true
# デバックコメントを出力する
Stylus.debug = true

RubyファイルにStylus.use :nibを記述した上で、Stylus内でnibをimportしなければnibを使用することができませんでした。
Stylusファイルの先頭に@import 'nib'を記述します。

// main.styl
@import 'nib'

.clearfix
  clearfix()

npmでインストールする必要があり、かつstylusのファイルでもimportの記述をしなければならないので、nodeのプロジェクトで使うよりは少し面倒ですが、これでnibも使えるようになったので、nodeのプロジェクトと同様にstylusでCSSを作成することができるようになりました。


[参考]

Ruby (2.2.0) 自分で作成したファイルを読み込む(require)にハマる

Ruby(2.2.0)を使っていて自作したファイルを読み込み(require)する際にハマったのでメモ。
結論から言えば自作ファイルを読み込ませる際は相対パスで読み込むrequire_relativeを使うのが良さそう。

ファイル構成と読み込み

ファイル構成

/app
  |- main.rb # <- 実行ファイル
  |- controller.rb
  |- /modules
  |   |- a.rb
  |   |- b.rb
  |   |- /dir
  |        |- e.rb
  |- /lib
       |- c.rb
       |- d.rb

読み込み順

main.rb # <- 実行ファイル
  |- controller.rb ... (1)
       |- /modules/a.rb ... (2)
            |- /modules/b.rb ... (3)
            |    |- /modules/dir/e.rb ... (4)
            |    |- /lib/d.rb ... (5)
            |- /lib/c.rb ... (5)

1. 実行ファイルから同一階層のファイルの読み込み (main.rb -> controller.rb)

main.rbからcontroller.rbを読み込みます。

require は ./ が無いとエラーになる

require 'controller.rb'
# => Loaderror

Ruby1.9.2から./が無いとエラーになる仕様になったらしい。

require './controller.rb'
# => SUCCESS

※ただし、実行ファイルが別階層のものになると./有りでもエラーになる

require_relative で指定する場合 ./ 無しでもOKっぽい

require_relative 'controller.rb'
# => SUCCESS
require_relative './controller.rb'
# => SUCCESS

requireでもrequire_relativeでも/からファイルを指定するとエラーになる場合がある

/からファイルやディレクトリを指定すると、完全なルートからファイルを探すっぽいのでルートからのパスと一致してない場合はエラーになるっぽい。

require '/controller.rb'
# => Loaderror cannot load such file -- /controller.rb

/app/controller.rbなので、/controller.rbは見つからないっぽい?
それとも仕様的にNGなのかちょっと不明。

require_relativeでも/からファイル指定するとルートからファイル検索するっぽい。

require_relative '/controller.rb'
# => Loaderror cannot load such file -- /controller.rb

 


2. 同一階層のフォルダ内のファイルの読み込み (controller.rb -> /modules/a.rb)

main.rbで読み込むcontroller.rbで、同一階層にある/modulesフォルダ内のa.rbを読み込ませます

同一階層のファイルの読み込みと同じ

require 'modules/a.rb'
# => Loaderror
require './modules/a.rb'
# => SUCCESS

※ただし、実行ファイルが別階層のものになると./有りでもエラーになる

require_relative 'modules/a.rb'
# => SUCCESS
require_relative './modules/a.rb'
# => SUCCESS

 


3. 実行ファイルと同じ階層にあるフォルダ内でもファイル読み込み (/modules/a.rb -> /modules/b.rb)

先のcontroller.rbで読み込む/modules/a.rbから同じ/modulesディレクトリ内のファイルを読み込ませる

main.rb # 実行ファイル
/modules
  |- a.rb # -> b.rb を読み込ませたい
  |- b.rb

require は 実行ファイルからの絶対パスでないとエラーになる

require 'b.rb'
# => LoadError

./を付けてもa.rbからの相対パスなので読み込めない

require './b.rb'
# => LoadError

main.rbからのパスを指定すればOK

require './modules/b.rb'
# => SUCCESS

※ただし、実行ファイルが別階層のものになると、当然パスが変わるのでエラーになる

相対パス読み込み require_relative

require_relative 'b.rb'
# => SUCCESS
require_relative './b.rb'
# => SUCCESS

 


4. フォルダ内のファイルから同一フォルダに有るディレクトリ内のファイルの読み込み (/modules/b.rb -> /modules/dir/e.rb)

/modules/b.rbから/modules/dir/e.rbを読み込む

main.rb # 実行ファイル
/modules
  |- b.rb # -> ./dir/e.rb を読み込ませたい
  |- /dir
       |- e.rb

requireなら実行ファイルからの絶対パスで指定する

require './modules/dir/e.rb'
# => SUCCESS

※ただし、実行ファイルが別階層のものになると、当然パスが変わるのでエラーになる

require_relativeの場合は相対パスで指定する

require_relative 'dir/e.rb'
# => SUCCESS
require_relative './dir/e.rb'
# => SUCCESS

 


5. 別階層のディレクトリにあるファイルの読み込み (/modules/a.rb -> /lib/c.rb, /modules/b.rb -> /lib/d.rb)

/modules/a.rbから/lib/c.rbを読み込む

main.rb # 実行ファイル
/modules
  |- a.rb # -> /lib/b.rb を読み込ませたい
/lib
  |- c.rb

requireなら実行ファイルからの絶対パスrequire_relativeなら読み込ませるファイル(a.rb)からの相対パスで指定すればOK。
/modules/b.rbから/lib/d.rbを読み込ませるのも同様。

require './lib/c.rb'
# => SUCCESS

※ただし、実行ファイルが別階層のものになると、当然パスが変わるのでエラーになる

require_relative '../lib/c.rb'
# => SUCCESS

まとめ

  • require ... 実行ファイルからの絶対パスで指定。必ず./から始める必要があるっぽい。
  • require_relative ... そのファイルからの相対パスで指定。

自作したファイルを読み込ませる場合は、
require_relativeで相対パスでファイル指定をする 方法が間違い無さそうだなと思いました。


[参考]

Effective Ruby

Effective Ruby

Ruby Bool値かどうか型チェックしたい。ついでにis_bool?メソッドを作ってみた。

Rubyで変数がBoolean型かどうかチェックしたい。
かなりレアケースな気がするけれど、この前参加した勉強会で要望があったのでトライしてみた。

RubyのTrueとFalse

  • false ... false, nil の時
  • true ... false, nil 以外全てtrue

Rubyには is_bool? という関数はない

true.is_bool?
# => undefined method `is_bool?' for true:TrueClass (NoMethodError)

エラーになります。

is_a?(クラス名)というメソッドを使うとオブジェクトが引数のクラスのインスタンスかどうか判定できるので、これを利用する。
trueTrueClassfalseFalseClassインスタンス
BooleanBoolClassってクラスは無いっぽい。

val = true
p val.is_a?(FalseClass) || val.is_a?(TrueClass) # => true

val = false
p val.is_a?(FalseClass) || val.is_a?(TrueClass) # => true

val = nil
p val.is_a?(FalseClass) || val.is_a?(TrueClass) # => false

チェックできるけど記述が長いので何回もチェックが必要だとめんどくさそう...

is_a? を使わない方法を考えてみる

値を!!でtrue/falseに変換して比較したらいけそうな気がしました。

def is_bool(v)
  !!v === v
end

p is_bool true  # => true
p is_bool false # => true
p is_bool nil   # => false
p is_bool 1     # => false
p is_bool 0     # => false
p is_bool ""    # => false
p is_bool true.to_s  # => false
p is_bool false.to_s # => false

なんとなく上手くいっている気もしますが、Ruby===演算子は型チェックをしているのではなく、左辺のオブジェクトによって判定の挙動が異なるらしいので本当にこれでOKなのか少し自信がありません。
ついでに関数化してみたのですが、is_boolって関数名で引数取る感じが何となくイケてない気がします...

他の言語を使っていると、ついつい「===演算子は==演算子よりも更に厳密な同値判定演算子でしょ?」なんて思ってしまいますが、ところがどっこい、Rubyの場合はむしろ===演算子のほうが柔軟な場合もあり得るのです。
Object#===の説明を見てみるとわかりますが、基本的にこの===演算子は、サブクラスで再定義されていない限り、単にObject#==を呼び出すだけなんですね。

出展: Rubyのcase式と===演算子について - しばそんノート

TrueClass, FalseClassでは#===が定義されていないようだったので、Object#===と同じなのではないかと思います。
ってコトは !!v==vでも同じことっぽい!?

Object
obj === other → true or false
Case Equality – For class Object, effectively the same as calling #==, but typically overridden by descendants to provide meaningful semantics in case statements.

出展: https://ruby-doc.org/core-2.2.2/Object.html#method-i-3D-3D-3D

Rubyは定義済みのクラスにメソッドとかを追加できるっぽい

手持ちの初めてのRubyという本を見ていると、オブジェクトとクラスの章にRubyは「定義済みのクラスに対して、いつでも定義を追加することができます。」とありました。
Objectクラスにis_bool?というメソッドを追加することも可能っぽかったので試してみました。

class Object
  def is_bool?
    !!self === self
  end
end

p true.is_bool?  # => true
p false.is_bool? # => true
p nil.is_bool?   # => false
p 1.is_bool?     # => false
p 0.is_bool?     # => false
p "".is_bool?    # => false
p true.to_s.is_bool?  # => false
p false.to_s.is_bool? # => false

一応 Object.is_bool? でBool値かどうか判定できるっぽものが出来ました。
メソッド内の判定をself.is_a?(FalseClass) || self.is_a?(TrueClass)にした方が判定としては確実かもしれません。

ただ、個人的に大元になるObjectクラスにメソッドを追加するのは正直どーなの?という感覚があるので良し悪しが判断できません。
こうするとCoolだよとかRubyっぽいよ。ってのがあれば教えてください。


[参考]

初めてのRuby

初めてのRuby