How to change Rails 3.0's default log path? - ruby-on-rails

I have to change my rail application's default log path because of my company's internal software deployment process: basically my rails app ends up on a read-only location, and I need the log files written in a directory "made for this".
With Rails 2.x we used to add some black magic in our FCGI script to force that in when deployed on prod. hosts:
class Rails::Configuration
def default_log_path
File.join(ENV['SOME_ENVIRONMENT_VAR'], "var/output/logs/rails.log")
end
end
However, Configuration isn't a class anymore in Rails 2.3 (it's a module), and it appears to me there isn't any default_log_path involved there anymore as well...

You just need define your logger
config.logger = ActiveSupport::BufferedLogger.new(File.join(ENV['SOME_ENVIRONMENT_VAR'], "var/output/logs/rails.log"))
This trick works with Rails 2 too. And you can define by environment where you really want your log file.

The config.log_path setting has been deprecated - the recommended solution is now:
config.paths.log = "/some/path/#{Rails.env}.log"

As of Rails 3.2.3, looks like the log pathname is also defined in Rails::Rack::LogTailer#initialize, and that comes from Rails::Server#log_path.
LOG_PATH = "log/mylog.log"
require 'rails/commands/server'
module Rails
class Server
def log_path
LOG_PATH
end
end
end
class Application < Rails::Application
...
config.paths['log'] = LOG_PATH
...
end

Related

Rails 6: Zeitwerk::NameError doesn't load class from module

I have a file which looks like this
#app/services/account/authenticate/base.rb
module Account
module Authenticate
AuthenticateError = Class.new(StandardError)
class Base < ::Account::Base
def self.call(*attrs)
raise NotImplementedError
end
end
end
end
Now when I will run the code from rails c I have an error
> ::Account::Authenticate::AuthenticateError
=> NameError (uninitialized constant Account::Authenticate::AuthenticateError)
> ::Account::Authenticate.constants
=> [:Base, :ViaToken]
So rails doesn't see AuthenticateError class. But when I will create a nested class from this folder like
=> Account::Authenticate::ViaToken
> ::Account::Authenticate.constants
=> [:Base, :AuthenticateError, :ViaToken]
AuthenticateError class is now visible
> ::Account::Authenticate::AuthenticateError
=> Account::Authenticate::AuthenticateError
The solution for this problem is to create a separate file authenticate_error.rb which will work from the beginning but this solution is not ideal for me. Is there any solution to preload all classes or smth?
(Ruby 2.6 with Rails 6.0.0.rc2)
I experienced this same issue when deploying a Rails 6.0.2 application to an Ubuntu 18.04 server.
Unable to load application: Zeitwerk::NameError: expected file /home/deploy/myapp/app/models/concerns/designation.rb to define constant Designation, but didn't
I found out that the issue was with zeitwerk. Zeitwerk is the new code loader engine used in Rails 6. It’s meant to be the new default for all Rails 6+ projects replacing the old classic engine. Zeitwerk provides the features of code autoloading, eager loading, and reloading.
Here's how I solved it:
Navigate to the config/application.rb file on your project.
Add this line within your application module to switch to classic mode for autoloading:
config.autoloader = :classic
Here's an example:
module MyApp
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
config.autoloader = :classic
end
end
You can read up more on zeitwerk in this article: Understanding Zeitwerk in Rails 6
updating a Rails app from 5.2 to 6.0 and also hit the Zeitwerk bump!
If you want to continue to use the autoloading mode you're currently using, avoiding Zeitwerk, then add this line to your application.rb file (#PromisePreston answer and Rails doc)
config.autoloader = :classic
If you want to upgrade to Zeitwerk then a command to use is bin/rails zeitwerk:check (From this guide article).
Scenario we hit closest to this particular question was where we had a file within a subfolder like this:
#presenters/submission_files/base.rb
module Presenters
module SubmissionFiles
class Base < Showtime::Presenter
def method_call
#code_here
end
end
end
end
Removing an extra modules to have:
#presenters/submission_files/base.rb
module Presenters
class SubmissionFiles::Base < Showtime::Presenter
def method_call
#code_here
end
end
end
Then, when calling the method in other ruby files in the app use: Presenters::SubmissionFiles::Base.method_call
zeitwerk follow conventional file structure which is able to load your project's classes and modules on demand (autoloading) as long as you follow it's rule.
# service/account/authenticate/base.rb
module Account
module Authenticate
puts "load service ....."
AuthenticateError = Class.new(StandardError)
class Base
end
end
end
::Account::Authenticate::AuthenticateError # uninitialized constant
::Account::Authenticate::Base # load service ....
::Account::Authenticate::AuthenticateError # OK
as you can see, the first time you attempt to reach the constant AuthenticateError, the log load service ... does not show, that because you don't play zeitwerk rule:
whenever it gets a request to load the const ::Account::Authenticate::AuthenticateError, first it'll check and return if that constant already loaded, otherwise it'll look for the file /account/authenticate/authenticate_error.rb which corresponding to the constant ::Account::Authenticate::AuthenticateError to find that constant definition, but it could not find it.
on step 2, when you call ::Account::Authenticate::Base, it could find the file /account/authenticate/base.rb and load it, during this time, it's also load the constant AuthenticateError which is defined on that file, now we have the constant ::Account::Authenticate::AuthenticateError, and of course, it's OK on step 3.
now let try to play with the zeitwerk rule, i create a file /account/authenticate/authenticate_error.rb as below
# service/account/authenticate/authenticate_error.rb
module Account
module Authenticate
puts "load error ....."
AuthenticateError = Class.new(StandardError)
end
end
and try to attempt that constant at the step 1
$ spring stop
$ rails c
> ::Account::Authenticate::AuthenticateError
load error .....
=> Account::Authenticate::AuthenticateError
it worked since zeitwerk found the file account/authenticate/authenticate_error.rb. (note that the file name /____authenticate_error.rb still work)
my thought: i think you could work safely with the constant AuthenticateError inside the module ::Account::Authenticate, in case of you want to expose those error constants to outside, you could create file /account/authenticate/error.rb
# service/account/authenticate/error.rb
module Account
module Authenticate
module Error
AuthenticateError = Class.new(StandardError)
end
end
end
then you could access ::Account::Authenticate::Error::AuthenticateError, in my opinion, it even clearer than put AuthenticateError inside base.rb.
I was able to fix it by not trying to fight Rails 6. Zeitwerk autoloads certain expected folders, which includes app/models, app/controllers, app/helpers and others.
I created a folder app/helpers and moved my services folder into it.
That's it!

How can I add a services directory to the load path in Rails?

In my Rails project, I want to add services directory in app folder and include some service objects.
So let's say I want to add app/services/foo/test.rb which looks like:
module Services
module Foo
class Test
end
end
end
In my config/application.rb I added:
config.paths.add File.join('app', 'services'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'services', '*')]
However when I try to load the files in console it doesn't work:
⇒ rails c
Loading development environment (Rails 4.1.4)
[1] pry(main)> Services::Foo::Test
NameError: uninitialized constant Services
Any help how can I solve this issue?
After add new dir, reload spring
spring stop
First of all, the code under app folder will be loaded without any config.
I think the problem was the folder structure doesn't match with your class definition.
So this config will work:
app/services/foo/test.rb
module Foo
class Test
end
end
My clue is, for example we have app/controllers/api/v1/users_controllers.rb and the class constant will be Api::V1::UsersController, not Controllers::Api::V1::UsersController
Update
Conventionally, we usually use FooServices instead of Foo, it is clearer, for example:
app/services/foo_services/bar_parser.rb
module FooServices
class BarParser
# Do stuff
end
end
So we understand that every class inside foo_services folder is a service which related to Foo
My problem was because of Rails naming conventions, I suppose. I just renamed class to not use module Services and it worked.

Ruby Monkey Patched Logger does not work in Rails Console

I have some helper methods on my ActiveSupport::BufferedLogger such as displaying KeyValue pairs, it all works fine from rails s but fails in rails c
In rails s, I can see that my Logger extends BufferedLogger and that is the class I have chosen to MonkeyPatch, I have also tested this out with ActiveSupport::Logger as well.
I had thought that rails c ran through the same initializers that rails s used, am I wrong in thinking that?
Do I need to run some sort of initializer when starting rails c?
Location of my file is:
config/initializers/active_support_logger.rb
Error is here:
Sample Snippet listed here
class ActiveSupport::BufferedLogger
def kv(key, value)
info('%-50s %s' % ["#{key}: ".brown, value])
end
def line
info(##l.brown)
end
def block(message)
line
info(message)
line
end
end
I recommend subclassing ActiveSupport::Logger (or ActiveSupport::BufferedLogger if you really want though it's deprecated in Rails 4) instead of monkey-patching. Rails provides a configuration option to override the logger instance used by the app. This works in any context that loads the Rails environment, including the server and console.
There are a number of ways you can do this; a quick and dirty way to get you started is to just define the class and set the logger instance in an initializer that will get loaded during the initialization process of the Rails environment:
# config/initializers/my_logger.rb
class MyLogger < ::ActiveSupport::Logger
# additional methods and overrides
end
Rails.logger = MyLogger.new(Rails.root.join("log", "#{Rails.env}.log"))
For more info, check out the Rails guide to debugging applications: http://guides.rubyonrails.org/debugging_rails_applications.html#the-logger

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

Rails 3 engine and code reloading in development mode

I have a rails 3 engine. In initializer it requires a bunch of files from some folder.
In this file user of my engine defines code, business logic, configures engine, etc..
All this data is stored statically in my engine main module (in application attribute)
module MyEngine
class << self
def application
#application ||= MyEngine::Application.new
end
end
end
I want this files to be reloaded on each request in development mode.
(So that the user don't have to reload server to see changes he just made)
Of course I can do something like this instead of initializer
config.to_prepare do
MyEngine.application.clear!
load('some/file')
end
But this way i will have issues (because constants defined in this file won't really be reloaded).
The ideal solution would be to make my whole engine reloadable on each request, but a have not found the way to do it.
It's an old question but I think adding ActiveSupport::Dependencies.explicitly_unloadable_constants += %w[ GemName ] to your development.rb should do the trick.
Have you tried turning reload_plugins on?
# environments/development.rb
config.reload_plugins = true
Its a bit of hack but using require_dependency and just reopening the class might work?
# app/models/project.rb
require_dependency File.join(MyEngine::Engine.root, 'app', 'models', 'project')
class Project
end
For those who are working on Engine views or I18n translations only: Those parts are autoreloaded by default, no need to restart the server!

Resources