Est-il possible d’obtenir une collection de tous les modèles dans votre application Rails?

Existe-t-il un moyen d’obtenir une collection de tous les modèles de votre application Rails?

Fondamentalement, puis-je faire les goûts de: –

Models.each do |model| puts model.class.name end 

EDIT: Regardez les commentaires et autres réponses. Il y a des réponses plus intelligentes que celle-ci! Ou essayez d’améliorer celui-ci en tant que wiki de communauté.

Les modèles ne s’enregistrent pas eux-mêmes sur un object maître, alors non, Rails n’a pas la liste des modèles.

Mais vous pouvez toujours regarder dans le contenu du répertoire des modèles de votre application …

 Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path| # ... end 

EDIT: Une autre idée (sauvage) serait d’utiliser la reflection Ruby pour rechercher toutes les classes qui prolongent ActiveRecord :: Base. Je ne sais pas comment vous pouvez lister toutes les classes si …

EDIT: Juste pour le fun, j’ai trouvé un moyen de lister toutes les classes

 Module.constants.select { |c| (eval c).is_a? Class } 

EDIT: Finalement réussi à lister tous les modèles sans regarder les répertoires

 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 

Si vous souhaitez également gérer la classe dérivée, vous devrez tester toute la chaîne de la superclasse. Je l’ai fait en ajoutant une méthode à la classe 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 

La réponse complète pour les rails 3, 4 et 5 est la suivante:

Si cache_classes est désactivé (par défaut, il est désactivé dans le développement, mais en production):

 Rails.application.eager_load! 

Alors:

 ActiveRecord::Base.descendants 

Cela garantit que tous les modèles de votre application, quel que soit leur emplacement, sont chargés et que toutes les gemmes que vous utilisez qui fournissent des modèles sont également chargées.

Cela devrait également fonctionner sur les classes qui héritent d’ ActiveRecord::Base , comme ApplicationRecord dans Rails 5, et ne retourner que le sous-arbre des descendants:

 ApplicationRecord.descendants 

Si vous souhaitez en savoir plus sur cette opération, consultez ActiveSupport :: DescendantsTracker .

Juste au cas où quelqu’un trébucherait sur celui-ci, j’ai une autre solution, ne pas compter sur dir pour lire ou étendre la classe Class …

 ActiveRecord::Base.send :subclasses 

Cela retournera un tableau de classes. Donc, vous pouvez alors faire

 ActiveRecord::Base.send(:subclasses).map(&:name) 
 ActiveRecord::Base.connection.tables.map do |model| model.capitalize.singularize.camelize end 

reviendra

 ["Article", "MenuItem", "Post", "ZebraSsortingpePerson"] 

Informations supplémentaires Si vous souhaitez appeler une méthode sur le nom de l’object sans modèle: ssortingng unknown method ou des erreurs de variable utilisent cette méthode

 model.classify.constantize.atsortingbute_names 

J’ai cherché des moyens de le faire et j’ai fini par choisir cette façon:

 in the controller: @data_tables = ActiveRecord::Base.connection.tables in the view: < % @data_tables.each do |dt| %> 
< %= dt %> < % end %>

source: http://portfo.li/rails/348561-how-can-one-list-all-database-tables-from-one-project

Je pense que la solution de @ hnovick est cool si vous n’avez pas de modèles sans table. Cette solution fonctionnerait également en mode développement

Mon approche est subtilement différente –

 ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact 

classify est bien censé vous donner le nom de la classe à partir d’une chaîne correctement . safe_constantize garantit que vous pouvez le transformer en une classe en toute sécurité sans lancer une exception. Cela est nécessaire si vous avez des tables de firebase database qui ne sont pas des modèles. compacte de sorte que tous les nils dans l’énumération soient supprimés.

Si vous voulez juste les noms de classe:

 ActiveRecord::Base.descendants.map {|f| puts f} 

Il suffit de l’exécuter dans la console Rails, rien de plus. Bonne chance!

EDIT: @ sj26 a raison, vous devez l’exécuter avant de pouvoir appeler les descendants:

 Rails.application.eager_load! 

Pour Rails5, les modèles sont maintenant des sous – classes de ApplicationRecord donc vous obtenez la liste de tous les modèles de votre application:

 ApplicationRecord.descendants.collect { |type| type.name } 

Ou plus court:

 ApplicationRecord.descendants.collect(&:name) 

Si vous êtes en mode dev, vous devrez vous soucier de charger les modèles avant:

 Rails.application.eager_load! 

Cela semble fonctionner pour moi:

  Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file } @models = Object.subclasses_of(ActiveRecord::Base) 

Rails charge uniquement les modèles lorsqu’ils sont utilisés, de sorte que la ligne Dir.glob “nécessite” tous les fichiers du répertoire des modèles.

Une fois que vous avez les modèles dans un tableau, vous pouvez faire ce que vous pensiez (par exemple, dans le code de vue):

 < % @models.each do |v| %> 
  • < %= h v.to_s %>
  • < % end %>

    Sur une ligne: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize } Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }

    ActiveRecord::Base.connection.tables

    En une seule ligne:

      ActiveRecord::Base.subclasses.map(&:name) 

    Je ne peux pas encore commenter, mais je pense que la réponse de sj26 devrait être la meilleure réponse. Juste un indice:

     Rails.application.eager_load! unless Rails.configuration.cache_classes ActiveRecord::Base.descendants 

    Cela fonctionne pour Rails 3.2.18

     Rails.application.eager_load! def all_models models = Dir["#{Rails.root}/app/models/**/*.rb"].map do |m| m.chomp('.rb').camelize.split("::").last end end 

    Pour éviter de pré-charger tous les rails, vous pouvez le faire:

     Dir.glob("#{Rails.root}/app/models/**/*.rb").each {|f| require_dependency(f) } 

    require_dependency (f) est le même que Rails.application.eager_load! les usages. Cela devrait éviter les erreurs de fichier déjà requirejses.

    Ensuite, vous pouvez utiliser toutes sortes de solutions pour répertorier les modèles AR, comme ActiveRecord::Base.descendants

     Module.constants.select { |c| (eval c).is_a?(Class) && (eval c) < ActiveRecord::Base } 

    Oui, il existe de nombreuses façons de trouver tous les noms de modèles, mais ce que j’ai fait dans ma gemme model_info est que cela vous donnera tous les modèles même inclus dans les gemmes.

     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 

    puis imprimez simplement ceci

     @model_array 

    Voici une solution qui a été validée avec une application Rails complexe (le One Powering 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 

    Il prend les meilleures parties des réponses dans ce fil et les combine dans la solution la plus simple et la plus complète. Cela gère les cas où vos modèles sont dans des sous-répertoires, utilisez set_table_name etc.

    Je viens juste de trouver celui-ci, car je dois imprimer tous les modèles avec leurs atsortingbuts (construits sur le commentaire de @Aditya Sanghi):

     ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact.each{ |model| print "\n\n"+model.name; model.new.atsortingbutes.each{|a,b| print "\n#{a}"}} 

    Cela a fonctionné pour moi. Merci à tous les messages ci-dessus. Cela devrait retourner une collection de tous vos modèles.

     models = [] Dir.glob("#{Rails.root}/app/models/**/*.rb") do |model_path| temp = model_path.split(/\/models\//) models.push temp.last.gsub(/\.rb$/, '').camelize.constantize rescue nil end 

    Le Rails implémente les descendants la méthode, mais les modèles n’héritent pas nécessairement d’ ActiveRecord::Base , par exemple, la classe qui inclut le module ActiveModel::Model aura le même comportement qu’un modèle, mais ne sera pas lié à un table.

    Donc, en complément de ce que disent les collègues ci-dessus, le moindre effort ferait cela:

    Patch de singe de classe Class de Ruby:

     class Class def extends? constant ancestors.include?(constant) if constant != self end end 

    et les models méthode, y compris les ancêtres, comme ceci:

    La méthode Module.constants retourne (superficiellement) une collection de symbols , au lieu de constantes, donc, la méthode Array#select peut être substituée comme ce patch de singe du 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 

    Patch de singe de Ssortingng .

     class Ssortingng 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 

    Et enfin la méthode des modèles

     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 

    J’ai essayé beaucoup de ces réponses sans succès dans Rails 4 (wow ils ont changé une chose ou deux pour l’amour de Dieu) j’ai décidé d’append le mien. Celles qui appelaient ActiveRecord :: Base.connection et tiraient les noms des tables fonctionnaient mais n’avaient pas obtenu le résultat que je voulais car j’ai caché certains modèles (dans un dossier à l’intérieur de l’app / models /) que je ne voulais pas effacer:

     def list_models Dir.glob("#{Rails.root}/app/models/*.rb").map{|x| x.split("/").last.split(".").first.camelize} end 

    Je mets ça dans un initialiseur et je peux l’appeler de n’importe où. Empêche l’utilisation de la souris inutile.

    peut vérifier cela

     @models = ActiveRecord::Base.connection.tables.collect{|t| t.underscore.singularize.camelize} 

    En supposant que tous les modèles sont dans l’application / modèles et que vous avez grep & awk sur votre serveur (la majorité des cas),

     # extract lines that match specific ssortingng, and print 2nd word of each line results = `grep -r "< ActiveRecord::Base" app/models/ | awk '{print $2}'` model_names = results.split("\n") 

    C'est plus rapide que Rails.application.eager_load! ou parcourir chaque fichier avec Dir .

    MODIFIER:

    L'inconvénient de cette méthode est qu'elle manque des modèles qui héritent indirectement d'ActiveRecord (par exemple, FictionalBook < Book ). Le moyen le plus sûr est Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) , même si c'est un peu lent.

    Je lance cet exemple ici si quelqu’un le trouve utile. La solution est basée sur cette réponse https://stackoverflow.com/a/10712838/473040 .

    Disons que vous avez une colonne public_uid qui est utilisée comme identifiant primaire pour le monde extérieur (vous pouvez trouver des raisons pour lesquelles vous voudriez le faire ici )

    Maintenant, disons que vous avez introduit ce champ dans un tas de modèles existants et que vous souhaitez maintenant régénérer tous les enregistrements qui ne sont pas encore définis. Vous pouvez le faire comme ça

     # 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.atsortingbute_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 

    vous pouvez maintenant lancer rake di:public_uids:generate