I am new to rails and get stuck on this problem.
The thing I am trying to do is:
I need to call service A to retrieve an idA and then I use idA to perform other actions. my actions in the controller is something like
class SomeController < ApplicationController
def someAction
idA = getIdAfromServiceA(config)
doSomethingElseToServiceB(idA)
end
end
Since the result from serviceA only depends on config, once the config is loaded, idA should not change. Therefore I want to cache idA.
I tried to use instance variable to cache it, no luck("getIdAfromServiceA is called is printed" on every request)
class SomeController
def getIdAfromServiceA(config)
#IdA ||= getIdAfromServiceAviaHTTP(config)
end
private
def getIdAfromServiceAviaHTTP(config)
puts "getIdAfromServiceAviaHTTP is called"
#make some http call
end
end
I also tried to put it in application.rb to cache it on start up. it shows error: undefined method 'getIdAfromServiceAviaHTTP' for SomeHelper:Module (NoMethodError)
module MyProject
class Application < Rails::Application
config.load_defaults 5.1
require_relative 'relativePathToSomeHelperModule'
config.idA = SomeHelper.getIdAfromServiceAviaHTTP(config)
end
end
So my question is, what's a good way to achieve this ? I've been googling for a while but end up in vain. Could you help me with it ? Thanks !
Create a ruby file in under /config/initializers/ and put that code there.
Option 1:
# config/initializers/ida_from_api.rb
IDA = SomeHelper.getIdAfromServiceAviaHTTP(config)
You can then use IDA through out the application.
Option 2:
# config/initializers/ida_from_api.rb
Rails.configuration.idA = SomeHelper.getIdAfromServiceA(config)
You can then use Rails.configuration.idA through out the application. Check Custom configuration for more details.
FYI - files under initializers loaded at the time of application startup.
Related
I have some logic that is going to manipulate data before starting a job queue. However, inside the controller and also in the rails console I cannot seem to access the classes. Example:
In app/services/hobo_service.rb I have
class HoboService
def initialize
#api = Hobos::Api.new
end
def run
hobo
end
private
attr_reader :api
def hobo
api.hobo
end
end
However, if in my relevent controller I put
...
def create
#name = HoboService.new.run
end
...
Raises an exception saying the object cannot be found.
It seems as if all in the app directory should be in the pipeline and available. What am I missing here? Haven't been on Rails since 3.2 until recently.
I'm not sure why a subdirectory of app would be ignored, but let's try the simple solution- what happens when you add this to the Application class in your application.rb?
config.autoload_paths += %W(#{config.root}/app/services)
It's my first RoR application.
I want to use this class (app/models/from_db/users/user_base.rb)
module FromDb::Users
class UserBase
include ActiveModel::Serializers::JSON
attr_accessor :Login, :Email
end
end
In this controller (app/controllers/my_controller.rb)
class MyController < ApplicationController
require "from_db/users/user_base"
def default
user = UserBase.new
user.Login = "Marcin"
user.Email = "ja#gmail.com"
end
end
user = UserBase.new throw this error:
uninitialized constant MyController::UserBase
When I put FromDb::Users::UserBase.new everything works fine but I thought that 'require' is like 'using' in C# or 'import' in java. I don't want to have to put this namespace all time before class from other dir. What I am doing wrong?
My second question. Is any way to write require "FromDb/Users/UserBase" instand of require "from_db/users/user_base" ? Now when I put first version (FromDb/Users/UserBase) it throw that error:
cannot load such file -- FromDb/Users/UserBase
I use ruby 2.1.5 and Rails 4.2.1
While require is similar to Java's import, it doesn't have any of the namespace manipulation stuff that import provides. If you really want to have shorter references, you'll need to create them yourself.
class MyController < ApplicationController
UserBase = FromDb::Users::UserBase
def default
user = UserBase.new
# ... etc
end
end
Also, since this is a Rails application, you don't need the explicit call to require (and it's better if you leave it off). If you name everything following the standard conventions, Rails will require the file for you automatically, then reload it whenever the file changes. If you do a manual require you'll lose the autoreloading.
With regards to the first question, try the following to import the namespace
include FromDb::Users
Don't use imports all over the place as they might cause conflicts within your class (see comment by apneadiving).
Or simply create an alias:
FUsers = FromDb::Users
FUsers::UserBase.new...
requirejust loads file's content in memory, so you still have to use FromDb::Users::UserBase and I recommend it, its clearer.
You cant camelize the name: its is meant to be a file name.
You have to give the path from the root, its safer:
require "#{Rails.root}/app/models/from_db/users/user_base"
Notice you dont need require since you have your code in /app
in order to create a shortcut you could do:
def default
user = user_base_class.new
end
def user_base_class
::FromDb::Users::UserBase
end
this way you dont create useless constant.
In Rails, how do you use a specific method from a module. For eg,
# ./app/controllers/my_controller.rb
class MyController < ApplicationController
include MyModule
def action
MyModule.a_method
end
private
def a_method
...
end
end
# ------------------------------------------------ #
# ./app/helpers/my_module.rb
module MyModule
def a_method
...
end
end
MyController includes MyModule. And in action ,I want to use MyModule.a_method (Please note I also have a private a_method in MyController and I don't want to use this.)
Things I've tried :
1) Defining the method in the module as self.
def self.a_method
end
2) Using the :: notation in controller (MyModule::a_method)
The error that I keep getting is
Undefined method:a_method for MyModule:module
For now, I've resorted to using a different name for the modules method. But I'd like to know how to namespace the function with either the Module:: or Module. notation
[UPDATE - 11/24/2014]
adding file structure in code, since Rails heavily relies on convention.
So I am not really sure what you are trying to accomplish with your module but a quick solution to get it working is below.
Move my_module.rb out of helpers and into lib/my_module.rb. The helpers directory is for methods that you use in your views. The convention is to utilize helpers that are namespaced after their respective controller or the application_helper.rb for global methods for your views. Not sure if that's what you are trying to accomplish with your module but wanted to throw that out there.
Create an initializer (you can all it whatever) in config/initializers/custom_modules.rb and add require 'my_module'
Update the a_method back to be self.a_method
You can now call MyModule.a_method in your app
Don't forget to restart your server for changes to lib/my_module.rb to take effect.
Also, a lot of people reference this post by Yehuda Katz as guidance on where to store code for your app. Thought it might be a helpful reference.
if you include MyModule into MyController, all the "instance methods" of the first will be mixed-in into the 2nd.
So if you only want to call MyModule.a_method, no need to include your module.
Then you'd want to require (or better autoload) your module before using it. To do so place it in controllers/concerns/my_module.rb, rails (4 at least) should autoload it, otherwise require its file in an intializer
# my_module.rb
module MyModule
def self.a_method
...
end
end
should work, but doing
# my_module.rb
module MyModule
extend self
def a_method
...
end
end
is more clean to me. You'd like to have a look to rails active support concern to understand the "rails way" on this topic.
If I do:
rails generate scaffold account/user username
I get a controller that looks like this:
class Account::UsersController < ApplicationController
def index
#account_users = Account::User.all
end
...
end
If I include the Account Module, then it looks like all the database calls don't need to be prefixed with "Account::". I.e.
class Account::UsersController < ApplicationController
include Account
def index
#account_users = User.all #this works because I included the Account Module above
end
...
end
Now if I were to move my
controllers/account/users_controller.rb
file to:
controllers/admin/account/users_controller.rb
The file looks like this (note: I also corrected my routes file after this move):
class Admin::Account::UsersController < ApplicationController
include Account
def index
#account_users = User.all #this call does not work now
end
...
end
But I get an error saying "uninitialized constant Admin::Account::UsersController::User"
It looks like rails is trying to make a database call on the "User" model without the "Account::" module in front of it.
So how does including modules in controllers work? Why does this not work when I move my controller into a different file (and leave the model in the same location from the generated scaffold) but it works with the scaffold generated files? How can I fix this issue?
Resolving the name of a module is done relative to the current module. Try and change it to:
include ::Account
or
include ::Admin::Account
(depending on the module in which your User model is defined)
This will tell ruby to look in the global namespace for the module Account
I guess I didn't realize you can just explicitly require the path to the module you would like to include. I learned this after reading up on modules some more...
So adding an explicit call to "require 'account/user'" just outside the controller class makes it so including the module in the controller works.
I've got two Rails 2.3 applications, we'll call them admin and frontend. In admin I have all my models specified in app/models. In frontend I have those models symlinked. I would like to add frontend specific methods to a model that only show up for the frontend application, and not the admin app.
At first I tried just adding config.autoload_paths += "#{RAILS_ROOT}/app/augments/address.rb" with:
class Address
def hello
"hello world"
end
end
But that just wasn't loaded. Calls to Address.first.hello would be met with undefined method 'hello'.
If I require a file that does this:
Address.class_eval do
def hello
"hello world"
end
end
It is loaded once, and for the first hit in development it works, but all subsequent reloads it fails. This is due to config.cache_classes = false in development.
A semi-working solution is to run that from ApplicationController:
class ApplicationController < ActionController::Base
Address.class_eval do
def hello
"hello world"
end
end
end
Which does reload and works every time in dev andprod, but doesn't work for script/runner or script/console. (If this is the only solution I'm sure we could extract that out into a module and include ModelExtensions in ApplicationController.)
Is there something I can add to environment.rb or an initializer that will get reloaded every time in development?
To extend your class you should use module and include it in your model. Something like this:
module Address
def hello
"hello world"
end
end
This is an old but always interesing article on that argument: http://weblog.jamisbuck.org/2007/1/17/concerns-in-activerecord
To include the module only in frontend you should check if the module exists with:
Class A
include Address if defined? Address
end