文字列からクラスにアクセスする

以前、複数のモデルを横断したリストを作成するという記事を書いたのですが、文字列からクラスにアクセスすることでもう少しうまくできそうな方法を見つけたのでメモしておく。

目次

参考

文字からクラスにアクセスする方法

ズバリ、.safe_constantize.constantizeを使う。
名前から、ObjectSpace というすべてのオブジェクトの登録した空間から、同じ名称のオブジェクトを得ることができます。

例えば以前は 2 つのモデルを混ぜたリストを作るとき以下コントローラを作りました。

app/controllers/mix_lists_controller.rb
1
2
3
4
5
6
7
8
9
10
11
class MixListsController < ApplicationController
def index
foods = Food.all
users = User.all

# 二つのリストを合体
lists = foods + users
# 作成時刻で並べ替え
@lists_sorted = lists.sort{|a,b| a.created_at <=> b.created_at}
end
end

これを以下のように書き換えることができます。

app/controllers/mix_lists_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MixListsController < ApplicationController
def index
classs = ["User","Food"]

lists = Array.new

classs.each do |c|
obj = c.constantize
lists += obj.all.to_a
end

# 作成時刻で並べ替え
@lists_sorted = lists.sort{|a,b| a.created_at <=> b.created_at}
end
end

このような使い方であれば、classs(変数名が適当だなとは感じますが)にアクセスしたいクラス名を書き加えるだけで、モデルを横断したリストを作ることができます。

では、classsに存在しないクラス名を書いてしまった場合は?
上記のままではエラーになるので、もう一段階書き換えます。

app/controllers/mix_lists_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MixListsController < ApplicationController
def index
classs = ["User","Food","NoClass"]

lists = Array.new

classs.each do |c|
#safe_constantizeは該当するものがなければnilを返す
obj = c.safe_constantize
if obj.present? #<=ここでエラーチェック
lists += obj.all.to_a
end
end

# 作成時刻で並べ替え
@lists_sorted = lists.sort{|a,b| a.created_at <=> b.created_at}
end
end

上記の場合、safe_constantizeを使用しています。
classsに存在しないクラス名Hogeを指定しています。
obj.present?で、エラーチェックを行うことで、存在しないものが与えられた場合に対応できます。

ruby でもできるのか

constantizesafe_constantizeは、rails のActiveSupport::Inflectorが提供する機能です。
これを ruby でも使えないか試みます。
以下の方法でできました。

まず、activesupportのインストールします。

1
2
3
4
5
6
bundle init

#GemFile に以下を追記
#gem 'activesupport'

bundle install

サンプルとして、sample.rbを作ります。

sample.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'active_support/all'

class User
def name
"田中"
end
end

class Item
def name
"オノ"
end
end

classs = ["User","Item"]

classs.each do |c|
obj = c.to_s.safe_constantize.new
puts obj.name
end

sample.rbruby sample.rbで実行します。
実行結果は以下の通りです。

1
2
田中
オノ

2 つのクラスをまたいで、中身にアクセスできました。


rails 及び active_support が ruby 自体を拡張している故に、便利なメソッドを ruby そのままでは使えないということがあるというのは、気を付けたいポイントでした。

ではでは。