Skip to content

Commit 1da69fe

Browse files
committed
Introduce virtual_has_many through
This will automatically add the ruby deletate It is assumed that a nil association is allowed, and a default of [] will be returned. The delegate defined does still have logic for attributes (namely the has_attribute?) part. Since there will be no attribute with this name, it will be ignored. Not using traditional `delegate`, because that does not provide the `default` option
1 parent 2f68387 commit 1da69fe

File tree

3 files changed

+24
-1
lines changed

3 files changed

+24
-1
lines changed

lib/active_record/virtual_attributes/virtual_reflections.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ def virtual_has_one(name, uses: nil, **options)
1414
add_virtual_reflection(reflection, name, uses)
1515
end
1616

17-
def virtual_has_many(name, uses: nil, **options)
17+
def virtual_has_many(name, uses: nil, source: nil, through: nil, **options)
1818
define_method(:"#{name.to_s.singularize}_ids") do
1919
records = send(name)
2020
records.respond_to?(:ids) ? records.ids : records.collect(&:id)
2121
end
22+
define_delegate(name, source || name, :to => through, :allow_nil => true, :default => []) if through
2223
reflection = ActiveRecord::Associations::Builder::HasMany.build(self, name, nil, options)
2324
add_virtual_reflection(reflection, name, uses)
2425
end

spec/db/models.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ class Book < VirtualTotalTestBase
135135
has_many :photos, :as => :imageable, :class_name => "Photo"
136136
has_one :current_photo, -> { all.merge(Photo.order(:id => :desc)) }, :as => :imageable, :class_name => "Photo"
137137

138+
virtual_has_many :author_books, :through => :author, :source => :books
139+
# sorry. books.books doesn't totally make sense.
140+
virtual_has_many :books, :through => :author
141+
138142
scope :ordered, -> { order(:created_on => :desc) }
139143
scope :published, -> { where(:published => true) }
140144
scope :wip, -> { where(:published => false) }

spec/virtual_delegates_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,24 @@ def self.connection
300300
end
301301
end
302302

303+
describe "virtual_has_many with :through" do
304+
it "with :source works" do
305+
Author.create_with_books(2)
306+
author = Author.create_with_books(3)
307+
books = author.books.order(:id)
308+
309+
expect(books.first.author_books.order(:id)).to eq(books)
310+
end
311+
312+
it "without :source works" do
313+
Author.create_with_books(2)
314+
author = Author.create_with_books(3)
315+
books = author.books.order(:id)
316+
317+
expect(books.first.books.order(:id)).to eq(books)
318+
end
319+
end
320+
303321
describe "#determine_method_names (private)" do
304322
it "works with column and to" do
305323
expect(determine_method_names("column", "relation", nil)).to eq([:column, :relation, :column])

0 commit comments

Comments
 (0)