Autoload paths and nested services classes crash in Ruby
Working solution
After deeper investigation and attempts, I realised that I had to eager_load
the services to avoid getting wrong constants when calling meta functionalities such as const_get('MyClassWithModelName')
.
But here's is the thing : the classic eager_load_paths
won't work because for some reason those classes will apparently be loaded before the entire core of Rails is initialized, and simple class names such as Base
will actually be mixed up with the core, therefore make everything crash.
Some could say "then rename Base into something else" but should I change a class name wrapped into a namespace because Rails tell me to ? I don't think so. Class names should be kept simple, and what I do inside a custom namespace is no concern of Rails.
I had to think it through and write down my own hook of Rails configuration. We load the core and all its functionalities and then service/
recursively.
On a side note, it won't add any weight to the production environment, and it's very convenient for development.
Code to add
Place this in config/environment/development.rb
and all other environment you want to eager load without Rails class conflicts (such as test.rb
in my case)
# we eager load all services and subdirectories after Rails itself has been initializer
# why not use `eager_load_paths` or `autoload_paths` ? it makes conflict with the Rails core classes
# here we do eager them the same way but afterwards so it never crashes or has conflicts.
# see `initializers/after_eager_load_paths.rb` for more details
config.after_eager_load_paths = Dir[Rails.root.join('app', 'services', '**/')]
Then create a new file initializers/after_eager_load_paths.rb
containing this
# this is a customized eager load system
# after Rails has been initialized and if the `after_eager_load_paths` contains something
# we will go through the directories recursively and eager load all ruby files
# this is to avoid constant mismatch on startup with `autoload_paths` or `eager_load_paths`
# it also prevent any autoload failure dû to deep recursive folders with subclasses
# which have similar name to top level constants.
Rails.application.configure do
if config.respond_to?(:after_eager_load_paths) && config.after_eager_load_paths.instance_of?(Array)
config.after_initialize do
config.after_eager_load_paths.each do |path|
Dir["#{path}/*.rb"].each { |file| require file }
end
end
end
end
Works like a charm. You can also change require
by load
if you need it.
Nested Helpers Issue
module Permissions
module ChartsHelper
# ...
end
end
Define (and reopen) namespaced classes and modules using explicit
nesting. Using the scope resolution operator can lead to surprising
constant lookups due to Ruby’s lexical scoping, which depends on the
module nesting at the point of definition.
- the Ruby Style Guide
Using ::
is not a shorthand for explicit nesting and it requires that Permissions be loaded first to avoid a missing constant error.
module Permissions::ChartsHelper
end
# NameError (uninitialized constant Permissions)
It also sets the wrong module nesting:
FOO = "I'm in the global scope"
module Permissions
FOO = "I'm Permissions::FOO"
end
module Permissions::ChartsHelper
puts FOO # I'm in the global scope
puts Module.nesting.inspect # [Permissions::ChartsHelper]
end
In Ruby only the module
and class
keywords change the current module nesting.
By contrast if you explicitly nest the modules:
FOO = "I'm in the global scope"
module Permissions
FOO = "I'm Permissions::FOO"
end
module Permissions
module ChartsHelper
puts FOO # I'm Permissions::FOO
puts Module.nesting.inspect # [Permissions::ChartsHelper, Permissions]
end
end
RoR autoload sub directories
Rails tries to guess namespaces by dirnames & filenames. So, to have a TransactionService::AuthorizeRequest class name, you should have the exact following structure:
app
- services
-- transaction_service
--- authorize_request.rb
Your class sould looks like :
module TransactionService
class AuthorizeRequest
end
end
remove special characters without removing space in between words
"String".squeeze(' ').gsub(/, /, ',').gsub(/[^0-9A-Za-z,\r\n ]/i, '').strip
gsub!(/[^0-9A-Za-z ]/, '') will remove all special character without start end spaces.
strip will remove start and end space from string.
Nested include with PHP
assuming that the filesystem looks like this..
/www
include/header.php
class/Login.class.php
class/Connection.class.php
resources/login/index_alt.php
resources/login/index_auth.php
index.php
this means that
index.php: include(__DIR__ . '/include/header.php');
header.php: include(__DIR__ . '/../resources/login/index_alt.php');
index_alt.php: include(__DIR__ . '/index_auth.php');
etc; see http://php.net/manual/en/language.constants.predefined.php
Related Topics
Store Image in Database Using Rails Paperclip Plugin
Group Users by Age Range in Ruby
Array#Rotate Equivalent in Ruby 1.8.7
Sending Array of Values to a SQL Query in Ruby
Callback for Active Storage File Upload
Why Won't Ruby Allow Me to Specify Self as a Receiver Inside a Private Method
Installing Ncurses for Ruby on Windows
Instance_Eval's Block Argument(S)- Documented? Purpose
Ruby Installation (2.2.2) Fails in MACos Big Sur
Ruby: Overriding the Puts Method
If 'Self' Is Always the Implied Receiver in Ruby, Why Doesn't 'Self.Puts' Work
How to Compare Private Attributes in Ruby
Single Quote String String Interpolation
Ruby on Rails Global Activerecord::Enum
Ruby How to Merge Two CSV Files with Slightly Different Headers
Are There More Elegant Ways to Prevent Negative Numbers in Ruby