Using wrong namespace in the Rails controller - ruby-on-rails

I have a controller: "/app/controllers/analyst/test_orders_controller.rb".
In this file I have:
class Analyst::TestOrdersController < ApplicationController
def new
#order = Order.new
end
end
But I have an error:
uninitialized constant Analyst::TestOrdersController::Order
But I don't want to use Analyst::TestOrdersController::Order.new, I just want to use Order.new. It is strange. What is the problem?

Use ::Order.new
The interpreter is looking for the definition of Order under the Analyst module namespace, this happens because the application models are loaded lazily so the file models/order.rb has not been read yet. Adding the general namespace tells it search for the definition in the Rails paths.
The way to confirm this is to add some random function call in the Order model body and see that it's not executed unless you call ::Order explicitly.

try:
module Analyst
class TestOrdersController < ApplicationController
def new
#order = Order.new
end
end
end
I think it has to do with module nesting:
https://cirw.in/blog/constant-lookup

Related

Ruby on Rails 6 loading module

I would like to know what is the right way to use a class with namespace in rails 6.
I have the follow, but it isn't working and I'm receiving the error:
"Uninitialized constant ProductsController::Operations Did you mean? ProductsController::Options"
#app/operations/create.rb
module Operations
class Create
def self.foo
...
end
end
end
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def create
Operations::Create.foo
end
end
Can you help me please?
Your module should be inside of any folder. For example app/services/operations/create.rb (any name will work) with the same content you have:
module Operations
class Create
def self.foo
...
end
end
end
and call it Operations::Create.foo.
Also make sure you restart spring with spring stop.

NameError (uninitialized constant Api::V1::RegistrationsController::JsonWebTokenAuthentication):

my jsonwebtokenauthentication.rb is in (app/lib.jsonwebtokenauthentication.rb):
class JsonWebTokenAuthentication
def some_method
#logic of the method
end
end
I am trying to access the above JsonWebTokenAuthentication method's in my registrations_controller.rb(app/controllers/api/v1/registrations_controller.rb)
class Api::V1::RegistrationsController < Api::V1::BaseController
def create
auth_token = JsonWebTokenAuthentication.some_method({user_id: user.id})
end
end
end
How can we use the class method specified inside lib folder in rails project.
Firstly if you want to use the RoR framework you are supposed to name your files using the Ruby Style guide. Also JsonWebTokenAuthentication looks more like a module, not a class for me, could you please clarify why you choose the class here?
I would suggest adding the some_method to the ApplicationController instead. Another option is to add a json_web_token_authentication.rb to the app/services/ but using the same namespace as the controller.

Encapsulating controller logic in rails

I have 2 controllers in rails with different authentications schemes,
but they do almost the same.
What is the best way in rails to encapsulate
the logic of a controller in another class or helper?
Sample:
def ControllerA < BasicAuthController
def create
blablacode
end
end
def ControllerB < TokenAuthController
def create
blablacode
end
end
Whats the proper way to do this? create a model with the code?
Create a helper? other?
The simplest thing is to make a module and then include it into the other controllers:
module ControllerMixin
def create
blablacode
end
end
The remaining question, though, is where to put this code such that it is works with Rails autoloader, since it needs to be loaded before the controllers. One way to do it would be to write the module to a file in the lib/ directory, then add that to the autoload paths (see auto-loading-lib-files-in-rails-4
Why don't you enable both schemes for a single controller? Especially if the only difference is Authentication. You could have two app/controllers/concerns to encapsulate both authentication methods and include Auth1 and include Auth2 for a single controller who is only responsible for whatever resource it manages.
Otherwise, services are the best approach to encapsulate controller logic.
Create a folder called services in your app folder and write PORO classes here. Say you have a few places in your app where you want to pay for stuff via make Stripe.
# app/services/stripe_service.rb
module StripeService
def customer(args)
...
end
def pay(amount, customer)
...
end
def reverse(stripe_txn_id)
...
end
end
# controller
StripeService.customer(data)
=> <#Stripe::Customer>
Or if you only need to do one thing.
# app/services/some_thing.rb
module SomeThing
def call
# do stuff
end
end
# controller
SomeThing.call
=> # w/e
If you need an object with multiple reponsibilities you could create a class instead.
class ReportingService
def initialize(args)
...
end
def query
...
end
def data
...
end
def to_json
...
end
end
https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services
I do it something like this:
#app/services/my_app/services/authentication.rb
class MyApp::Services::Authentication
class < self
def call(params={})
new(params).call
end
end # Class Methods
#==============================================================================================
# Instance Methods
#==============================================================================================
def initialize(params)
#params = params
end
def call
... do a lot of clever stuff
... end by returning true or false
end
private
def params() #params end
end
Then:
class FooController < ApplicationController
before_action :authenticate
def authenticate
redirect_to 'some_path' unless MyApp::Services::Authenticate.call(with: 'some_params')
end
end
Short answer, i choose to create a Helper.
From all the suggestions in the answers
Create a Module:
Seems correct but it didnt feel right to have logic outside
the app directory. This wasnt an external module or library but
something very related to the logic of my app.
Integrate diferents authentications in one controller:
Was a good suggestion but i have to change all the logic of my app.
Create a Helpers:
It seems to me the better solution, i had the code on a helper, and
is inside the app directory, very near from the other logic.

undefined method for imported module

I have a controller under app/controllers
require_relative '../../lib/bases_helper'
class BasesController < ApplicationController
include BasesHelper
def index
BasesHelper.available_bases
end
end
I am trying to use a method defined in another module under lib:
module BasesHelper
def available_bases
#bases = Base.all
end
end
When I run my application and access the site, I get an error
undefined method `available_bases' for BasesHelper:Module
I can navigate to the method with my IDE by just clicking on its name. Why doesn't it resolve the method? What am I missing?
Although Junan Chakma answer will work, i will advise against setting it up that way. Its better (and follows Rails conventions) to use a private method in your controller and use a callback (i.e. before_action); for example:
class BasesController < ApplicationController
before_action :set_available_bases, only: [:index]
def index
end
private
def set_available_bases
#bases = Base.all
end
end
This will set up #bases instance variable to be used in your index action and index.html.erb view.
I think you don't need to add BasesHelper to use available_bases method. Just use method name like this
def index
available_bases
end
As you imported BasesHelper module in your controller all methods of BasesHelper will be available in your controller. So you can use those method just by calling(without its module name) its name.
If you want to improve your code quality and follow rails conventions then please check Gerry's answer.
It is because your method available_bases is an instance method of BasesHelper, not a class method. And you are calling it as if it were a class method.
If you want to use available_bases like a class method, extend the class instead of include-ing it.
class BasesController < ApplicationController
extend BasesHelper
...
end

Rails: How to get the model class name based on the controller class name?

class HouseBuyersController < ...
def my_method
# How could I get here the relevant model name, i.e. "HouseBuyer" ?
end
end
This will do it:
class HouseBuyersController < ApplicationController
def index
#model_name = controller_name.classify
end
end
This is often needed when abstracting controller actions:
class HouseBuyersController < ApplicationController
def index
# Equivalent of #house_buyers = HouseBuyer.find(:all)
objects = controller_name.classify.constantize.find(:all)
instance_variable_set("##{controller_name}", objects)
end
end
If your controller and model are in the same namespace, then what you want is
controller_path.classify
controller_path gives you the namespace; controller_name doesn't.
For example, if your controller is
Admin::RolesController
then:
controller_path.classify # "Admin::Role" # CORRECT
controller_name.classify # "Role" # INCORRECT
It's a bit of a hack, but if your model is named after your controller name then:
class HouseBuyersController < ApplicationController
def my_method
#model_name = self.class.name.sub("Controller", "").singularize
end
end
... would give you "HouseBuyer" in your #model_name instance variable.
Again, this makes a huge assumption that "HouseBuyersController" only deals with "HouseBuyer" models.
For namespaces working:
def resource_class
controller_path.classify.constantize
end
The accepted solution did not work for me as my controller and model was namespaced. Instead, I came up with the following method:
def controllers_model
(self.class.name.split('::')[0..-2] << controller_name.classify).join('::')
end
This is not possible if you are using the default MVC, which your code doesn't seem to follow. Your controller seems to be a model but maybe you just got a type there. Anyway, controllers and models are fundamentally separated in Rails MVC so controllers cannot know which model they are associated with.
For example you could have a model named post. This can have a controller posts_controller or could have a controller like articles_controller. Rails only knows about models when you def the actual code in the controller such as
def index
#posts = Post.all
#posts = Article.all
end
In rails standard controllers there is no way to know what the model is.

Resources