Rails 6: How to load global variables only once? - ruby-on-rails

My app uses global variables like logos, app name, etc retrieved from the database and shown on different controllers and views. I put it in ApplicationController to be available to all, but I find that the individual controllers repeat the same query sometimes.
class ApplicationController < ActionController::Base
$image = Setting.find_by_name('image').value
$city = Setting.find_by_name('city').value
$currency = Setting.find_by_name('currency').value
end
Is there a way to make the same variables available to all controllers (and users) with just a one-time query with the variables saved on memory, such as when the app starts up?

You could attempt to use initializers.
Rail will load all files in config/initializers/ folder when the server starts up. This can work as a place to initialize application-wide variables. We could create a file inventory.rb file in the initializers directory:
at config/initializers/inventory.rb
module Inventory
class Count
Orders = Order.all
end
end
Inventory::Count::Orders
# => "[your orders will show here]"
These will only be loaded once when the server is started or restarted. As such this works well if the values you need won't change. If they will change I don't think there is a good way to avoid running multiple queries.

Whats about caching? Rails is using SQL Caching and you can use Low Level Caching. See the guides (1.7 and 1.8): https://guides.rubyonrails.org/caching_with_rails.html#low-level-caching

Related

##classVariable in ruby on rails turns uninitialized on nginx - ec2

I have a class variable that I initialize, that later magically becomes uninitialized by itself... When developing locally on rails' default server (WEBrick), there is no problem. This only happens on nginx on ec2. Here's some code..
def TestController < ApplicationController
  ##classVariable = ""
  def index
    ##classVariable = "What's up homie"
    log(##classVariable)
  end
  def callThisMethodViaAJAXFromJavascript
    log("reached this method")
    log(##classVariable)
  end
  def log(str)
    File.write("aValidPath", str)
  end
end
Here's what I do: When I load the page on test/index, the index method executes, and properly logs ##classVariable as:
"What's up homie"
But when I call the method callThisMethodViaAJAXFromJavascript via AJAX from the frontend, my log file looks like:
"reached this method"
""
Again, this ONLY occurs on nginx on, ec2 (OS is ubuntu). When I run locally on WEBrick, this NEVER happens.
Any ideas? Thank you very much.
You don't say how you're using nginx (as a reverse proxy to some unicorn instances, with passenger etc) but either way you would typically have multiple instances of your application. Each one is a separate process so setting a class variable in one process has no effect on the other process.
Nginx will balance requests between the rails instances - so the index page is served by one instance and the ajax action will frequently be served by another process where the clas variable is still the empty string.
In development with webrick there is only one rails instance so you don't encounter this problem. I'm not sure what you were trying to do but class variables are not a good way to preserve state across requests
Per #Frederick's answer, I used the rails session instead to store the variable.
As a rule of thumb, it seems like you should NOT use global variables nor class variables in rails if your variables are changing.
The values stored in the session object are the same from any process in rails. Though the pointer to the session may be different, the values will remain the same. Here's another post about sessions that goes more into detail:
How is rails session shared among unicorn workers?

Where is a good place to initialize an API?

I wanted to use this api: https://github.com/coinbase/coinbase-ruby and the first step is to initialize the API, like this:
coinbase = Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
I was wondering what the best place to put this code is, and how would I access it if I put it "there"? I want this variable (coinbase) to be accessible ANYWHERE in the application.
Thanks!
The answer to this question really depends on your use case and your approach. My geral recommendation, however, is to create a Service Object (in the DDD sense) (see the section named "Domain Objects Should Not Know Anything About Infrastructure Underneath" in that link), that handles all communication with the Coinbase API. And then, within this service object, you can simply initialize the Coinbase::Client object once for however many times you call into it. Here's an example:
# app/services/coinbase_service.rb
class CoinbaseService
cattr_reader :coinbase_client, instance_accessor: false do
Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
end
def self.do_something
coinbase_client.do_something_in_their_api
end
def self.do_something_else
coinbase_client.do_something_else_in_their_api
end
end
So then you might do, e.g.:
# From MyController#action_1
if CoinbaseService.do_something
# ...
else
# ...
end
Or:
# From MyModel
def do_something
CoinbaseService.do_something_else
end
To get the service object working, you may need to add app/services to your autoload paths in application.rb file. I normally just add this:
# config/application.rb
config.autoload_paths += %W(#{config.root}/app)
I find this Service Object approach to be very beneficial organizationally, more efficient (only 1 invocation of the new Coinbase client needed), easier to test (easy to mock-out calls to Coinbase::Client), and simply joyful :).
One way to go about having a global variable can be done as similar as initializing redis in a Rails application by creating an initializer in config/initializers/coinbase.rb with:
$coinbase = Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
Now, you can access $coinbase anywhere in the application!
In the file config/initializers/coinbase.rb
Rails.application.config.after_initialize do
CoinbaseClient = Coinbase::Client.new(
Rails.application.credentials.coinbase[:api_key],
Rails.application.credentials.coinbase[:api_secret])
end
In place of the encrypted credentials, you could also use environment variables: ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET']
The above will make the constant CoinbaseClient available everywhere in your app. It will also ensure all your gems are loaded before the client is initialized.
Note: I am using Rails 6.1.4.4, and Ruby 2.7.5

Where can I store site-wide variables in Rails 4?

I am new to Rails and come from a ColdFusion background, where we would store global / site-wide variables in the 'application' scope. This persists the variable across any view or controller. Does Rails 4 have an equivalent functionality for this type of thing?
The site-wide variable won't typically change often so it doesn't need protecting in any way.
For example, in my situation, I want to store the website's domain name. One for testing and one for live environments. Localhost for development and xxxxxx.com for production.
Any tips or pointers would help. I have Googled this extensively and solutions seem to be far too complicated to achieve what seems to be such a trivial task. What's the best elegant solution for Rails 4?
The simplest, basic and default way is to use the Rails.application.config store.
Rails.application.config.my_config = 'foo'
You can assign a config in your environment:
# application.rb
module MyApp
class Application < Rails::Application
config.my_config = 'foo'
end
end
and read it with
Rails.application.config.my_config
# => 'foo'
This approach works well for very simple applications, but if you want something more advanced there are several gems available.
I'm currently using SimpleConfig. The main advantages are:
per-environment configuration. You can configure default configurations for the application, then override defaults with environment specific configurations
local.rb file for custom overrides
capistrano-like configuration style
it works nicely with the dotenv gem, very useful to avoid storing sensitive credentials in your repo.
This sounds like a perfect example for configuration values stored in config/environments/production.rb and config/environments/development.rb. Just store any value there:
config.my_special_value = 'val'
And access it in your application like this:
Rails.application.config.my_special_value
Always the value of your environment is active.
If you just want to have a „global“ value, store it in your application controller. All your view controllers are derived from your app controller, so you can save any value there as an instance or class variable:
class ApplicationController < ActionController::Base
MY_CONSTANT_VALUE = "foo"
end
class MyViewController < ApplicationController
def index
raise MY_CONSTANT_VALUE.inspect
end
end
You also could implement an helper:
# app/helpers/application_helper.rb
module ApplicationHelper
FOO = "bar"
end
# app/controllers/foo_controller.rb
class FooController < ApplicationController
def index
raise FOO
end
end
I can recommend good method to store variable. I use this on production
Passwords can be stored easier to .env file
like this
#Root dir create file ".env"
PASSWORD=123456
and load password
#Somewhere in app
ENV['PASSWORD'] #=> 123456
it works I hope will help you
You can use gem figaro
write your variables in config/application.yml
HELLO: world
development:
HELLO: developers
production:
HELLO: users
Then you can fetch
ENV["HELLO"]
In rails there is gem named as
gem 'dotenv-rails'
By using it we can assign the variables to system level and used in application.
By using simple steps
First create a simple filed in system level at any place with named extension .env
//in application.rb
require 'dotenv'
Dotenv.load('path-of-your-file.env')
And restart your application
Source
Please got the link for the desscription of dot env gem

Is there a safe spot to store dynamically created Rails models?

I'm using the following Rails 3 model to dynamically create models:
# encoding: UTF-8
require_dependency 'read_only'
require_dependency 'readings_methods'
class Reading < ActiveRecord::Base
self.abstract_class = true
include ReadOnly
include ReadingsMethods
##mutex = Mutex.new
##children = {}
def self.with_table(t)
##mutex.synchronize do
child = ##children[t]
if child.nil?
child = Class.new(self) do
self.table_name = t
end
##children[t] = child
end
child
end
end
end
Debugging shows, however, that ##children is reset on every page load, thus making every page load very, very slowly. How do I prevent this? And what is causing it? Unless I memoize children, Rails goes into infinite loop, which leads me to believe that it reloads at least some classes whenever I create a new model, or something like that. How do I prevent this behavior?
Rails reloads code for you in development mode so that you don't have to restart the rails server every time you make a change (in rails 3.2 it tries to only reload the code that has changed).
When a class is reloaded, rails unsets the constant and loads a fresh copy, so in particular the new one will have a fresh set of class variables.
ActiveSupport::Dependencies.autoload_once_paths and ActiveSupport::Dependencies.autoload_paths control what is reloaded in this manner. You should be able to add the (full) path to your file to autoload_once_paths to prevent that one file from being reloaded (but you will then have to restart rails so see any changes made to it )

How can I keep my initializer configuration from being lost in development mode?

I'm working on a Rails app that uses an engine. I'm using an initializer to configure one of my engine's controllers so that it will trigger an action in the host app. The code looks something like this:
# config/initializers/my_engine.rb
MyEngine::SomeController.after_filter proc {
# Do something in the host app
}, :only => :update
This works fine in production, but in development mode, the proc is only called on the first request. This is because the classes are getting reloaded and this configuration is lost, because it was stored in a class variable. (For example, MyEngine::SomeController is reloaded from the file it's in, and since the after_filter isn't declared there, it isn't added back on.)
Some Rails background
In development mode, Rails uses the following load strategy:
Code in the app directory is reloaded on each request, on the assumption that you're actively changing it.
Code in the lib directory, along with config/initializer files, are loaded once, when the application boots.
Initializer files are generally used for configuring gems. In the past, gems have mostly had code in the lib directory, so running their configuration once was sufficient.
How engines change things
However, Rails engines have code in the app directory: controllers, models, etc. These files are reloaded in development mode on each request. Therefore, configuration like my example above is lost.
Enter to_prepare
Rails provides config.to_prepare specifically to solve this problem: it run once in production, and on every request in development.
For example, we have this in application.rb, which works fine:
config.to_prepare do
# set up class variables (after_filters, etc)
end
However, if I have to put all my engines' configuration in application.rb, this defeats the point of config/initializers in keeping things organized.
So, for any configuration of classes in my engines' app directories, I want to put that code in files under config/initializers.
Here are my questions.
I'm unclear how to get config into scope in an initializer file. I'm thinking it would be Rails.application.config. Is that right?
Can I add add multiple to_prepare blocks? I'm afraid that calling it multiple times will overwrite previous blocks.
Update
As #Frederick Cheung mentioned, Rails.application.config.to_prepare does work in config/initializer files, and one can use as many of these as needed in the various files; each call appends its block to an array, so nothing is overwritten.
So the solution to this problem is:
# config/initializers/my_engine.rb
Rails.application.config.to_prepare do
MyEngine::SomeController.after_filter proc {
# Do something in the host app
}, :only => :update
end
One thing that still seems odd: I expected the to_prepare block to be called on every request in development mode, but instead it seems to be called randomly every 3rd request or so. I added block:
Rails.application.config.to_prepare do
Rails.logger.info "Running the prepare block!"
end
... restarted my app, and refreshed the page nine times. I only saw the message on the 1st, 5th, 7th and 9th requests. I'm not sure what explains this behavior, but it does explain why my code without the to_prepare worked intermittently in development.
You can add as many to_prepare blocks as you want - when you do config.to_prepare, Rails is doing (in configuration.rb in railties)
def to_prepare(&blk)
to_prepare_blocks << blk if blk
end
and then iterates over those blocks handing them over to ActionDispatch::Reloader, where to_prepare is implemented using ActiveSupport::Callbacks (i.e. the same thing that is used for before_save and so on). Multiple to_prepare blocks are fine.
Currently it looks like Rails iterates over to_prepare_blocks after reading application initialisers so adding to Rails.application.configuration.to_prepare should work. You may prefer to use ActionDispatch::Reloader.to_prepare.
There's nothing to stop you from doing initializer code in a file that lives in app/models.
for example
class MyClass
def self.run_me_when_the_class_is_loaded
end
end
MyClass.run_me_when_the_class_is_loaded
MyClass.run_me... will run when the class is loaded .... which is what we want, right?
Not sure if its the Rails way.... but its extremely straightforward, and does not depend on the shifting winds of Rails.

Resources