Is it possible to use the session variable inside of an ordinary .rb file except from the controller?
I'm using this facebook messenger rails gem to build a bot. All requests come to app/bot/listen.rb. In that listen.rb file:
require "facebook/messenger"
extend ActionView::Helpers # I need also the session
include Facebook::Messenger
Bot.on :message do |message|
session[:demo] = 1
puts "Your session number is: #{session[:demo]}"
end
Module::DelegationError (ActionView::Helpers::ControllerHelper#session delegated to controller.session, but controller is nil: main)
Is it possible to store data in session from within that listen.rb file?
The UserInfo module encapsulates reading and writing the User object from/to the Thread local. This module can then be mixed in with other objects for easy access.
module UserInfo
def session
Thread.session
end
def self.session=(session)
Thread.session = session
end
end
A before_filter set in the ApplicationController will be called before any action is called in any controller. You can take advantage of this to copy a value out of the HTTP session and set it in the Thread local:
class ApplicationController < ActionController::Base
before_filter :set_user
protected
# Sets the current user into a named Thread location so that it can be accessed by models and observers
def set_user
UserInfo.session = session[:user]
end
end
At any point in a Model class that you need to have access to those values you can just mixin the helper module and then use its methods to access the data. In this final example we mixin the UserInfo module to our model and it will now have access to the current_user method:
class Account < ActiveRecord::Base
include UserInfo
after_update :log_audit_change
private
def log_audit_change
Audit.audit_change(current_user, self.id, self.new_balance)
end
end
Related
In my Rails application I have a class that I want to initialize and then access it throughout my controllers. So the idea is that I set it via the application controller if it's not already been defined:
class ApplicationController < ActionController::Base
before_action :set_custom_class
# create an instance of customclass if doesn't exist
def set_custom_class
#custom_class ||= CustomClass.new
end
end
An example of the class:
class CustomClass
def initialize; end
def custom_method
#custom_method
end
def custom_method=(content)
#custom_method = content
end
end
If I then have a controller like:
class MyController < ApplicationController
def method_1
# set the custom_method value on my instance
#custom_class.custom_method('Some content')
# return the value I set above
#variable = #custom_class.custom_method
redirect_to :method_2
end
def method_2
# I should be able to retrieve the same value from that same instance
#variable = #custom_class.custom_method
end
end
What I'm finding is that when calling method_1 the #variable will return my content fine, but when calling method_2 AFTER method_1 (so the custom_method for the app wide #custom_class has been set) it's returning nil.
Why isn't the instance being retained? The #custom_class shouldn't be creating a new instance as it's already been set. So I can't understand why the value I have set gets lost when requesting it.
You witnessing such behaviour, because state of a controller is not preserved between requests. For example, imagine that current_user method sets #current_user for one request and returns the same user for another one.
Please, consider an option of using cookies or database for sharing state between requests.
Otherwise, a workaround would be setting a class variable of CustomClass, but I don't recommend to do it.
Looks like your before_action will re-instantiate the new object on every request. That means that since you aren't passing anything through to the class in Method2, it will come out as NULL.
Since you said app-wide, why not make it app-wide?
In config/application.rb,
module App
class Application < Rails::Application
def custom_class
#custom_class ||= CustomClass.new
end
end
end
in your application code,
Rails.application.custom_class
I'm trying to access to the current user outside of a controller and outside of a model. This is the architecture of the project
main_engine
|_bin
|_config
|_blorgh_engine
|_ —> this where devise is installed
|
|_ blorgh2_engine
|_app
|_assets
|_models
|_assets
|_queries
|_ filter_comments.rb -> Where I want to use current_user
module Blorgh2
# A class used to find comments for a commentable resource
class FilterComments < Rectify::Query
# How to get current_user here ?
...
end
end
I don't think there is a way to do it. If you have an idea, you are welcome.
If the engine is running in the same thread then perhaps you could store the current_user in the Thread.
class ApplicationController < ActionController::Base
around_action :store_current_user
def store_current_user
Thread.current[:current_user] = current_user
yield
ensure
Thread.current[:current_user] = nil
end
end
Then in your filter_comments.rb you can define a method
def current_user
Thread.current[:current_user]
end
The current_user variable is tied to the current request, and thus controller instance. In this case you should probably just parameterize your query with the user you want to filter for:
class FilterComments < Rectify::Query
def initialize(user)
#user = user
end
def query
# Query that can access user
end
end
Then, in your controller:
filtered_comments = FilterComments.new(current_user)
This makes it clear where it's coming from, allows you to reuse it with any user, and makes the query object testable, since you can just pass in any user in your test setup.
In my apps, I'm using variables that scoped to the thread currently executing. This is Rails 5 feature, and it really helping with such out of scope situations.
Idea in this blogpost.
Realisation based on Module#thread_mattr_accessor
Here example of code.
class AuthZoneController < ApplicationController
include Current
before_action :authenticate_user
around_action :set_current_user
private
def set_current_user
Current.user = current_user
yield
ensure
# to address the thread variable leak issues in Puma/Thin webserver
Current.user = nil
end
end
# /app/controllers/concerns/current.rb
module Current
thread_mattr_accessor :user
end
Now you can access Current.user in your current thread in all application scope.
I'm using attr_accessor in my controllers to store permissions which are set in a method defined in the applicationController but I'm having issues.
If I do this:
class ApplicationController < ActionController::Base
attr_accessor :perms
def self.set_permissions *permissions
self.perms = permissions
end
def check_permissions
print self.perms
end
end
Then rails claims that perms= doesn't exist
if I do:
class ApplicationController < ActionController::Base
attr_accessor :perms
def self.set_permissions *permissions
#perms = permissions
end
def check_permissions
print #perms
end
end
#perms in check_permissions is nil in any child controller, I checked for what instance methods are available using: print self.instance_variables but #perms didn't show up; probably because #perms is being set for the class instance but not the object instance of the controller that's created when the action is called
Whats going on? Does rails not like attr_accessor on controllers? How can I get this to work so I can set permissions within the class and have them accessible to any objects of that class so I can set them like this:
class Api::ApiController < ApplicationController
set_permissions :api
before_action :check_permissions, except: :set_permissions
end
I have to use self.set_permissions, otherwise It claims that set_permissions doesn't exist when I try to set them (because it's an object method not a class method)
EDIT:
For clarification, I want the permissions to be set in the controller (but be different for each controller); be the same for any instance's of that controller (I.e. object instance created for an action) or any controllers that inherit from that controller (e.g. node controller inherits from api controller and so uses the same permissions) unless it's been defined within that inherited controller separately (tokens controller inherits from api controller but need to be permission free so a person can actually get a token without needing permissions).
You are defining
def self.set_permissions *permissions
self.perms = permissions
end
which is a class level method, while attr_accessor is at instance level.
If you wish to set class level permissions I suggest you to use cattr_accessor. Check its instance_reader and instance_writer options too.
I'm trying to share a session variable in both the controllers, the views and the model.
With the following code, it is working in the controllers and in the views :
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :best_language_id
# Returns the ID of the language to use to display the description.
def best_language_id
#best_language_id ||= session[:best_language_id]
#best_language_id ||= current_project.default_language.id
return #best_language_id
end
end
But I can't call it from the model.
I would like to be able to call best_language_id either in the controllers, views and in one model, to get a fallback of the best_language_id if a translation is not found.
Example in my model (not working) :
class Point < ActiveRecord::Base
# Retuns the attached word in the given language if exists.
# Otherwise, falls back on another translation
def word(preffered_language_id)
word = Word.find(:translation_id => self.translation_id, :language_id => preffered_language_id)
if word.blank?
word = translations.where(:translation_id => self.translation_id, :language_id => best_language_id)
end
return word
end
end
I know that model should not include applicationcontroller method calls, but how is it possible to share my best_language_id accross controllers and model ?
Edit : using i18n is not the question here. Translations are not fixed string but variables in a database.
Thanks for helping !
In your rails app, you have a base module in config/application.rb. It should be named after your application. Let's say its called MyApp. What you could do is define two methods like this:
module MyApp
...
def self.language_id=(value)
#language_id = value
end
def self.language_id
#language_id ||= 'en' # default vaule
end
...
end
Then, in app/controllers/application_controller.rb add a before_filter like this:
before_filter :language
def language
MyApp.language_id = session[:language_id] if session[:language_id]
end
Then, from all over the app, you can access the value via
MyApp.language_id
Needless to say that the approach is not thread safe so don't use it in a threaded environment.
I would suggest you switch the situation around, store the best_language_id in the model as a class accessor, then you can set and get it from your controllers and it will still be available in the models.
class Point < ActiveRecord::Base
cattr_accessor :best_language_id # to store the variable
end
# Persist the content of that variable at the start of every action
class ApplicationController < ActionController::Base
before_filter :set_best_language
def set_best_language
Point.best_language_id = session[:best_language_id]
Point.best_language_id ||= current_project.default_language.id
end
end
# Use the variable in a controller
class SomeOtherController < ActionController::Base
def show
#best_language = Language.find(Point.best_language_id)
...
end
end
# Use the variable in a model
class SomeOtherController < ActiveRecord::Base
def some_method
best_language = Language.find(Point.best_language_id)
...
end
end
I am playing with the heroku api in Rails and have come across a potential issue.
After submitting a login form i am instantiating the heroku object.
heroku = Heroku::API.new(:username => USERNAME, :password => PASSWORD)
I would then like to use the heroku object in all controllers to further querying the api. I have tried #heroku, ##heroku and $heroku, but none work. Is this possible?
The only solution i have found it to use the api to fetch the users api key, store this in a session then use it to re-instantiate the heroku object within each controller method. Is this the best / only solution?
In general, a before_filter could solve your re-instantiating problem. If you want to set an instance variable that is available to every controller method, do something like this:
class UsersController < ApplicationController
before_filter :get_user
def profile
# #user is accessible here
end
def account
# #user is accessible here
end
private
def get_user
#user = User.find(params[:id])
end
end
You can also user before_filters in the application controller to set instance variables that are accessible in all of your controllers. Read about filters here.
As for storing the API keys to the session, that works, but if you want long term access, you might want to write the API keys to the database. Combined with before filters, you could do something like this:
class ApplicationController < ActionController::Base
before_filter :setup_heroku
def setup_heroku
if current_user && current_user.heroku_api_key
#heroku = Heroku::API.new(:api_key => current_user.heroku_api_key)
end
end
end