with render_to_string rails how from ruby-on-rails collections model

ruby on rails - render_to_string - ¿Hay alguna forma de obtener una colección de todos los Modelos en su aplicación Rails?



routes in ruby on rails (26)

¿Hay alguna forma de obtener una colección de todos los Modelos en su aplicación Rails?

Básicamente, ¿puedo hacer lo siguiente de:

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


Acabo de encontrar este, ya que necesito imprimir todos los modelos con sus atributos (basado en el comentario de @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}"}}


Aquí hay una solución que ha sido probada con una aplicación compleja de Rails (la que alimenta 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

Toma las mejores partes de las respuestas en este hilo y las combina en la solución más simple y completa. Este maneja los casos donde tus modelos están en subdirectorios, usa set_table_name, etc.


Asumiendo que todos los modelos están en la aplicación / modelos y que tienes grep & awk en tu servidor (la mayoría de los casos),

# 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")

Es más rápido que Rails.application.eager_load! o recorriendo cada archivo con Dir .

EDITAR:

La desventaja de este método es que falla en los modelos que heredan indirectamente de ActiveRecord (por ejemplo, FictionalBook < Book ). La forma más segura es Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) , aunque es un poco lento.



Creo que la solución de @ hnovick es genial si no tienes modelos sin tablas. Esta solución funcionaría también en modo de desarrollo

Mi enfoque es sutilmente diferente -

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

se supone que clasifique para darle el nombre de la clase de una cadena correctamente . safe_constantize garantiza que puede convertirlo en una clase segura sin lanzar una excepción. Esto es necesario en caso de que tenga tablas de bases de datos que no sean modelos. compacto para eliminar cualquier nils en la enumeración.


En caso de que alguien se tropiece con este, tengo otra solución, no confiar en leer dir o extender la clase Class ...

ActiveRecord::Base.send :subclasses

Esto devolverá una matriz de clases. Entonces puedes hacer

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


En una línea: Dir[''app/models//*.rb''].map {|f| File.basename(f, ''.*'').camelize.constantize } Dir[''app/models//*.rb''].map {|f| File.basename(f, ''.*'').camelize.constantize }


En una sola línea:

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


Esto funcionó para mí. Un agradecimiento especial a todas las publicaciones anteriores. Esto debería devolver una colección de todos sus modelos.

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


Esto funciona para 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


Esto parece funcionar para mí:

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

Los rieles solo cargan modelos cuando se usan, por lo que la línea Dir.glob "requiere" todos los archivos en el directorio de modelos.

Una vez que tenga los modelos en una matriz, puede hacer lo que estaba pensando (por ejemplo, en el código de vista):

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


He intentado muchas de estas respuestas sin éxito en Rails 4 (wow cambiaron una cosa o dos por el amor de Dios) Decidí agregar la mía. Los que llamaron a ActiveRecord :: Base.connection y extrajeron los nombres de la tabla funcionaron pero no obtuvieron el resultado que quería porque he ocultado algunos modelos (en una carpeta dentro de la aplicación / modelos /) que no quería borrar:

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

Lo puse en un inicializador y puedo llamarlo desde cualquier lugar. Impide el uso innecesario del mouse.


La respuesta completa para Rails 3, 4 y 5 es:

Si cache_classes está desactivado (por defecto está desactivado en desarrollo, pero en producción):

Rails.application.eager_load!

Entonces:

ActiveRecord::Base.descendants

Esto asegura que todos los modelos en su aplicación, independientemente de dónde se encuentren, se carguen, y también se cargan las gemas que esté utilizando y que proporcionen modelos.

Esto también debería funcionar en las clases que heredan de ActiveRecord::Base , como ApplicationRecord en Rails 5, y devuelve solo ese subárbol de descendientes:

ApplicationRecord.descendants

Si desea obtener más información acerca de cómo se hace esto, consulte ActiveSupport::DescendantsTracker .


No puedo comentar todavía, pero creo que la respuesta sj26 debería ser la mejor respuesta. Solo una pista:

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


Para Rails5, los modelos ahora son subclases de ApplicationRecord por lo tanto, para obtener una lista de todos los modelos en su aplicación, haga lo siguiente:

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

O más corto:

ApplicationRecord.descendants.collect(&:name)

Si se encuentra en el modo de desarrollo, necesitará cargar modelos antes de:

Rails.application.eager_load!


Para evitar precargar todos los Rails, puede hacer esto:

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

require_dependency (f) es lo mismo que Rails.application.eager_load! usos. Esto debería evitar los errores de archivo ya requeridos.

Luego puede usar todo tipo de soluciones para enumerar modelos AR, como ActiveRecord::Base.descendants


Sí, hay muchas formas en que puede encontrar todos los nombres de modelo, pero lo que hice en mi gem es: model_info , le dará todos los modelos incluidos en las gemas.

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

entonces simplemente imprime esto

@model_array


Si solo desea los nombres de clase:

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

Simplemente ejecútelo en la consola de Rails, nada más. ¡Buena suerte!

EDITAR: @ ​​sj26 es correcto, necesita ejecutar esto primero para poder llamar a los descendientes:

Rails.application.eager_load!


Solo estoy lanzando este ejemplo aquí si alguien lo encuentra útil. La solución se basa en esta respuesta https://.com/a/10712838/473040 .

Supongamos que tiene una columna public_uid que se utiliza como ID principal para el mundo exterior (puede encontrar razones por las que le gustaría hacer eso here )

Ahora supongamos que ha introducido este campo en muchos modelos existentes y ahora desea regenerar todos los registros que aún no están configurados. Puedes hacer eso así

# 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

ahora puede ejecutar rake di:public_uids:generate


The Rails implementa los descendants del método, pero los modelos no necesariamente heredan de ActiveRecord::Base , por ejemplo, la clase que incluye el módulo ActiveModel::Model tendrá el mismo comportamiento que un modelo, simplemente no se vinculará a un mesa.

Complementando lo que dicen los colegas de arriba, el más mínimo esfuerzo haría esto:

Monkey Patch de la clase Class of the Ruby:

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

y los models métodos, incluidos los ancestros, como este:

El método Module.constants devuelve (superficialmente) una colección de symbols , en lugar de constantes, entonces, el método Array#select puede ser sustituido como este parche mono del 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 parche de 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

Y, finalmente, el método de los modelos

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


puede verificar esto

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


ActiveRecord::Base.connection.tables


EDITAR: Mire los comentarios y otras respuestas. ¡Hay respuestas más inteligentes que ésta! O intenta mejorar este como wiki de la comunidad.

Los modelos no se registran en un objeto maestro, entonces no, Rails no tiene la lista de modelos.

Pero aún podrías mirar el contenido del directorio de modelos de tu aplicación ...

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

EDITAR: Otra idea (salvaje) sería usar la reflexión de Ruby para buscar todas las clases que amplíen ActiveRecord :: Base. No sé cómo puedes enumerar todas las clases, aunque ...

EDITAR: solo por diversión, encontré una manera de enumerar todas las clases

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

EDITAR: finalmente logró enumerar todos los modelos sin mirar los directorios

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 también quieres manejar la clase derivada, necesitarás probar toda la cadena de la superclase. Lo hice agregando un método a la clase 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


ActiveRecord::Base.connection.tables.map do |model| model.capitalize.singularize.camelize end

regresará

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

Información adicional Si desea llamar a un método en el nombre del objeto sin modelo: método desconocido de cadena o errores variables, use esto

model.classify.constantize.attribute_names


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


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