Autoload Paths and Nested Services Classes Crash in Ruby

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



Leave a reply



Submit