มีวิธีรับโมเดลทั้งหมดในแอพ Rails ของคุณหรือไม่?


201

มีวิธีที่คุณจะได้รับชุดโมเดลทั้งหมดในแอพ Rails ของคุณหรือไม่?

โดยทั่วไปฉันสามารถทำสิ่งที่ชอบของ: -

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

1
หากคุณจำเป็นต้องรวบรวมทุกรุ่นรวมถึงรุ่นของเครื่องยนต์ Rails / รถไฟดูคำตอบโดย@jaime
Andrei

ไม่ทำงานบนราง 5.1
aks

คำตอบ:


98

แก้ไข: ดูความคิดเห็นและคำตอบอื่น ๆ มีคำตอบที่ฉลาดกว่านี้! หรือพยายามปรับปรุงอันนี้เป็นวิกิชุมชน

แบบจำลองไม่ได้ลงทะเบียนตัวเองกับวัตถุหลักดังนั้นไม่ Rails ไม่มีรายการของแบบจำลอง

แต่คุณยังสามารถดูเนื้อหาของไดเรกทอรีรุ่นของแอปพลิเคชันของคุณ ...

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

แก้ไข: ความคิดอื่น (ป่า) จะใช้การสะท้อน Ruby เพื่อค้นหาทุกชั้นที่ขยาย ActiveRecord :: Base ไม่ทราบวิธีที่คุณสามารถแสดงรายการคลาสทั้งหมดแม้ว่า ...

แก้ไข: เพื่อความสนุกฉันพบวิธีที่จะแสดงรายการคลาสทั้งหมด

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

แก้ไข: ในที่สุดก็ประสบความสำเร็จในการแสดงรายการทุกรุ่นโดยไม่ต้องดูที่ไดเรกทอรี

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

6
FYI ฉันจับเวลาทั้งสองวิธีเพื่อความสนุก การค้นหาไดเรกทอรีเป็นลำดับความสำคัญเร็วกว่าการค้นหาในชั้นเรียน นั่นอาจจะเป็นที่เห็นได้ชัด แต่ตอนนี้คุณรู้ :)
เอ็ดเวิร์ดเดอร์สัน

9
นอกจากนี้สิ่งสำคัญคือต้องทราบว่าการค้นหารุ่นผ่านวิธีคงที่จะไม่รวมสิ่งที่ไม่ได้อ้างอิงตั้งแต่เริ่มต้นแอปเนื่องจากโหลดเฉพาะรุ่นตามต้องการ
Edward Anderson

4
ฉันชอบ 'Kernel.const_get constant_name' ถึง 'eval constant_name'
Jeremy Weathers

3
RAILS_ROOTไม่มีใน Rails 3 แล้วให้ใช้แทนDir.glob(Rails.root.join('app/models/*'))
fanaugen

1
ที่จริงแล้วโมเดลต่าง ๆ ลงทะเบียนตัวเองเป็นลูกหลานของActiveRecord::Baseตอนนี้ดังนั้นหากคุณอยากโหลดทุกรุ่นคุณสามารถทำซ้ำได้อย่างง่ายดาย - ดูคำตอบของฉันด้านล่าง
sj26

393

คำตอบทั้งหมดสำหรับ Rails 3, 4 และ 5 คือ:

หากcache_classesปิดอยู่ (โดยค่าเริ่มต้นจะปิดอยู่ระหว่างการพัฒนา แต่อยู่ระหว่างการผลิต):

Rails.application.eager_load!

แล้ว:

ActiveRecord::Base.descendants

สิ่งนี้ทำให้แน่ใจว่าทุกรุ่นในแอปพลิเคชันของคุณไม่ว่าจะอยู่ที่ไหนโหลดและอัญมณีใด ๆ ที่คุณใช้ซึ่งมีการโหลดโมเดลไว้ด้วย

สิ่งนี้ควรทำงานกับคลาสที่สืบทอดมาActiveRecord::Baseเช่นApplicationRecordใน Rails 5 และส่งคืนเฉพาะทรีย่อยของลูกหลานเท่านั้น:

ApplicationRecord.descendants

หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับวิธีการนี้จะทำตรวจสอบActiveSupport :: DescendantsTracker


33
! น่ากลัว นี่ควรเป็นคำตอบที่ยอมรับได้ สำหรับใครก็ตามที่ใช้สิ่งนี้ในภารกิจคราด: ทำให้งานของคุณขึ้นอยู่กับ:environmentการeager_load!ทำงาน
Jo Liss

1
หรือเป็นทางเลือกที่เร็วกว่าเล็กน้อยRails.application.eager_load!คุณสามารถโหลดแบบจำลอง:Dir.glob(Rails.root.join('app/models/*')).each do |x| require x end
Ajedi32

5
@ Ajedi32 ที่ไม่สมบูรณ์แบบสามารถกำหนดได้นอกไดเรกทอรีเหล่านั้นโดยเฉพาะเมื่อใช้เครื่องมือที่มีรุ่น ดีขึ้นเล็กน้อยอย่างน้อย glob ทั้งหมดRails.paths["app/models"].existentไดเรกทอรี การโหลดแอปพลิเคชั่นทั้งหมดนั้นเป็นคำตอบที่สมบูรณ์ยิ่งขึ้นและจะทำให้แน่ใจว่าไม่มีที่เหลืออีกแล้วสำหรับรุ่นที่จะถูกกำหนด
sj26

2
ฉันได้สิ่งที่ sj26 หมายถึง แต่อาจมีข้อผิดพลาดเล็กน้อย: ตราบใดที่ฉันรู้ว่าในสภาพแวดล้อมการพัฒนา cache_classes ปิด (เท็จ) นั่นเป็นเหตุผลที่คุณต้องกระตือรือร้นที่จะโหลดแอปพลิเคชันด้วยตนเองเพื่อเข้าถึงทุกรุ่น อธิบายที่นี่
masciugo

3
@ Ajedi32 อีกครั้งไม่ใช่คำตอบที่สมบูรณ์ หากคุณต้องการที่จะโหลดเฉพาะรุ่นลอง:Rails.application.paths["app/models"].eager_load!
sj26

119

ในกรณีที่ทุกคนสะดุดกับเรื่องนี้ฉันมีวิธีแก้ไขปัญหาอื่นโดยไม่พึ่งพาการอ่านหรือขยายชั้นเรียน ...

ActiveRecord::Base.send :subclasses

นี่จะส่งคืนอาร์เรย์ของคลาส ดังนั้นคุณสามารถทำได้

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

8
ทำไมคุณไม่ใช้ActiveRecord::Base.subclassesแต่ต้องใช้send? นอกจากนี้ดูเหมือนว่าคุณจะต้อง "สัมผัส" โมเดลก่อนที่มันจะปรากฏขึ้นตัวอย่างเช่นc = Category.newมันจะปรากฏขึ้น มิฉะนั้นจะไม่
nonopolarity

52
ใน Rails 3 สิ่งนี้ได้ถูกเปลี่ยนเป็นActiveRecord::Base.descendants
Tobias Cohen

3
คุณต้องใช้ "ส่ง" เนื่องจาก: สมาชิกคลาสย่อยได้รับการคุ้มครอง
Kevin Rood

11
ขอบคุณสำหรับเคล็ดลับ Rails 3 สำหรับใครก็ตามที่เข้ามาคุณยังต้อง "แตะ" โมเดลก่อนก่อนจึงActiveRecord::Base.descendantsจะแสดงรายการ
nfm

3
ในทางเทคนิคใน Rails 3 คุณมีคลาสย่อยและลูกหลานพวกมันหมายถึงสิ่งต่าง ๆ
sj26

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

จะกลับมา

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

ข้อมูลเพิ่มเติมหากคุณต้องการเรียกวิธีการในชื่อวัตถุโดยไม่มีรุ่น: วิธีการที่ไม่รู้จักสตริงหรือข้อผิดพลาดตัวแปรใช้สิ่งนี้

model.classify.constantize.attribute_names

8
สิ่งนี้จะทำให้คุณได้รับตารางทั้งหมดไม่ใช่แค่แบบจำลองเนื่องจากบางตารางไม่ได้มีรุ่นที่เกี่ยวข้องเสมอไป
courtimas

คำตอบนี้ควรพิจารณาว่าไม่ถูกต้องเนื่องจากเป็นไปได้ (และเป็นเรื่องปกติในการตั้งค่าแบบดั้งเดิม) เพื่อกำหนดชื่อของตารางให้เป็นชื่ออื่นนอกเหนือจากชื่อที่มีหลายชื่อของแบบจำลอง คำตอบนี้ให้คำตอบที่ถูกต้องแม้ว่าการตั้งค่าจะเบี่ยงเบนจากการกำหนดค่าเริ่มต้น
lorefnon

ในบางกรณีวิธีนี้ใช้งานได้ดีกว่าการActiveRecord::Base.send :subclassesค้นหาชื่อตารางเป็นความคิดที่ดี การสร้างชื่อรุ่นโดยอัตโนมัติอาจมีปัญหาตามที่กล่าวถึงใน lorefnon
Tilo

.capitalize.singularize.camelize.classifyสามารถเปลี่ยนไป
Maxim

34

ฉันค้นหาวิธีการทำสิ่งนี้และลงเอยด้วยการเลือกวิธีนี้:

in the controller:
    @data_tables = ActiveRecord::Base.connection.tables

in the view:
  <% @data_tables.each do |dt|  %>
  <br>
  <%= dt %>
  <% end %>
  <br>

แหล่งที่มา: http://portfo.li/rails/348561-how-can-one-list-all-database-tables-from-one-project


1
นี่เป็นวิธีเดียวที่ฉันจะได้รับทุกรุ่นรวมถึงรุ่นของเครื่องยนต์ Rails ที่ใช้ในแอพ ขอบคุณสำหรับทิป!
Andrei

2
วิธีการที่มีประโยชน์ไม่กี่แบบ: ActiveRecord::Base.connection.tables.each{|t| begin puts "%s: %d" % [t.humanize, t.classify.constantize.count] rescue nil end}บางรุ่นอาจไม่เปิดใช้งานดังนั้นคุณต้องช่วยชีวิต
Andrei

2
การปรับตัว @ Andrei นิดหน่อย: model_classes = ActiveRecord::Base.connection.tables.collect{|t| t.classify.constantize rescue nil }.compact
Max Williams

30

สำหรับRails5รุ่นอยู่ในขณะนี้คลาสย่อยของApplicationRecordเพื่อที่จะได้รับรายชื่อของทุกรุ่นใน app ของคุณที่คุณทำ:

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

หรือสั้นกว่า:

ApplicationRecord.descendants.collect(&:name)

หากคุณอยู่ในโหมด dev คุณจะต้องกระตือรือร้นที่จะโหลดโมเดลก่อน:

Rails.application.eager_load!

1
ฉันคิดว่าสิ่งนี้จะต้องมีการโหลดคลาสแล้วและจะให้ผลลัพธ์ที่ไม่สมบูรณ์ในสภาพแวดล้อมการพัฒนาโดยเปิดใช้งานการโหลดอัตโนมัติ ฉันจะไม่ลงคะแนน แต่อาจพูดถึงเรื่องนี้ในคำตอบ
lorefnon

ค่าโดยสารเพียงพออัพเดต
Nimir

ฉันใช้ Rails 6.0.2 และ eager_load แล้ว! ไม่ได้ทำให้เมธอดสืบทอดเพื่อส่งคืนสิ่งใดนอกจากอาร์เรย์ว่าง
jgomo3

23

ฉันคิดว่าวิธีแก้ปัญหาของ @ hnovick เป็นสิ่งที่ยอดเยี่ยมหากคุณไม่มีโมเดลที่ไม่ต้องใช้โต๊ะ วิธีนี้จะทำงานในโหมดการพัฒนาเช่นกัน

วิธีการของฉันแตกต่างอย่างละเอียดว่า -

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

ประเภทควรที่ดีที่จะให้ชื่อของชั้นจากสตริงอย่างถูกต้อง safe_constantize ทำให้แน่ใจว่าคุณสามารถเปลี่ยนเป็นคลาสได้อย่างปลอดภัยโดยไม่ต้องโยนข้อยกเว้น สิ่งนี้จำเป็นในกรณีที่คุณมีตารางฐานข้อมูลที่ไม่ใช่รุ่น กะทัดรัดเพื่อให้นิลส์ใด ๆ ในการแจงนับถูกลบออก


3
มันยอดเยี่ยม @Aditya Sanghi safe_constantizeผมไม่ทราบเกี่ยวกับ
lightyrs

สำหรับ Rails 2.3.x ให้ใช้: ActiveRecord :: Base.connection.tables.map {| x | x.classify.constantize กู้ภัยศูนย์} .compact
iheggie

@iheggie โดยทั่วไปการโพสต์นั้นเป็นคำตอบที่แยกต่างหากดีกว่าการแก้ไขในโพสต์ที่มีอยู่
Pokechu22

ขอบคุณฉันพบว่าคุณตอบคำตอบที่เหมาะสมที่สุดสำหรับฉัน #adiya
นักเล่นกลลวงตา

21

ถ้าคุณต้องการแค่ชื่อคลาส:

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

เพียงแค่เรียกใช้ในคอนโซล Rails ไม่มีอะไรเพิ่มเติม โชคดี!

แก้ไข: @ sj26 ถูกต้องคุณต้องเรียกใช้สิ่งนี้ก่อนจึงจะสามารถเรียกทายาท:

Rails.application.eager_load!

สิ่งที่ฉันต้องการ ขอบคุณ!
sunsations

โทรmapด้วยputs? ฉันไม่เข้าใจประเด็นนี้ActiveRecord::Base.descendants.map(&:model_name)
Nuno Costa

คุณสามารถทำเช่นนั้นได้ แต่จะอยู่ในอาร์เรย์เดียวแทนที่จะอ่านทีละบรรทัดในรูปแบบที่อ่านง่ายกว่ามาก
Jordan Michael Rush

17

ดูเหมือนว่าจะใช้งานได้สำหรับฉัน:

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

Rails จะโหลดโมเดลเมื่อมีการใช้งานเท่านั้นดังนั้นบรรทัด Dir.glob "ต้องการ" ไฟล์ทั้งหมดในไดเรกทอรีรุ่น

เมื่อคุณมีแบบจำลองในอาร์เรย์คุณสามารถทำสิ่งที่คุณคิด (เช่นในรหัสมุมมอง):

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

ขอบคุณ bhousel ฉันเริ่มต้นด้วยวิธีการแบบนี้ แต่ลงเอยด้วยการใช้วิธีแก้ปัญหาที่ Vincent โพสต์ไว้ด้านบนเพราะมันหมายความว่าฉันไม่จำเป็นต้อง "Modelize" ชื่อไฟล์เช่นกัน พวกเขาอีกครั้ง)
mr_urf

กับไดเรกทอรีย่อย:...'/app/models/**/*.rb'
artemave

Object.subclasses_ofเลิกใช้หลังจาก v2.3.8
David J.

11

ในหนึ่งบรรทัด: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }


7
อันนี้ดีตั้งแต่ใน Rails 3 โมเดลของคุณจะไม่โหลดอัตโนมัติตามค่าเริ่มต้นดังนั้นวิธีการข้างต้นจำนวนมากจะไม่ส่งคืนโมเดลที่เป็นไปได้ทั้งหมด การเปลี่ยนแปลงของฉันยังจับรูปแบบในปลั๊กอินและไดเรกทอรีย่อย:Dir['**/models/**/*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
wbharding

2
@wbharding มันค่อนข้างดี แต่มันเกิดข้อผิดพลาดเมื่อพยายามทำให้ชื่อของการทดสอบรูปแบบ rspec คงที่ ;-)
Ajedi32

@wbharding ทางออกที่ดี แต่มันจะหยุดเมื่อคุณมีรุ่นเนมสเปซ
Marcus Mansur

10

ActiveRecord::Base.connection.tables


นอกจากนี้การติดตามที่ดีคือ <table_name> .column_names เพื่อแสดงคอลัมน์ทั้งหมดในตาราง ดังนั้นสำหรับตารางผู้ใช้ของคุณคุณจะดำเนินการ User.column_names
ทำเครื่องหมาย Locklear

สิ่งนี้จะทำให้คุณได้รับตารางทั้งหมดไม่ใช่แค่แบบจำลองเนื่องจากบางตารางไม่ได้มีรุ่นที่เกี่ยวข้องเสมอไป
courtimas

7

ในหนึ่งบรรทัด:

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

2
ไม่ได้แสดงโมเดลทั้งหมดสำหรับฉัน ไม่แน่ใจว่าทำไม ในความเป็นจริงมันสั้นคู่
courtimas

1
ทำงานให้ฉัน สายเพียงเล็กน้อยที่จะตอบว่าทั้งหมด ให้เวลา
boulder_ruby

2
มันอาจต้องRails.application.eager_load!ก่อนที่จะดำเนินการในโหมดการพัฒนา
denis.peplin

7

ฉันยังไม่ได้แสดงความคิดเห็น แต่ฉันคิดว่าคำตอบ sj26ควรเป็นคำตอบที่ดีที่สุด เพียงคำใบ้:

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

6

ด้วยRails 6ทำให้Zetiwerkกลายเป็นตัวโหลดโค้ดเริ่มต้น

สำหรับการโหลดที่กระตือรือร้นลอง:

Zeitwerk::Loader.eager_load_all

แล้วก็

ApplicationRecord.descendants

5

ใช่มีหลายวิธีที่คุณสามารถค้นหาชื่อรุ่นทั้งหมดได้ แต่สิ่งที่ฉันทำในอัญมณี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

3

ใช้งานได้กับ 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

upvolt สำหรับ Rails.application.eager_load! ความคิด
เทียบเท่า 8

3

เพื่อหลีกเลี่ยงการโหลด Rails ล่วงหน้าทั้งหมดคุณสามารถทำได้ดังนี้:

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

require_dependency (f) เหมือนกันกับที่ Rails.application.eager_load!ใช้ สิ่งนี้ควรหลีกเลี่ยงข้อผิดพลาดไฟล์ที่จำเป็นแล้ว

จากนั้นคุณสามารถใช้โซลูชันทุกชนิดเพื่อระบุรุ่นของ AR เช่น ActiveRecord::Base.descendants


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

พ่น TypeError: ไม่มีการแปลง Symbol เป็นนัยใน String ในคอนโซล
snowangel

1

นี่คือวิธีการแก้ปัญหาที่ได้รับการตรวจสอบด้วยแอพ Rails ที่ซับซ้อน

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 ฯลฯ


1

เพิ่งเจอสิ่งนี้เพราะฉันต้องพิมพ์ทุกรุ่นด้วยคุณสมบัติของพวกเขา (สร้างขึ้นตามความคิดเห็นของ @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}"}}

1

สิ่งนี้ใช้ได้สำหรับฉัน ขอขอบคุณเป็นพิเศษสำหรับโพสต์ทั้งหมดข้างต้น สิ่งนี้จะส่งคืนคอลเลกชันทุกรุ่นของคุณ

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

1

การRailsใช้วิธีการdescendantsแต่รุ่นที่ไม่จำเป็นต้องสืบทอดมาActiveRecord::Baseเช่นคลาสที่มีโมดูลActiveModel::Modelจะมีพฤติกรรมเหมือนกับโมเดล แต่จะไม่เชื่อมโยงกับตาราง

ดังนั้นการเสริมสิ่งที่กล่าวว่าเพื่อนร่วมงานข้างต้นความพยายามเพียงเล็กน้อยจะทำเช่นนี้:

Monkey Patch จากคลาสClassของ 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

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

1
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

สิ่งนี้จะให้คลาสโมเดลทั้งหมดที่คุณมีในโปรเจ็กต์ของคุณ


0
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

ฉันลองคำตอบเหล่านี้จำนวนมากไม่สำเร็จในRails 4 (ว้าวพวกเขาเปลี่ยนสิ่งหนึ่งหรือสองเพื่อประโยชน์ของพระเจ้า) ฉันตัดสินใจที่จะเพิ่มของฉันเอง คนที่เรียกว่า ActiveRecord :: Base.connection และดึงชื่อตารางทำงาน แต่ไม่ได้ผลลัพธ์ที่ฉันต้องการเพราะฉันซ่อนบางรุ่น (ในโฟลเดอร์ภายในแอพ / รุ่น /) ที่ฉันไม่ต้องการ ลบ:

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

ฉันใส่มันไว้ใน initializer และสามารถโทรจากที่ใดก็ได้ ป้องกันการใช้เมาส์โดยไม่จำเป็น


0

สามารถตรวจสอบสิ่งนี้ได้

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

0

สมมติว่าทุกรุ่นอยู่ในแอพ / รุ่นและคุณมี 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)ถึงแม้ว่ามันจะช้าก็ตาม


0

ฉันแค่โยนตัวอย่างนี้ที่นี่ถ้าใครพบว่ามีประโยชน์ การแก้ปัญหาจะขึ้นอยู่กับคำตอบนี้https://stackoverflow.com/a/10712838/473040

สมมติว่าคุณมีคอลัมน์public_uidที่ใช้เป็น ID หลักสำหรับโลกภายนอก (คุณสามารถค้นหาสาเหตุที่คุณต้องการทำที่นี่ )

ตอนนี้สมมติว่าคุณได้เปิดใช้ฟิลด์นี้กับกลุ่มของโมเดลที่มีอยู่และตอนนี้คุณต้องการสร้างระเบียนทั้งหมดที่ยังไม่ได้ตั้งค่าใหม่ คุณสามารถทำสิ่งนี้ได้

# 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.