1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rubyでインクルードガード的なものを作る

Last updated at Posted at 2017-11-21

TL;DR (長い3行で)

・同じスコープに同じ定義値が復数あると警告がでる
・直接requireとシンボリックリンクのrequireが同居しているのが原因だと修正できない
・C/C++でおなじみのインクルードガード相当のことをRubyでもやる

困りごと

同じRubyファイルを直接requireする子と,シンボリックリンク経由でrequireする子が同居すると,↓ のような警告がでます.

warning: already initialized constant

本質的には同じものをrequireしているので,警告がでても困ることはさして無いのですが,Console上に出てくるのでちょっと目障り.

C/C++であるようなインクルードガードがRubyでも欲しい.

まずは問題の再現

↓ 用意するのは3ファイル.

main.rb          → requireをcallする側
required.rb      → requireされる側
required_link.rb → required.rbへのシンボリックリンク

requireされる側のファイル

required.rb
TOP_LEVEL_DEF = "top level def"
puts "## require DONE"

requireする側のファイル

main.rb
# 直接require
require_relative './required.rb'
# シンボリックリンク経由でrequire
require_relative './required_linked.rb'

puts "## TOP_LEVEL_DEF = #{top level def}"

ここでmain.rbを実行すると,↓ のような結果になります.

## require DONE
required_linked.rb:1: warning: already initialized constant TOP_LEVEL_DEF
required.rb:1: warning: previous definition of TOP_LEVEL_DEF was here
## require DONE
## TOP_LEVEL_DEF = top level def

required.rbが2回読み込まれ,TOP_LEVEL_DEFの2回目の初期化のときに警告が出ていることがわかります.

Ruby版インクルードガード

C/C++のように#ifndefが使えれば良いのですが,定数が宣言されているかどうか,を調べるような関数は見つけられませんでした.

ただ,カレントスコープでアクセス可能な定義値の一覧を取得する関数はあるようです.

Module.constants
https://docs.ruby-lang.org/ja/latest/method/Module/s/constants.html

これを使えば,定数の未宣言/宣言済の判定ができそうです.

先ほどのrequired.rbを ↓ のように改造します.

required.rb
if !Module.constants.include?(:IS_REQUIRED)
  IS_REQUIRED = true # 値は特に意味なし

  TOP_LEVEL_DEF = "top level def"
  puts "## require DONE"
end

この状態でmain.rbを実行すると,

## require DONE
## TOP_LEVEL_DEF = top level def

という出力になり,警告が出ないようになりました.
required.rbが1回しか読み込まれていないことが確認できますし,そのあとの定義値の使用も問題ありません.

まとめると

if !Module.constants.include?(:IS_REQUIRED)
  IS_REQUIRED = true

  # Do something.

end

という構文でRubyでもC/C++のようなインクルードガードが実現できそうです.
あまり使う機会は無さそうですが,直接とシンボリックリンク経由で同一ファイルをrequireすることがあったので調べてみました.

この半端ではない車輪の再発明感

追記 (2017/11/22)

コメントでdefined?関数を教えていただきました.
こちらを使うほうが短くて直感的な書き方にできるのでよさそうです.

if !defined? IS_REQUIRED
  IS_REQUIRED = true

  # Do something.

end

---///

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?