Can't access current_user inside .new do block in the ApplicationController - ruby

I'm using devise and the bitbucket api gem and I have a method in my ApplicationController which creates an instance so I can make API calls. To do that, it tries to read the token and secret from the current_user.
This works fine with hardcoded token and secret strings, I'm also able to do puts current_user.inspect before the do block, and that all works fine. I'm also sure that bb_token and bb_secret exist (I'm able to call puts on them individually).
But once I try to create my bitbucket instance, it can't read current_user anymore. Any ideas?
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
def bitbucket
puts "token----------"
puts current_user
#bitbucket = BitBucket.new do |config|
config.oauth_token = current_user.bb_token # replaceing this with hardcoded string works
config.oauth_secret = current_user.bb_secret # replaceing this with hardcoded string works
config.client_id = 'xx'
config.client_secret = 'yy'
config.adapter = :net_http
end
end
end
And the error:
NameError (undefined local variable or method `current_user' for #<BitBucket::Client:0x007fbebc92f540>):
app/controllers/application_controller.rb:12:in `block in bitbucket'
app/controllers/application_controller.rb:11:in `bitbucket'

It seems block passed to BitBucket.new is executed in context of new BitBucket::Client instance (BitBucket.new is really BitBucket::Client.new, according to this).
A glance to the source confirms this supposition.
If you want to pass current_user, you can recall that the blocks are closures, so they keep the context in which they are defined. So you can do something like this:
def bitbucket
# (...)
user = current_user # local variable assignment
#bitbucket = BitBucket.new do |config|
config.oauth_token = user.bb_token # it works because user is local variable and the block is closure
# (...)
end
end

Inside BitBucket.new do..end block,self is set to config. But current_user is not a instance method of BitBucket class. Thus a valid error is thrown.

Related

How do I set a current_user in Rack middleware for GraphQL

I'm using GraphQLPlayground configured in my config.ru
map "/graphql-playground" do
use GraphqlPlaygroundAuthentication
use GraphQLPlayground, endpoint: "/graphql"
end
end
And I want to authorize my requests via GraphqlPlaygroundAuthentication since Rack does not send cookies with the request.
In my graphql_controller.rb I have this:
def execute
variables = prepare_variables(params[:variables])
query = params[:query]
operation_name = params[:operationName]
context = {
current_actor: current_user
}
# more code here
How do I set current_user inside of GraphqlPlaygroundAuthentication.rb?
I have tried to set cookies with Rack::Utils.set_cookie_header! but even though I see the cookies inside Application tab in my browser, my current_user inside request is nil.
I have no idea how to set the current_user to be available inside the controller and then my playground requests are unauthorized.
Reason why Playground is mounted inside config.ru and not routes.rb: CSP configuration of my project. I cannot change that.
You need to define current_user method in your controllers/application_controller.rb.
class ApplicationController < ActionController::API
def current_user
# If test situation when user is logged in
User.first
# If test situation when user is not logged in
# nil
end
end

NameError trying to access Connection variable in Channel

I have a Connection that sets a current_user variable:
class MyConnection < ActionCable::Connection::Base
identified_by :current_user
def connect
# snip - work to authenticate the user
self.current_user = user
end
end
According to the docs, this should make the variable current_user available to the corresponding channel. The docs say:
Also note that in this example, current_user is available because it was marked as an identifying attribute on the connection. All such identifiers will automatically create a delegation method of the same name on the channel instance.
However, when I try to access current_user in MyChannel:
class MyChannel < ActionCable::Channel::Base
def subscribed
subscription_name = "my_channel#{current_user.id}"
stream_from subscription_name
end
end
I get a NameError:
NameError - undefined local variable or method `current_user' for #<MyChannel:0x00000000deadbeef>
What am I doing wrong?

Padrino controller abstraction

I've been trying Padrino framework in one of my project, and there is one thing that really annoys me. I want to implement just for instance a user registration process using OmniAuth and want to break my request handler (controller's action) to separate methods, like this:
get ":provider/callback" do
#user = find_the_user_by_oauth(request)
create_user unless #user
store_user_in_session
end
def find_the_user_by_oauth(request)
#...
end
def store_user_in_session
session[:user_id] = #user.id
end
I know it would be nicer to push the logic to the model layer, but my question is, how could I break a controller logic to separated methods and share information among them (like using instance variables). In Rails I created these methods in the private scope of my controller, but here I should extend the Application class because it throws Undefined method exception for the previous code. I tried Helpers, but helpers don't know the instance variables, so you should pass the variables every time.
What is the good way to make my controller actions clean in Padrino?
To define a method inside an Padrino Controller you can use define_method instead of def.
For your example, do something like this:
Admin.controllers :dummy do
define_method :find_the_user_by_oauth do |request|
request.params["username"]
# ...
end
define_method :store_user_in_session do
session[:user_id] = #user
end
get :test do
#user = find_the_user_by_oauth(request)
create_user unless #user
store_user_in_session()
session.inspect
end
end
Padrino runs the block sent to Admin.controllers using instance_eval.
See this answer for the differences https://stackoverflow.com/a/3171649 between define_method and def
possible offtopic, but would you consider to use Espresso Framework instead.
then you'll can solve your issue as simple as:
class App < E
def index provider, action = 'callback'
#user = find_the_user_by_oauth
create_user unless #user
store_user_in_session
end
private
def find_the_user_by_oauth
# provider, action are accessed via `action_params`
# action_params[:provider]
# action_params[:action]
end
def store_user_in_session
session[:user_id] = #user.id
end
end

Ruby 1.9.2: How to change scope/binding of a block

Hi I have something like the folowing:
class TrialRequest
attr_accessor :trial_email
def initialize(email)
#trial_email = email
puts "Trial_email: #{trial_email}"
end
def create
#email = ::Gmail.connect!(gmail_name, gmail_password) do |gmail|
email = gmail.compose do
to 'trial#domain.com'
from trial_email
subject trial_email
text_part do
content_type 'text/html; charset=UTF-8'
body 'Sign me up.'
end
end
#binding.pry
gmail.deliver!(email)
end
end
end
The problem is that inside the compose block trial_email is not defined:
NameError: undefined local variable or method `trial_email' for #<Mail::Message:0x0000000431b830>
Is this a Ruby 1.9 issue or a gmail gem issue?
How should I go about making this method 'visible'/within the scope of the compose block?
Update:
This is an issue/feature of the gmail gem - ruby 1.9 blocks have changed but not this much!
In addition to the accepted answer, another workaround is to pass the data in as a method parameter:
def create(trial_email)
...
end
Looks like a GMail issue to me. Inside the blocks, self will be some object from the GMail gem so that you can have to, from, and similar DSL niceties available. You should be able to put self.trial_email into a local variable and then access that inside the blocks:
email_address = self.trial_email
#email = ::Gmail.connect!(gmail_name, gmail_password) do |gmail|
email = gmail.compose do
to 'trial#domain.com'
from email_address
subject email_address
#...
You're expecting (as you're entitled to) that the block should preserve the value of self, as it usually does. It looks like the gmail gem is using instance_exec here which allows it to change the value of self for the block to an instance of Mail::Message (which is why you can call to and from in that block even though you define no such methods)
While instance_exec is handy for producing nice DSLs, it is not without its downsides. Local variable scope isn't affected so you could store either trial_email or self in a local variable prior to the block and then use that local variable inside the block
The problem is that the block you pass to compose method is later passed to Mail.new and finally to Message.new (if I traced the chain correctly) and then this block is evaluated like that here:
instance_eval(&block)
As it's performed inside initialize method of a different object (instance of Message class) you do not have access to attributes of your TrialRequest object.
You can do the same thing without having any troubles like that:
email = gmail.compose
email.to = 'trial#domain.com'
email.from = trial_email
email.subject = trial_email
email.text_part do
content_type 'text/html; charset=UTF-8'
body 'Sign me up.'
end

Testing #current_user method using RSpec

I've been trying to do this for a couple of days now, but I can't figure it out. I have the following code in my controller:
#some_object = #current_user.some_method
In my spec, I want to attach a should_receive hook on that method, but I can't make it work. I've tried all of these, but none of them work:
assigns[:current_user].should_receive(:some_method).at_least(:once) # expected 1, got 0
User.should_receive(:some_method).at_least(:once) # expected 1, got 0
How is the correct way of testing this? I'm running this in my spec, and login is working:
setup :activate_authlogic
...
UserSession.create(users(:rune))
Thanks!
One example comes from the Ruby on Rails Tutorial. Rather than setting and reading #current_user directly, it defines two helper methods:
def current_user=(user)
#current_user = user
end
def current_user
#current_user
end
Later, they access this method in the tests using the controller method:
def test_sign_in(user)
controller.current_user = user
end
Using this methodology, you should be able to use
controller.current_user.should_receive(:some_method).at_least(:once)
You can’t call something like in the controllers:
expect(current_user).to be_present
expect(user_signed_in?).to be_true
So to do so, you can do this :
module ControllerMacros
def current_user
user_session_info = response.request.env['rack.session']['warden.user.user.key']
if user_session_info
user_id = user_session_info[0][0]
User.find(user_id)
else
nil
end
end
def user_signed_in?
!!current_user
end
end
You can either include the ControllerMacros in the top of the controller spec or include it in the spec_helper.rb like so :
RSpec.configure do |config|
config.include ControllerMacros, type: :controller
end

Resources