回答:
編集:コメントや他の答えを見てください。これよりも賢い答えがあります!または、コミュニティwikiとしてこれを改善してみてください。
モデルは自分自身をマスターオブジェクトに登録しないため、Railsにはモデルのリストがありません。
しかし、あなたはまだあなたのアプリケーションのモデルディレクトリの内容を見ることができます...
Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
# ...
end
編集:別の(ワイルド)アイデアは、Rubyリフレクションを使用して、ActiveRecord :: Baseを拡張するすべてのクラスを検索することです。ただし、すべてのクラスをリストする方法がわからない...
編集:楽しみのために、すべてのクラスをリストする方法を見つけました
Module.constants.select { |c| (eval c).is_a? Class }
編集:最後に、ディレクトリを見ずにすべてのモデルをリストすることに成功しました
Module.constants.select do |constant_name|
constant = eval constant_name
if not constant.nil? and constant.is_a? Class and constant.superclass == ActiveRecord::Base
constant
end
end
派生クラスも処理したい場合は、スーパークラスチェーン全体をテストする必要があります。Classクラスにメソッドを追加することでそれを行いました:
class Class
def extend?(klass)
not superclass.nil? and ( superclass == klass or superclass.extend? klass )
end
end
def models
Module.constants.select do |constant_name|
constant = eval constant_name
if not constant.nil? and constant.is_a? Class and constant.extend? ActiveRecord::Base
constant
end
end
end
RAILS_ROOT
Rails 3では使用できなくなりました。代わりに、次を使用してくださいDir.glob(Rails.root.join('app/models/*'))
ActiveRecord::Base
現在の子孫として登録されているため、すべてのモデルを積極的にロードすると、簡単に反復できます。以下の私の回答を参照してください。
Rails 3、4、および5の全体的な答えは次のとおりです。
cache_classes
オフの場合(デフォルトでは開発ではオフですが、本番ではオンです):
Rails.application.eager_load!
次に:
ActiveRecord::Base.descendants
これにより、アプリケーションのすべてのモデルがどこにあるかに関係なく確実に読み込まれ、モデルを提供する使用中の宝石も読み込まれます。
これは、Rails 5のActiveRecord::Base
ようApplicationRecord
にから継承するクラスでも機能し、子孫のそのサブツリーのみを返します。
ApplicationRecord.descendants
これがどのように行われるかについてもっと知りたい場合は、ActiveSupport :: DescendantsTrackerをチェックしてください。
:environment
向け:タスクが機能するために依存するようにしますeager_load!
。
Rails.application.eager_load!
、あなただけのモデルをロードすることができますDir.glob(Rails.root.join('app/models/*')).each do |x| require x end
Rails.paths["app/models"].existent
ディレクトリをグロブします。アプリケーション全体を熱心にロードすることは、より完全な答えであり、モデルを定義するための場所が完全になくなることを確実にします。
Rails.application.paths["app/models"].eager_load!
誰かがこれにつまずく場合に備えて、クラスのクラスの読み取りまたは拡張に依存しない別の解決策があります...
ActiveRecord::Base.send :subclasses
これはクラスの配列を返します。だからあなたはそれから
ActiveRecord::Base.send(:subclasses).map(&:name)
ActiveRecord::Base.subclasses
が、使用する必要がありますかsend
?また、たとえばc = Category.new
、表示される前にモデルを「タッチ」する必要があるようです。そうでなければ、それはしません。
ActiveRecord::Base.descendants
ActiveRecord::Base.descendants
をリストする前に、まだモデルに「触れる」必要があります。
ActiveRecord::Base.connection.tables.map do |model|
model.capitalize.singularize.camelize
end
戻ります
["Article", "MenuItem", "Post", "ZebraStripePerson"]
追加情報 model:string不明なメソッドまたは変数エラーなしでオブジェクト名のメソッドを呼び出したい場合は、これを使用してください。
model.classify.constantize.attribute_names
ActiveRecord::Base.send :subclasses
ことがあります。テーブル名を探すことをお勧めします。lorefnonが言及しているように、モデル名の自動生成は問題があるかもしれません。
.capitalize.singularize.camelize
に置き換えることができます.classify
。
私はこれを行う方法を探し、この方法を選択することになりました。
in the controller:
@data_tables = ActiveRecord::Base.connection.tables
in the view:
<% @data_tables.each do |dt| %>
<br>
<%= dt %>
<% end %>
<br>
ソース:http : //portfo.li/rails/348561-how-can-one-list-all-database-tables-from-one-project
ActiveRecord::Base.connection.tables.each{|t| begin puts "%s: %d" % [t.humanize, t.classify.constantize.count] rescue nil end}
一部のモデルがアクティブ化されていない可能性があるため、レスキューする必要があります。
model_classes = ActiveRecord::Base.connection.tables.collect{|t| t.classify.constantize rescue nil }.compact
Rails5モデルは今サブクラスのApplicationRecord
ようにあなたがあなたのアプリケーション内のすべてのモデルのリストを取得します:
ApplicationRecord.descendants.collect { |type| type.name }
またはより短い:
ApplicationRecord.descendants.collect(&:name)
開発モードの場合は、事前にモデルを熱心にロードする必要があります。
Rails.application.eager_load!
テーブルレスモデルがない場合、@ hnovickのソリューションはクールなソリューションだと思います。このソリューションは開発モードでも機能します
私のアプローチは微妙に異なりますが-
ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact
classifyは、文字列からクラスの名前を適切に与えることを想定しています。safe_constantizeは、例外をスローせずにクラスを安全にクラスに変換できることを保証します。これは、モデルではないデータベーステーブルがある場合に必要です。列挙型のnilが削除されるようにコンパクトにします。
safe_constantize
。
クラス名だけが必要な場合:
ActiveRecord::Base.descendants.map {|f| puts f}
Railsコンソールで実行するだけです。幸運を!
編集:@ sj26は正しいです、子孫を呼び出す前にこれを最初に実行する必要があります:
Rails.application.eager_load!
map
とputs
?私はポイントがあるべき得ることはありませんActiveRecord::Base.descendants.map(&:model_name)
これは私にとってはうまくいくようです:
Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file }
@models = Object.subclasses_of(ActiveRecord::Base)
Railsはモデルが使用されている場合にのみモデルをロードするため、Dir.glob行はモデルディレクトリ内のすべてのファイルを「要求」します。
モデルを配列に入れたら、考えていたことを実行できます(たとえば、ビューコードで)。
<% @models.each do |v| %>
<li><%= h v.to_s %></li>
<% end %>
...'/app/models/**/*.rb'
1行で: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
Dir['**/models/**/*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
ActiveRecord::Base.connection.tables
たった1行で:
ActiveRecord::Base.subclasses.map(&:name)
Rails.application.eager_load!
開発モードで実行する前に必要です。
はい、すべてのモデル名を見つける方法はたくさんありますが、gem_model_infoで私がしたことは、gemに含まれているすべてのモデルを提供します。
array=[], @model_array=[]
Rails.application.eager_load!
array=ActiveRecord::Base.descendants.collect{|x| x.to_s if x.table_exists?}.compact
array.each do |x|
if x.split('::').last.split('_').first != "HABTM"
@model_array.push(x)
end
@model_array.delete('ActiveRecord::SchemaMigration')
end
次に、これを印刷します
@model_array
すべてのRailsをプリロードしないようにするには、次のようにします。
Dir.glob("#{Rails.root}/app/models/**/*.rb").each {|f| require_dependency(f) }
require_dependency(f)は同じです Rails.application.eager_load!
。これにより、すでに必要なファイルエラーが回避されます。
次に、あらゆる種類のソリューションを使用して、ARモデルを一覧表示できます。 ActiveRecord::Base.descendants
Module.constants.select { |c| (eval c).is_a?(Class) && (eval c) < ActiveRecord::Base }
これは、複雑なRailsアプリ(Squareを動かすもの)で吟味されたソリューションです
def all_models
# must eager load all the classes...
Dir.glob("#{RAILS_ROOT}/app/models/**/*.rb") do |model_path|
begin
require model_path
rescue
# ignore
end
end
# simply return them
ActiveRecord::Base.send(:subclasses)
end
このスレッドの回答の最良の部分を取り、それらを最も単純で最も完全なソリューションに組み合わせます。これは、モデルがサブディレクトリにある場合を処理し、set_table_nameなどを使用します。
すべてのモデルをその属性とともに印刷する必要があるため、これに遭遇しました(@Aditya Sanghiのコメントに基づいて作成):
ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact.each{ |model| print "\n\n"+model.name; model.new.attributes.each{|a,b| print "\n#{a}"}}
Rails
実装方法descendants
は必ずしもこれまでから継承されないが、モデルActiveRecord::Base
モジュールを含み、例えば、クラス、ActiveModel::Model
モデルと同じ動作をしますが、ちょうどテーブルにリンクされますされません。
上記の同僚の言うことを補足すると、わずかな労力でこれを実行できます。
Class
Ruby のクラスのモンキーパッチ:
class Class
def extends? constant
ancestors.include?(constant) if constant != self
end
end
そして、このようにmodels
、祖先を含むメソッド:
このメソッドModule.constants
はsymbols
定数の代わりに(見かけ上)のコレクションを返すので、このメソッドArray#select
は次のサルのパッチのように置き換えることができますModule
:
class Module
def demodulize
splitted_trail = self.to_s.split("::")
constant = splitted_trail.last
const_get(constant) if defines?(constant)
end
private :demodulize
def defines? constant, verbose=false
splitted_trail = constant.split("::")
trail_name = splitted_trail.first
begin
trail = const_get(trail_name) if Object.send(:const_defined?, trail_name)
splitted_trail.slice(1, splitted_trail.length - 1).each do |constant_name|
trail = trail.send(:const_defined?, constant_name) ? trail.const_get(constant_name) : nil
end
true if trail
rescue Exception => e
$stderr.puts "Exception recovered when trying to check if the constant \"#{constant}\" is defined: #{e}" if verbose
end unless constant.empty?
end
def has_constants?
true if constants.any?
end
def nestings counted=[], &block
trail = self.to_s
collected = []
recursivityQueue = []
constants.each do |const_name|
const_name = const_name.to_s
const_for_try = "#{trail}::#{const_name}"
constant = const_for_try.constantize
begin
constant_sym = constant.to_s.to_sym
if constant && !counted.include?(constant_sym)
counted << constant_sym
if (constant.is_a?(Module) || constant.is_a?(Class))
value = block_given? ? block.call(constant) : constant
collected << value if value
recursivityQueue.push({
constant: constant,
counted: counted,
block: block
}) if constant.has_constants?
end
end
rescue Exception
end
end
recursivityQueue.each do |data|
collected.concat data[:constant].nestings(data[:counted], &data[:block])
end
collected
end
end
のサルパッチString
。
class String
def constantize
if Module.defines?(self)
Module.const_get self
else
demodulized = self.split("::").last
Module.const_get(demodulized) if Module.defines?(demodulized)
end
end
end
そして最後に、modelsメソッド
def models
# preload only models
application.config.eager_load_paths = model_eager_load_paths
application.eager_load!
models = Module.nestings do |const|
const if const.is_a?(Class) && const != ActiveRecord::SchemaMigration && (const.extends?(ActiveRecord::Base) || const.include?(ActiveModel::Model))
end
end
private
def application
::Rails.application
end
def model_eager_load_paths
eager_load_paths = application.config.eager_load_paths.collect do |eager_load_path|
model_paths = application.config.paths["app/models"].collect do |model_path|
eager_load_path if Regexp.new("(#{model_path})$").match(eager_load_path)
end
end.flatten.compact
end
def load_models_in_development
if Rails.env == "development"
load_models_for(Rails.root)
Rails.application.railties.engines.each do |r|
load_models_for(r.root)
end
end
end
def load_models_for(root)
Dir.glob("#{root}/app/models/**/*.rb") do |model_path|
begin
require model_path
rescue
# ignore
end
end
end
私はRails 4でこれらの回答の多くを失敗に終わったので(すごいために1つまたは2つ変更したので)、自分で追加することにしました。ActiveRecord :: Base.connectionを呼び出してテーブル名をプルしたものは機能しましたが、(app / models /内のフォルダーにある)いくつかのモデルを非表示にしているため、希望する結果が得られませんでした削除:
def list_models
Dir.glob("#{Rails.root}/app/models/*.rb").map{|x| x.split("/").last.split(".").first.camelize}
end
それを初期化子に入れて、どこからでも呼び出すことができます。不必要なマウスの使用を防ぎます。
すべてのモデルがapp / modelsにあり、サーバーにgrep&awkがあると仮定します(ほとんどの場合)。
# extract lines that match specific string, and print 2nd word of each line
results = `grep -r "< ActiveRecord::Base" app/models/ | awk '{print $2}'`
model_names = results.split("\n")
それはでRails.application.eager_load!
各ファイルをループするよりも速い かループしますDir
。
編集:
このメソッドの欠点は、ActiveRecordから間接的に継承するモデル(たとえばFictionalBook < Book
)が欠落していることです。Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name)
少し遅いですが、最も確実な方法はです。
誰かがそれが便利だと思ったら、私はこの例をここに投げます。ソリューションはこの回答https://stackoverflow.com/a/10712838/473040に基づいています。
public_uid
外部の世界へのプライマリIDとして使用される列があるとしましょう(ここでそれを実行する理由を見つけることができます)
ここで、既存のモデルの束にこのフィールドを導入し、まだ設定されていないすべてのレコードを再生成するとします。このようにできます
# lib/tasks/data_integirity.rake
namespace :di do
namespace :public_uids do
desc "Data Integrity: genereate public_uid for any model record that doesn't have value of public_uid"
task generate: :environment do
Rails.application.eager_load!
ActiveRecord::Base
.descendants
.select {|f| f.attribute_names.include?("public_uid") }
.each do |m|
m.where(public_uid: nil).each { |mi| puts "Generating public_uid for #{m}#id #{mi.id}"; mi.generate_public_uid; mi.save }
end
end
end
end
あなたは今走ることができます rake di:public_uids:generate