Forum: Ruby on Rails Opinion on a particular use of Initializers

Posted by Alex Braha Stoll (Guest)
on 2012-11-21 23:35
(Received via mailing list)
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.
Posted by Frederick Cheung (Guest)
on 2012-11-21 23:42
(Received via mailing list)
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
Posted by Alex Braha Stoll (Guest)
on 2012-11-22 03:59
(Received via mailing list)
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:
Posted by Jordon Bedwell (Guest)
on 2012-11-22 04:07
(Received via mailing list)
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.
Posted by Frederick Cheung (Guest)
on 2012-11-22 15:23
(Received via mailing list)
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,
Posted by Alex Braha Stoll (Guest)
on 2012-11-22 17:06
(Received via mailing list)
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
No account? Register here.