Reputation: 5695
I've separated some piece of functionality into a service/library of sorts, to try to cleanly separate this functionality from the rest of my application. I'll call it the service from here on.
Currently this small service leaks ActiveRecord objects into the calling code, but I'd really prefer the separation to be cleaner.
Service's API:
module MyService
# This method should really be read-only, but we're exposing
# ActiveRecord objects, so the caller can mess around with them. :(
def self.people
Person.all
end
end
Calling code example
MyService.people.find_each(batch_size: 500) do |person|
# Do something useful with person
end
I'm thinking converting the ActiveRecord objects into Structs before returning them to the calling code could be an idea to achieve this.
There's just one problem with this: The Person model has thousands of entries. Thus my naive approach would instantiate thousands of ActiveRecord, copy their contents to thousands of Structs and then return. This would obviously be a huge memory hog.
What I'd like is to be able for the calling code to fetch the Person objects in batches, and avoid having MyService.people instantiating all records at once.
I really can't completely wrap my head around how this can be implemented. Can anyone point me in the right direction here?
Upvotes: 1
Views: 33
Reputation: 18813
You can write your own method on MyService
that expects a block and converts the Person
to a read-only version before it yield
s.
module MyService
ReadOnlyPerson = Struct.new(:first_name, :last_name)
def self.each_person
Person.find_each do |person|
read_only_person = ReadOnlyPerson.new(person.first_name, person.last_name)
yield read_only_person
end
end
end
MyWrapper.each_person do |person|
puts person.first_name
end
If you want to keep full ActiveRecord
functionality except for writing, you can also directly mark your instances with readonly!
:
def self.each_person
Person.find_each do |person|
person.readonly!
yield person
end
end
You can still assign things like person.first_name = 'Phil'
, but trying to save
will raise an exception:
ActiveRecord::ReadOnlyRecord: Person is marked as readonly
Upvotes: 2