Hi!
In the Rails app I'm developing I have some data stored in the database
that acts like constants. For example, I populate a profiles table with
all
the profile types using seeds.rb. To avoid hardcoding the profile ids in
my
code, everytime I needed to do an operation involving a profile type I
first retrieved the profile type from the database:
<code>
admin = Profile.find_by_name('admin')
@all_admins = admin.people
</code>
Since the profile ids (and other 'constants' stored in the database) can
only change if the sys admin changes their values before the deploy
(editing seeds.rb), I though of using initializers to retrieve
references
to all those database constants (therefore opening less connections with
the database during application usage):
in .../initializers/
<code>
class DatabaseConstants < ActiveRecord::Base
# loading the profiles
temp_profiles = {}
Profile.all.each do |profile|
case profile.name
when 'foo'
temp_profiles[:foo] = profile
when 'bar'
temp_profiles[:bar] = profile
when 'admin'
temp_profiles[:admin] = profile
when 'super-admin'
temp_profiles[:super_admin] = profile
end
end
PROFILES = temp_profiles
# other 'constants' to be loaded
# ...
end
</code>
and then, I can do something like the following in a controller:
<code>
@all_admins = DatabaseConstants::PROFILES[:admin].people
</code>
I have tested this solution and it works perfectly fine. However, I
would
like to know from veteran Rails developers if this is a good (or
acceptable) use of initializers (and if this should really increase
performance).
Cheers,
Alex.
on 2012-11-21 23:35
on 2012-11-21 23:42
On Wednesday, November 21, 2012 5:57:15 PM UTC, Alex Braha Stoll wrote: > > > Since the profile ids (and other 'constants' stored in the database) can > only change if the sys admin changes their values before the deploy > (editing seeds.rb), I though of using initializers to retrieve references > to all those database constants (therefore opening less connections with > the database during application usage): > > I don't think this will affect the number of connections to the database, although you will of course save some queries > > I have tested this solution and it works perfectly fine. However, I would > like to know from veteran Rails developers if this is a good (or > acceptable) use of initializers (and if this should really increase > performance). > > Personally if this was worthwhile for my app I would load the profiles lazily rather than in an initializer. One reason is that initializers get run in a lot of cases where you probably don't need those rows cached, eg rake routes. Occasionally this sort of stuff can bite you too - depending on how you deploy the app, the profiles table might not exist (fresh deploy). So you run rake db:schema:load to set things up, but that would also load you initializer and would therefore blow up when that initializer tried to access the profiles table. Fred
on 2012-11-22 03:59
Thanks for the answer, Fred. When you say lazily loading the data I need you mean waiting for a first request that uses the data, right? If so, how can I do that in a way that the data will persist for the next requests (from the same and from other users)? Just storing the data into a class that checks if the constants were already fetch doesn't guarantee the persistence of the data between requests, right? Can you point me a resource where I can learn more about a solution for my scenario? Cheers, Alex. Em quarta-feira, 21 de novembro de 2012 20h41min25s UTC-2, Frederick Cheung escreveu:
on 2012-11-22 04:07
On Wed, Nov 21, 2012 at 8:57 PM, Alex Braha Stoll <alexbrahastoll@gmail.com> wrote: > Thanks for the answer, Fred. > > When you say lazily loading the data I need you mean waiting for a first > request that uses the data, right? If so, how can I do that in a way that > the data will persist for the next requests (from the same and from other > users)? Just storing the data into a class that checks if the constants were > already fetch doesn't guarantee the persistence of the data between > requests, right? Can you point me a resource where I can learn more about a > solution for my scenario? Most objects should persist between requests so if you were to create a struct and load all your data onto that struct the object would be persisted between requests. You could just create a Persist object with an initializer and and there is your persistent struct.
on 2012-11-22 15:23
On Thursday, November 22, 2012 2:57:46 AM UTC, Alex Braha Stoll wrote: > > I mean something like class Profile < AR::Base def self.cached @cached ||= begin #build up the hash of profiles end end end Then do Profile.cached[:foo] when you need it Fred Cheers,
on 2012-11-22 17:06
Thank you both Fred and Jordon for the help.
I followed Fred's code example and ended with the following solution:
class Profile < ActiveRecord::Base
has_many :person_profiles
has_many :people, through: :person_profiles
def self.cached
@@profiles ||= fetch_data
end
def self.fetch_data
temp_profiles = {}
Profile.all.each do |profile|
case profile.name
when 'admin'
temp_profiles[:admin] = profile
when 'super-admin'
temp_profiles[:super_admin] = profile
# ... other profiles
end
end
temp_profiles
end
end
In the controller:
@admins = Profile.cached[:admin].people
Doing some tests, I noticed that starting on the second request there is
a
significative difference in ActiveRecords total processing time.
However,
if all the users log out, it seems that the garbage collector sweeps the
data cached in the class variable. When I have time, I will further
investigate this in order to find a way to persist that data for all the
time the application is running.
Cheers,
Alex.
Em quinta-feira, 22 de novembro de 2012 12h22min34s UTC-2, Frederick
Cheung
escreveu:
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.