Есть ли способ получить коллекцию всех моделей в вашем приложении Rails?

Есть ли способ получить коллекцию всех моделей в вашем приложении Rails?

В принципе, могу ли я сделать такие, как: –

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

    26 Solutions collect form web for “Есть ли способ получить коллекцию всех моделей в вашем приложении Rails?”

    EDIT: посмотрите комментарии и другие ответы. Есть более умные ответы, чем этот! Или попробуйте улучшить это как сообщество wiki.

    Модели не регистрируются на главном объекте, поэтому нет, у Rails нет списка моделей.

    Но вы все равно можете посмотреть содержимое каталога моделей вашего приложения …

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

    EDIT: Еще одна (дикая) идея – использовать Ruby reflection для поиска всех classов, расширяющих ActiveRecord :: Base. Не знаю, как вы можете перечислить все classы, хотя …

    EDIT: Просто для удовольствия, я нашел способ перечислить все classы

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

    EDIT: Наконец удалось перечислить все модели, не глядя на каталоги

     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 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 3, 4 и 5:

    Если cache_classes выключен (по умолчанию он отключен в процессе разработки, но cache_classes в производство):

     Rails.application.eager_load! 

    Затем:

     ActiveRecord::Base.descendants 

    Это гарантирует, что все модели в вашем приложении, независимо от того, где они находятся, загружаются, и любые драгоценные камни, которые вы используете, которые предоставляют модели, также загружаются.

    Это также должно работать над classами, которые наследуются от ActiveRecord::Base , например ApplicationRecord в Rails 5, и возвращают только это поддерево потомков:

     ApplicationRecord.descendants 

    Если вы хотите узнать больше о том, как это сделать, ознакомьтесь с ActiveSupport :: DescendantsTracker .

    На всякий случай, когда кто-то натыкается на это, у меня есть другое решение, не полагающееся на чтение дирижабля или расширение classа classа …

     ActiveRecord::Base.send :subclasses 

    Это вернет массив classов. Таким образом, вы можете сделать

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

    вернется

     ["Article", "MenuItem", "Post", "ZebraStripePerson"] 

    Дополнительная информация Если вы хотите вызывать метод по имени объекта без модели: строка неизвестного метода или ошибки переменной используют это

     model.classify.constantize.attribute_names 

    Я искал способы сделать это и в итоге решил:

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

    источник: http://portfo.li/rails/348561-how-can-one-list-all-database-tables-from-one-project

    Я думаю, что решение @ hnovick – classное, если у вас нет моделей без таблиц. Это решение будет работать и в режиме разработки

    Мой подход тонко отличается, хотя –

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

    classify, как предполагается, должно дать вам имя classа из строки правильно . safe_constantize гарантирует, что вы можете безопасно превратить его в class без исключения исключения. Это необходимо, если у вас есть таблицы базы данных, которые не являются моделями. компактный, так что любые нули в перечислении удаляются.

    Если вы хотите просто имена classов:

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

    Просто запустите его в консоли Rails, не более того. Удачи!

    EDIT: @ sj26 прав, вам нужно запустить это сначала, прежде чем вы сможете назвать потомков:

     Rails.application.eager_load! 

    Для моделей Rails5 теперь являются подclassами ApplicationRecord поэтому для получения списка всех моделей в вашем приложении:

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

    Или короче:

     ApplicationRecord.descendants.collect(&:name) 

    Если вы находитесь в режиме dev, вам нужно будет загружать модели до:

     Rails.application.eager_load! 

    Кажется, это работает для меня:

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

    Rails загружает только модели, когда они используются, поэтому строка Dir.glob «требует» всех файлов в каталоге моделей.

    Когда у вас есть модели в массиве, вы можете делать то, о чем вы думали (например, в коде зрения):

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

    В одной строке: 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

    В одной строке:

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

    Я еще не могу комментировать, но я думаю, что ответ sj26 должен быть лучшим ответом. Просто подсказка:

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

    Это работает для 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 

    Чтобы избежать предварительной загрузки всех 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 } 

    Да, есть много способов найти все имена моделей, но то, что я сделал в моем gem model_info , это даст вам все модели, даже включенные в драгоценные камни.

     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-приложением (одним из которых является 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}"}} 

    Это сработало для меня. Особая благодарность всем вышеперечисленным сообщениям. Это должно вернуть коллекцию всех ваших моделей.

     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 

    Rails реализует descendants метода, но модели не обязательно когда-либо наследуются от ActiveRecord::Base , например, class, который включает в себя модуль ActiveModel::Model будет иметь такое же поведение, что и модель, просто не будет связан с Таблица.

    Так что, дополняя то, что говорит коллеги выше, малейшие усилия сделают это:

    Патч обезьяны classа Class of the 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 

    Патч Monkey 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 

    И, наконец, метод моделей

     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 (ничего себе они не изменили вещь или два для бога) я решил добавить свой собственный. Те, которые вызвали ActiveRecord :: Base.connection и вытащили имена таблиц, работали, но не получили результат, который я хотел, потому что я спрятал некоторые модели (в папке внутри приложения / models /), которую я не хотел Удалить:

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

    Я помещаю это в инициализатор и могу позвонить ему из любого места. Предотвращает ненужное использование мыши.

    может проверить это

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

    Предполагая, что все модели находятся в приложении / моделях, и у вас есть 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) Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) , хотя он медленный.

    Я просто бросаю этот пример здесь, если кто-то считает его полезным. Решение основано на этом ответе https://stackoverflow.com/a/10712838/473040 .

    Скажем, у вас есть столбец public_uid который используется как первичный идентификатор для внешнего мира (вы можете найти причины, по которым вы хотели бы сделать это здесь )

    Теперь предположим, что вы ввели это поле в кучу существующих моделей, и теперь вы хотите восстановить все записи, которые еще не установлены. Вы можете сделать это вот так:

     # 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

    Давайте будем гением компьютера.