Whenever i hit the endpoints (example: greet ) exposed by rails engine i receive this error.
Error:
"LoadError(Unable to autoload constant TestingController,
expected /local_path/app/controllers/testing_controller.rb to define it):
/Users/xxxxx/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/
dependencies.rb:507:in `load_missing_constant'"
Engine:
I have an engine with action method greet defined as below
# type: class
# path: app/controllers/testing_controller.rb
# file name: testing_controller.rb
module Parent
module Child
class TestingController < ApplicationController
def initialize
#name = 'BOB'
end
def greet
render json: {'hi': 'hi', 'name': #name}
end
end
end
end
routes defined as
# type: route
# path: app/config/routes.rb
# file name: routes.rb
Parent::Child::Engine.routes.draw do
get 'greet', to: 'testing#greet', as: :greet
end
Details:
This engine has been mounted to a gem ABC which is then used in the rails app called Example. When i hit the greet route in the app via
http://localhost:3000/greet for the first time i receive LoadError.
However if i refresh the page it renders the
json as expected. {"hi":"hi","name":"BOB"}.
we have development.rb (app/config/env/) has cache and eager load defined as below
config.cache_classes = false
config.eager_load = false
Ruby: 2.5.1
Rails: 5.2.1
Thanks for your help, much appreciated.
The message seems to be coming from the expectation that in the root level of the file testing_controller.rb has a definition of a TestingController class whilst you have that class defined nested within Parent::Child:: modules.
I guess that if you put your testing_controller.rb file in the following path: app/controllers/parent/child/testing_controller.rb the error will disappear.
Related
I want to create a Messenger Bot used by different users for their Facebook pages. I created a rails app and use the facebook-messenger gem.
I successfully created the bot and it works when I do the set up for one page. Now, I follow the instructions to make my bot live on multiple Facebook Pages (see "Make a configuration provider" section).
I'm new to rails and I'm not sure where to put the class ExampleProvider? I put it in my config/application.rb file:
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 BotletterApp
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.
config.paths.add File.join('app', 'bot'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'bot', '*')]
end
class BotProvider < Facebook::Messenger::Configuration::Providers::Base
def valid_verify_token?(verify_token)
bot.exists?(verify_token: verify_token)
end
def app_secret_for()
ENV['APP_SECRET']
end
def access_token_for(page_id)
bot.find_by(user_id: page_id).page_access_token
end
private
def bot
MyApp::Bot
end
end
Facebook::Messenger.configure do |config|
config.provider = BotProvider.new
end
end
Then I have my app/bot/setup.rb file to create the bot. I don't know how to use the provider I created in place of the ENV variables?
require "facebook/messenger"
include Facebook::Messenger
Facebook::Messenger::Subscriptions.subscribe(access_token: ENV["ACCESS_TOKEN"])
Facebook::Messenger::Profile.set({
get_started: {
payload: 'GET_STARTED_PAYLOAD'
}
}, access_token: ENV['ACCESS_TOKEN'])
I searched in the Rails documentation how to make it work but unfortunately could not find anything.
UPDATE:
Now I'm able to set up the bots for different pages. Unfortunately, the following line of my ConfigProvider is getting an error:
config/initializers/config_provider.rb
def bot
Rails.application.class.parent::Bot
end
I'm getting the following error:
NameError (uninitialized constant BotletterApp::Bot):
config/initializers/config_provider.rb:17:in bot'
config/initializers/config_provider.rb:7:inapp_secret_for'
Do you know how should I name my app?
My BotModule:
module BotletterApp
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.
config.paths.add File.join('app', 'listen'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'listen', '*')]
end
end
UPDATE, it works with ::Application, here is the new file:
class ConfigProvider < Facebook::Messenger::Configuration::Providers::Base
def valid_verify_token?(verify_token)
ENV['VERIFY_TOKEN']
end
def app_secret_for(page_id)
ENV['APP_SECRET']
end
def access_token_for(page_id)
CoreBot.find_by_page_id(page_id).page_access_token
end
private
def bot
BotletterApp::Application
end
end
Facebook::Messenger.configure do |config|
config.provider = ConfigProvider.new
end
The problem is I get the following error unless my db query seems working (it works in the rails console):
ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column:
page_id.id: SELECT "core_bots".* FROM "core_bots" WHERE
"page_id"."id" = ? LIMIT ?):
Moving to an answer for improved readability ;)
Regarding 'plain'... Instead of
def bot
BotletterApp::Application
end
use
def bot
Bot
end
or (it looks like you named your model containing all pages CoreBot(?) (assuming you have a typical ActiveRecord model in /app/models/core_bot.rb, I was assuming Bot)
def bot
CoreBot
end
Then you should be able to use the template code from the README.md
As for your latest problem: it seems like the access_token_for-method gets called with a hash, searching with something like {id: 1}. You might want to check where that value is coming from. I would suggest to take a few steps back, and stay closer to the template code.
I have defined a constant in config/environment.rb called LOCAL_SETTINGS that I use for configuration and use throughout my app. It's a YAML file stored in config/local_settings.yml and will sometimes contain sensitive data like API keys etc.
I'm currently trying to write a spec for a method that uses LOCAL_SETTINGS["slack"]["slack_token"].
The problem is that the constant is not being stubbed for my expectations. ie expect(subject.path).to eq(help_request) fails because it returns a path including the non-stubbed LOCAL_SETTINGS hash.
However if I put a debugger in beneath stub_const and then type LOCAL_SETTINGS, I can see that stub_const has worked.
My questions are:
Is there something I can do in Rspec to get stubs working for constants defined in config/environment.rb?
Should I simply define this constant elsewhere? If so, where? I will need access to it throughout my application in the app/ lib/ and spec/ folders.
My config/environment.rb file:
# Load the Rails application.
require_relative 'application'
LOCAL_SETTINGS = YAML.load_file("#{Rails.root}/config/local_settings.yml")
# Initialize the Rails application.
Rails.application.initialize!
My spec:
describe RequestBuilder, type: :model do
let(:help_params) { {"user_name"=>"some_user_name", "text"=>"release-bot help"} }
let(:help_builder) { RequestBuilder.new(help_params) }
let(:help_request) { "/api/files.upload?file=lib%2Fresponses%2Fhelp&filetype=ruby&channels=stubbed_channel&token=stubbed_token" }
let(:slack_settings) { {"slack"=>{"slack_token"=>"stubbed_token", "slack_channel"=>"stubbed_channel"}} }
context 'Given an incoming request' do
context 'With a correctly formatted request' do
context 'And the "help" command' do
subject { help_builder.build_request_hash }
it 'builds a request containing the help data' do
stub_const("LOCAL_SETTINGS", slack_settings)
expect(subject).to have_key(:request)
expect(subject[:request].path).to eq(help_request)
end
end
end
end
end
If you're using Rails, and you already have a .yaml file in the config directory, I would suggest looking at Rails custom configuration to load the YAML file. This way you'll be able to isolate any credentials from your test environment without having to stub the LOCAL_SETTINGS const for every test or change any of your classes.
# config/local_settings.yml
development:
slack_token: some_dev_token
slack_channel: some_channel
test:
slack_token: fake_token
slack_channel: fake_channel
production:
slack_token: <%= ENV['SLACK_TOKEN'] %>
slack_channel: <%= ENV['SLACK_CHANNEL'] %>
And then to load this configuration file:
# config/application.rb
module MyApp
class Application < Rails::Application
config.local_settings = config_for(:local_settings)
end
end
Then you can access the values from Rails.configuration.local_settings['slack_token'] rather than from the LOCAL_SETTINGS constant.
Another blog post highlighting the custom configuration.
I want to do an API for an Android app. When searching, I found {grape}. I'm following this tutorial, but I have a problem launching the Rails server:
=> Booting WEBrick
=> Rails 4.0.2 application starting in development on http://0.0.0.0:80
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
Exiting
C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/activesupport-4.0.2/lib/act
ive_support/dependencies.rb:464:in `load_missing_constant': Unable to autoload c
onstant Usuarios, expected C:/Sites/appCerca/app/api/v1/usuarios.rb to define it
(LoadError)
My directory:
app
..api
....api.rb
....v1
......root.rb
......usuarios.rb
and the files:
#application.rb
module AppCerca
class Application < Rails::Application
config.paths.add "app/api", glob: "**/*.rb"
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
end
end
#routes.rb
AppCerca::Application.routes.draw do
mount API::Root => '/'
[...]
#app/api/root.rb
module API
class Root < Grape::API
prefix 'api'
mount API::V1::Root
end
end
# app/api/v1/root.rb
module API
module V1
class Root < Grape::API
mount API::V1::Usuarios
end
end
end
# app/api/v1/usuarios.rb
module API
module V1
class Usuarios < Grape::API
version 'v1'
format :json
resource :usuarios do
desc "Return list of authors"
get do
Usuario.all
end
end
end
end
end
Why am I getting this error? I am using Ruby 1.9.3p484 and Rails-4.0.2.
Try either
Moving your API code's files from app/api to app/api/api, or
Moving your API classes outside the API module (i.e. deleting all the module API lines and their corresponding end statements).
From Grape's documentation:
Place API files into app/api. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for Twitter::API should be app/api/twitter/api.rb.
Thus the correct location for your API::Root class would actually be app/api/api/root.rb.
With this change your code starts and works fine for me on Rails 4.0.2.
We use Rails.cache and see an undefined class/module X error when loading pages that invoke class X. We followed the suggestion here to include an initializer that requires all models in the dev environment.
However, we see the same error. Any other suggestions? We included the initializer code below, and from the output to the console, it appears like the code is getting invoked.
We're on Rails 3.2.12 + Ruby 1.9.3.
if Rails.env == "development"
Dir.foreach("#{Rails.root}/app/models") do |model_name|
puts "REQUIRING DEPENDENCY: #{model_name}"
require_dependency model_name unless model_name == "." || model_name == ".."
end
end
Stack trace:
Processing by DandyController#get_apps as JSON
Parameters: {"category"=>"featured", "country_id"=>"143441", "language_id"=>"EN"}
WARNING: Can't verify CSRF token authenticity
Completed 500 Internal Server Error in 2ms
ArgumentError (undefined class/module App):
app/controllers/dandy_controller.rb:66:in `get_featured_apps'
app/controllers/dandy_controller.rb:50:in `get_apps'
Rendered C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/actionpack-3.2.12/lib/action_dispatch/middleware/templat
es/rescues/_trace.erb (2.0ms)
Rendered C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/actionpack-3.2.12/lib/action_dispatch/middleware/templat
es/rescues/_request_and_response.erb (2.0ms)
Rendered C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/actionpack-3.2.12/lib/action_dispatch/middleware/templat
es/rescues/diagnostics.erb within rescues/layout (46.0ms)
Code:
def get_featured_apps( country_id )
# Fetch featured apps from cache or from DB
apps = Rails.cache.fetch( 'featured_apps', {:expires_in => 1.days} ) do
logger.info '+++ Cache miss: featured apps'
get_featured_apps_helper country_id
end
# Get sponsored results
apps = get_featured_apps_helper country_id
# Return featured apps
return apps
end
I've overwritten the fetch method which worked wonders for me!
unless Rails.env.production?
module ActiveSupport
module Cache
class Store
def fetch(name, options = nil)
if block_given?
yield
end
end
end
end
end
end
A more elegant solution is to write a wrapper method you can call around your call to Rails.cache.fetch:
def some_func
apps = with_autoload_of(App) do
Rails.cache.fetch( 'featured_apps', {:expires_in => 1.days} ) do
logger.info '+++ Cache miss: featured apps'
get_featured_apps_helper country_id
end
end
end
##
# A simple wrapper around a block that requires the provided class to be
# auto-loaded prior to execution.
#
# This method must be passed a block, which it always yields to.
#
# This is typically used to wrap `Rails.cache.fetch` calls to ensure that
# the autoloader has loaded any class that's about to be referenced.
#
# #param [Class] clazz
# The class to ensure gets autoloaded.
#
def self.with_autoload_of(clazz)
yield
end
I have several small classes that are in a single file in /app/models, similar to:
# /app/models/little_class.rb
class LittleClass; ...do stuff; end;
class AnotherLittleClass; ...do stuff; end;
Rails only seems geared to autoload classes in files reflecting the class name. So referencing AnotherLittleClass outside of the file raises "unitialized constant" errors as below until LittleClass is referenced:
irb(main):001:0> AnotherLittleClass
NameError: uninitialized constant AnotherLittleClass
irb(main):02:0> LittleClass
=> LittleClass
irb(main):03:0> AnotherLittleClass
=> LittleClass2
It would be a pain and messy to split them into individual files. Is there a way to autoload these classes, so referencing AnotherLittleClass without LittleClass doesnt raise an error?
You could put them into a module and use them within this namespace SomeLittleClasses::LittleClass.do_something
# /app/models/some_little_classes.rb
module SomeLittleClasses
class LittleClass
def self.do_something
"Hello World!"
end
end
class AnotherLittleClass
def self.do_something
"Hello World!"
end
end
end
Try this trick:
1.9.2p312 :001 > AnotherLittleClass.new
# => NameError: uninitialized constant AnotherLittleClass
1.9.2p312 :002 > autoload :AnotherLittleClass, File.dirname(__FILE__) + "/app/models/little_class.rb"
# => nil
1.9.2p312 :003 > AnotherLittleClass.new
# => #<AnotherLittleClass:0xa687d24>
These are your choices, as I see it:
split your file up into one file per class, put them in a dir named according to the rails convention (SomeClass => some_class.rb) and in a startup file (say, create a file in config/initializers), call:
autoload_paths Rails.application.config.root + "/path/to/lib"
add something like this to a startup file:
%W[
Class1 Class2
Class3 Class4 Class4
].map(&:to_sym).each dp |klass|
autoload klass,Rails.application.config.root + "/path/to/lib/file"
end
This of course will have to be updated each time a new class is added to the file.
Move all of the classes into a module/class namespace and call autoload to add it as above
just load the whole file up-front in a startup file with require. Ask yourself: does the extra effort warrant delaying the load of this file?
The following file app/models/statistic.rb is given :
class Statistic
# some model code here
end
class UsersStatistic < Statistic; end
class CommentsStatistic < Statistic; end
class ConnectionsStatistic < Statistic; end
Create a file config/initializers/autoload_classes.rb and add the following code:
# Autoloading subclasses that are in the same file
# This is the normal way to load single classes
#
# autoload :UsersStatistic, 'statistic'
# autoload :CommentsStatistic, 'statistic'
# autoload :ConnectionsStatistic, 'statistic'
# This is the most dynamic way for production and development environment.
Statistic.subclasses.each do |klass|
autoload klass.to_s.to_sym, 'statistic'
end
# This does the same but loads all subclasses automatically.
# Useful only in production environment because every file-change
# needs a restart of the rails server.
#
# Statistic.subclasses