Есть ли способ получить коллекцию всех моделей в вашем приложении Rails? - PullRequest
187 голосов
/ 05 февраля 2009

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

В основном, я могу сделать что-то вроде: -

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

Ответы [ 27 ]

1 голос
/ 22 ноября 2018
Dir.foreach("#{Rails.root.to_s}/app/models") do |model_path|
  next unless model_path.match(/.rb$/)
  model_class = model_path.gsub(/.rb$/, '').classify.constantize
  puts model_class
end

Это даст вам все классы моделей, которые есть в вашем проекте.

1 голос
/ 17 октября 2016

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

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

Накидка Обезьяны класса Class Рубина:

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

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

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
0 голосов
/ 22 августа 2013
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
0 голосов
/ 03 мая 2013

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

@models = ActiveRecord::Base.connection.tables.collect{|t| t.underscore.singularize.camelize}
0 голосов
/ 15 ноября 2013

Я безуспешно пытался ответить на многие из этих ответов в Rails 4 (вау, они поменяли одну или две вещи, ради бога), я решил добавить свои. Те, которые вызывали ActiveRecord :: Base.connection и извлекали имена таблиц, работали, но не получили желаемый результат, потому что я спрятал некоторые модели (в папке внутри app / models /), которые я не хотел удалить:

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

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

0 голосов
/ 06 октября 2016

Я просто привожу этот пример сюда, если кто-нибудь посчитает его полезным. Решение основано на этом ответе 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

0 голосов
/ 09 октября 2015

Предполагая, что все модели находятся в приложении / моделях, и у вас есть 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.

EDIT:

Недостатком этого метода является то, что он пропускает модели, которые косвенно наследуются от ActiveRecord (например, FictionalBook < Book). Самый верный способ - Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name), хотя он довольно медленный.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...