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





collections model (22)


बस अगर कोई इस पर ठोकर खाता है, तो मुझे एक और समाधान मिल गया है, जो कि डीआईआर पढ़ने या कक्षा वर्ग को विस्तारित करने पर निर्भर नहीं है ...

ActiveRecord::Base.send :subclasses

यह कक्षाओं की एक सरणी वापस कर देगा। तो आप कर सकते हैं

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

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

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

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



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



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

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

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

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




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



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

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



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

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



मैं अभी तक टिप्पणी नहीं कर सकता, लेकिन मुझे लगता है कि sj26 उत्तर शीर्ष जवाब होना चाहिए। बस एक संकेत:

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



मैंने इन उत्तरों में से कई उत्तरों को रेल 4 में असफल तरीके से करने की कोशिश की है (वाह उन्होंने भगवान के लिए एक या दो चीज़ों को बदल दिया) मैंने अपना खुद का फैसला करने का फैसला किया। जिन्हें ActiveRecord :: Base.connection कहा जाता है और तालिका नामों को खींच लिया जाता है लेकिन मुझे परिणाम नहीं मिला क्योंकि मैं कुछ मॉडल (ऐप / मॉडल / के अंदर एक फ़ोल्डर में) छिपा हुआ हूं जिसे मैं नहीं चाहता था हटाएँ:

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

मैंने इसे प्रारंभकर्ता में रखा और इसे कहीं से भी कॉल कर सकता हूं। अनावश्यक माउस-उपयोग रोकता है।




यदि आप केवल कक्षा के नाम चाहते हैं:

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

इसे रेल कंसोल में चलाएं, और कुछ नहीं। सौभाग्य!

संपादित करें: @ sj26 सही है, आप वंश को कॉल करने से पहले इसे पहले चलाने की जरूरत है:

Rails.application.eager_load!



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

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

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

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




हां, आप सभी मॉडल नामों को ढूंढने के कई तरीके हैं लेकिन मेरे मणि 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



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

  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 %>



Rails5 मॉडल के लिए अब एप्लिकेशन रिकॉर्ड्स के उप-वर्ग हैं ताकि आप अपने ऐप में सभी मॉडलों की सूची प्राप्त कर सकें:

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

या छोटा:

ApplicationRecord.descendants.collect(&:name)

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

Rails.application.eager_load!



अगर मैं इसे उपयोगी पाता हूं तो मैं सिर्फ इस उदाहरण को फेंक रहा हूं। समाधान इस उत्तर 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['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize } Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }




मान लें कि सभी मॉडल एप / मॉडलों में हैं और आपके पास अपने सर्वर (ज्यादातर मामलों) पर 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! से 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) Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) , भले ही यह Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) धीमी हो।




यहां एक समाधान है जिसे एक जटिल रेल ऐप (एक पावर स्क्वायर) के साथ पेश किया गया है।

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 आदि का उपयोग करें।




सिर्फ एक पंक्ति में:

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



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



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



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

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

Rails.application.eager_load!

फिर:

ActiveRecord::Base.descendants

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

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

ApplicationRecord.descendants

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




यह उल्लेख करना महत्वपूर्ण है कि चिंताओं का उपयोग कई लोगों द्वारा बुरा विचार माना जाता है।

  1. इस आदमी की तरह
  2. और ये वाला

कुछ कारणों से:

  1. दृश्यों के पीछे कुछ अंधेरा जादू हो रहा है - चिंता पैचिंग include विधि include है, एक पूर्ण निर्भरता हैंडलिंग सिस्टम है - जिस तरह से कुछ पुरानी रूबी मिश्रित पैटर्न के लिए बहुत अधिक जटिलता है।
  2. आपकी कक्षाएं कम सूखी नहीं हैं। यदि आप विभिन्न मॉड्यूल में 50 सार्वजनिक विधियों को प्रस्तुत करते हैं और उन्हें शामिल करते हैं, तो आपकी कक्षा में अभी भी 50 सार्वजनिक विधियां हैं, यह सिर्फ इतना है कि आप उस कोड की गंध को छिपाते हैं, ड्रॉर्स में अपना कचरा डालें।
  3. कोडेबेस वास्तव में उन सभी चिंताओं के साथ नेविगेट करना कठिन है।
  4. क्या आप सुनिश्चित हैं कि आपकी टीम के सभी सदस्यों को समान समझ है कि वास्तव में चिंता का विषय क्या होना चाहिए?

पैर में खुद को शूट करने के लिए चिंताएं आसान तरीके हैं, उनके साथ सावधान रहें।





ruby-on-rails collections model