[Ruby-on-rails] क्या आपके रेल ऐप में सभी मॉडलों का संग्रह प्राप्त करने का कोई तरीका है?


Answers

रेल 3, 4 और 5 के लिए पूरा जवाब है:

यदि cache_classes बंद है (डिफ़ॉल्ट रूप से यह विकास में बंद है, लेकिन उत्पादन में):

Rails.application.eager_load!

फिर:

ActiveRecord::Base.descendants

यह सुनिश्चित करता है कि आपके आवेदन के सभी मॉडल, चाहे वे कहां हैं, भरे हुए हैं, और आप जिन रत्नों का उपयोग कर रहे हैं, वे मॉडल भी प्रदान करते हैं।

यह उन वर्गों पर भी काम करना चाहिए जो ActiveRecord::Base से प्राप्त होते हैं, जैसे कि रेल 5 में ApplicationRecord रिकॉर्डर, और केवल वंशजों की उस उप-वापसी को वापस करें:

ApplicationRecord.descendants

यदि आप यह कैसे करना चाहते हैं, इसके बारे में और जानना चाहते हैं, तो ActiveSupport::DescendantsTracker

Question

क्या कोई तरीका है कि आप अपने रेल ऐप में सभी मॉडलों का संग्रह प्राप्त कर सकते हैं?

असल में, क्या मैं पसंद कर सकता हूं: -

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



ActiveRecord::Base.connection.tables




बस इस पर आया, क्योंकि मुझे अपने गुणों के साथ सभी मॉडलों को मुद्रित करने की आवश्यकता है (@ आदित्य संघी की टिप्पणी पर निर्मित):

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}"}}



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



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

वापस होगा

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

अतिरिक्त जानकारी यदि आप मॉडल के बिना ऑब्जेक्ट नाम पर कोई विधि कॉल करना चाहते हैं: स्ट्रिंग अज्ञात विधि या चर त्रुटियां इसका उपयोग करती हैं

model.classify.constantize.attribute_names



मुझे लगता है कि यदि आपके पास टेबल-कम मॉडल नहीं हैं तो @ hnovick का समाधान एक अच्छा है। यह समाधान विकास मोड में भी काम करेगा

मेरा दृष्टिकोण काफी अलग है हालांकि -

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

वर्गीकृत आपको अच्छी तरह से एक स्ट्रिंग से कक्षा का नाम देने के लिए माना जाता है । safe_constantize सुनिश्चित करता है कि आप अपवाद फेंकने के बिना इसे सुरक्षित रूप से कक्षा में बदल सकते हैं। यदि आपके पास डेटाबेस टेबल हैं जो मॉडल नहीं हैं तो इसकी आवश्यकता है। कॉम्पैक्ट ताकि गणना में किसी भी नाखून को हटा दिया जा सके।




एक पंक्ति पर: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize } Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }




यह रेल 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



यह मेरे लिए काम करता प्रतीत होता है:

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

जब रेल का उपयोग किया जाता है तो रेल केवल मॉडल लोड करता है, इसलिए Dir.glob लाइन को मॉडल निर्देशिका में सभी फ़ाइलों की आवश्यकता होती है।

एक बार आपके पास सरणी में मॉडल हो जाने के बाद, आप जो भी सोच रहे थे वह कर सकते हैं (उदाहरण के लिए कोड देखें):

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



Rails विधि descendants लागू करता है, लेकिन मॉडलों को कभी भी ActiveRecord::Base से विरासत में नहीं मिलता है, उदाहरण के लिए, कक्षा जिसमें मॉड्यूल ActiveModel::Model शामिल है, 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



यह जांच सकते हैं

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



अगर मैं इसे उपयोगी पाता हूं तो मैं सिर्फ इस उदाहरण को फेंक रहा हूं। समाधान इस उत्तर https://.com/a/10712838/473040 पर आधारित है।

मान लें कि आपके पास कॉलम public_uid जो बाहरी दुनिया के लिए प्राथमिक आईडी के रूप में उपयोग किया जाता है (आप पाते हैं कि आप here ऐसा क्यों करना चाहते here )

अब मान लें कि आपने इस मॉडल को मौजूदा मॉडल के समूह पर पेश किया है और अब आप उन सभी रिकॉर्ड्स को पुन: उत्पन्न करना चाहते हैं जो अभी तक सेट नहीं हैं। आप ऐसा कर सकते हैं

# 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 चला सकते हैं rake di:public_uids:generate




सभी रेलों को प्री-लोड से बचने के लिए, आप यह कर सकते हैं:

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

require_dependency (f) वही है जो Rails.application.eager_load! का उपयोग करता है। यह पहले से ही आवश्यक फाइल त्रुटियों से बचना चाहिए।

फिर आप एआर मॉडल सूचीबद्ध करने के लिए सभी प्रकार के समाधानों का उपयोग कर सकते हैं, जैसे ActiveRecord::Base.descendants




Links