How do I include a 'lib/' class or module in my models, Grape API and tests? For example, I have a class:
ROOT/lib/links/link.rb
module Links
class Link
...
end
end
And I want to include that class in my User model (app/models/user.rb), User Grape API (app/api/v1/users.rb), and testing suites (test/models/user_test.rb and test/api/v1/users/users_links_test.rb)
For example, I tried accessing it in my tests through
link = Links::Link.new(LINK_NAME, LINK_SITE)
but I get:
uninitialized constant API::V1::Users::APITest::Links
I've tried adding this to my config/application.rb:
config.autoload_paths += Dir["#{Rails.root}/lib/**"]
require_relative 'boot'
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
# require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
require "sprockets/railtie"
require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module ArbitraryAppName
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Auto-load API and its subdirectories
config.paths.add 'app/api', glob: '**/*.rb'
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
config.autoload_paths << "#{Rails.root}/lib"
# For Cross-Origin Resource Sharing (rack-cors)
config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :options]
end
end
end
end
but it doesn't work. What am I missing? How do I include lib files and how should I be calling those classes?
EDIT:
It seems like the only way I can access it is if I do:
require "#{Rails.root}/lib/links/link"
Is there a better, more conventional way?
There seems to be another issue, adding config.autoload_paths << whatever does not seem to be doing anything. For example, when I puts ActiveSupport::Dependencies.autoload_paths in rails console, my changes do not appear.
No, you shouldn't resort to using require (if you want to follow Rails conventions). Rails autoloading is based on paths and namespacing, as those two things have to match up. Besides the initial problem with namespacing that was fixed, the way you modify the autoload paths is incorrect. It should be done this way:
config.autoload_paths << "#{Rails.root}/lib"
This is if you want to leave lib/ at the root of your project. As mentioned in the comments, you could move it into your app/ directory.
Paul Hoffer's answer is the accepted answer but for anyone who notices that any changes to config/application.rb isn't persisting:
There seems to have been an issue with Spring, where any changes to the config/application.rb file wasn't persisting. I had to run spring stop to get things to work again. If anyone can explain why this happens sometimes, that'd be nice.
You should not put your lib into autoload_paths (as Paul Hoffer suggested), since doing so will prevent it being eager loaded in production (when config.eager_load = true).
You will find extensive guide on this topic here: http://hakunin.com/rails3-load-paths
Related
I have a class in libs and try to use it in a controller. However I cannot access it. I tried to use the autoload function, but that doesn't work, and it shouldn't work in rails 5 in production mode, so I guess I don't need to try this one.. I also tried to require it in my controller, but I don't get the syntax correct I guess. I'm also wondering where to put my class, since I have read several different opinions..
config/application.rb
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Qipmatedevel
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.1
config.active_job.queue_adapter = :sidekiq
config.autoload_paths += %W(#{config.root}/lib)
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
end
end
app/controller/imports_controller
class ImportsController < ApplicationController
require 'lib/class_qip'
adding
require './lib/class_qip.rb'
fixed it
Put your classes in lib directory only & load as,
config.eager_load_paths << Rails.root.join('lib')
It works on both development and production environment.
I decided to do a bit of refactoring on my code since one of my project starts to get really big. I thus decided to namespace some models in quite independent modules, for clarity.
However, my controllers have an extra outer module since I have different subdomains or different kinds of controllers for all those models.
Therefore, I have a model A::MyModel in app/models/a/my_model.rb
and controllers X::A::MyModelsController and Y::A::MyModelsController in app/controllers/x/a/my_models_controller.rb and `app/controllers/y/a/my_models_controller.rb.
I happen to get, on some endpoints only, an exception LoadError: Unable to autoload constant MyModel, expected [...]/app/models/a/my_model.rb to define it. Exception is pretty clear except that it should be looking for A::MyModel.
It only happens on some of the endpoints, I even managed to make it work for an endpoint with AJAX, but not in HTML. However, it works properly with Rspecs as none of my tests fail.
I suspect this has something to do with Autoload as when it fails, it does not even get into the controller: There is no stack and no Processed By xxx in the logs.
I am using Ruby 2.4.2, Rails 5.2.0, and here is my application.rb
require_relative "boot"
require "rails"
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
require "sprockets/railtie"
require "rails/test_unit/railtie"
Bundler.require(*Rails.groups)
module MonApp; end
class MonApp::Application < Rails::Application
config.action_view.embed_authenticity_token_in_remote_forms = true
config.generators do |generate|
generate.assets false
end
config.active_job.queue_adapter = :sidekiq
config.autoload_paths += Dir[Rails.root.join("app", "models", "**", "*")]
config.middleware.use I18n::JS::Middleware
config.exceptions_app = self.routes
end
Surprisingly, it seems to fail when using Ajax with a contentType: "application/json". Same endpoints without ajax or without this contentType seem to work.
My controller is
class X::A::MyModelsController
def create; end
end
Has anyone some insights on this?
Thanks!
I'm trying to use a module from the folder 'app/helpers/transaction_helper.rb'. (It's important to note that the application is using the api_only flag, and helpers aren't being generated or loaded by default.)
module TransactionHelper
module Generator
def self.code_alphanumeric(params)
end
end
end
I got this error:
NameError: uninitialized constant TransactionHelper
I tried to add the following line to the application.rb
config.autoload_paths += %W(#{config.root}/app/helpers)
require_relative 'boot'
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
# require "sprockets/railtie"
require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module SistemaControleContasApi
class Application < Rails::Application
config.autoload_paths += %W(#{config.root}/app/helpers)
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true
end
end
But this until not work. If I try to put this file in the app/models directory, this work. However, this is not the right local to place a helper.
Could someone help, please?
Normally everything under app is autoloaded by default if you follow the Rails™ naming conventions. See here Docs.
set_autoload_paths: This initializer runs before bootstrap_hook. Adds
all sub-directories of app and paths specified by
config.autoload_paths, config.eager_load_paths and
config.autoload_once_paths to
ActiveSupport::Dependencies.autoload_paths.
So in the first step you should remove config.autoload_paths += %W(#{config.root}/app/helpers) from your config.
Then for every module you should create a subfolder.
So for your problem you should put your module under app/modules/transaction_helper/generator.rb
Also you don't need self in a module scope.
Personally I would create the helper as follows:
module TransactionHelper
def generate_alphanumeric_code
# params are automatically available in helpers.
end
end
And put it under app/modules/transaction_helper.rb.
Every time I call validates_with NumberValidator. I get an uninitialized constant error. I have created a directory called validators under the "app" directory. This is what I have so far.
This is my model
class Worker < ActiveRecord::Base
include ActiveModel::Validations
validates_with NumberValidator
end
This is my validator file
class NumberValidator < ActiveModel::Validator
def validate(record, attribute, value)
puts record
puts attribute
puts value
end
end
require File.expand_path('../boot', __FILE__)
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module ApplicationName
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Do not swallow errors in after_commit/after_rollback callbacks.
config.active_record.raise_in_transactional_callbacks = true
config.autoload_paths += %W["#{config.root}/app/validators/"]
end
end
I have restarted my server and I'm just not sure what I am doing wrong
Rails will automatically load anything under app/ assuming you follow the expected structure. The way Rails does this is with Constant Autoloading, which maps a constant, such as MyValidator to a file name, such as my_validator.rb under a directory in the autoload paths.
In order for Rails to load your NumberValidator, you should name the file number_validator.rb and put it in a folder in the autoload path, such as app/validators/number_validator.rb. You will need to restart the server if you have added a directory under app because these are initialized at boot time (be sure to run spring stop if you are using spring so it restarts too!).
Notes:
You do not need to add anything under app to your app config, so you can remove the config.autoload_paths line.
ActiveRecord::Base already includes ActiveModel::Validations, so you can also remove that include from your Worker class.
For more information on how this process works, check out the Autoloading and Reloading Constants page in the Rails Guides.
under lib/ I have 'aisis_writer/loader.rb' which, inside that file, looks like:
module AisisWriter
module Loader
end
end
I then have, in my application.rb the following set up:
require File.expand_path('../boot', __FILE__)
# Pick the frameworks you want:
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env)
module AisisWriter
class Application < Rails::Application
# Load Lib
config.autoload_paths += %W(#{config.root}/lib)
# Use Rack Attack For Throttling
config.middleware.use Rack::Attack
end
end
From there I did, in the ApplicationController.rb: include AisisWriter::Loader and then I ran my tests and got:
'<class:ApplicationController>': uninitialized constant AisisWriter::Loader (NameError)
Either I cannot do what I am doing because of naming conflicts or I am doing something wrong. Any one care to tell me what I might be doing wrong?
I don't think your config.autoload_paths is broad enough -- it's not including subfiles of the lib directory.
This should do the trick:
config.autoload_paths += Dir[Rails.root.join('lib', '{**/}')]
Try defining like this within 'aisis_writer/loader.rb'
module AisisWriter::Loader
end