Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

session, cookies, and current_user are only accessible while rendering #52

Open
ernest4 opened this issue Sep 12, 2020 · 5 comments
Open
Labels
bug Something isn't working

Comments

@ernest4
Copy link

ernest4 commented Sep 12, 2020

Hey guys :)

If i understand this correctly then i need to uncomment this to get access to session / cookies?

Screenshot 2020-09-12 at 01 24 16

But no matter what, i cant seem to be able to access them. Get an error about undefined method 'controller'

Screenshot 2020-09-12 at 01 26 35

Am i doing something wrong or?

just trying to execute this bit of logic here, update Current user.

Screenshot 2020-09-12 at 01 27 58

Thanks

@ernest4 ernest4 changed the title Session / cookies Session / cookies inacessible Sep 12, 2020
@ernest4 ernest4 changed the title Session / cookies inacessible Session / cookies inaccessible Sep 12, 2020
@alecdotninja
Copy link
Contributor

alecdotninja commented Sep 12, 2020

Howdy, @ernest4 !

If I understand correctly, you do not need to un-comment that section in the initializer. You would only need to do that if you wanted to do something different than what is already there.

The session and cookies are available as instance methods (session and cookies respectively). If you are using Devise, current_user should be as well. The Rails Current object works separately from the session/cookies (it's a "thread-isolated attributes singleton").

If you want to use the user from there, you need to set it yourself. I suspect adding something like this to your component will give you what you want:

around_action :with_current_user

private

def with_current_user(&block)
  Current.set(user: current_user, &block)
end

@alecdotninja alecdotninja self-assigned this Sep 12, 2020
@alecdotninja alecdotninja added the question Further information is requested label Sep 12, 2020
@ernest4
Copy link
Author

ernest4 commented Sep 12, 2020

Sorry I'm still stuck :D

So I've commented out the motion config again.

And I'm trying to access the cookies and session in the component. This is the component:

module Account
  class LocalizationComponent < SvComponent
    include Motion::Component

    around_action :with_current_user

    def initialize
      @setting = Account::AccountComponent::ACCOUNT_SETTINGS.find do |setting|
        setting[:title] == 'Localization'
      end
    end

    map_motion :update_language

    def with_current_user(&block)
      debugger
      # Current.set(user: current_user, &block)
    end

    def update_language(event)
      # TODO: ...

      # Any of these puts will fail
      # puts current_user
      # puts session
      # puts cookies

      debugger
      # doest work, no cookies or session ... 
      # Current.user.update(:language => event.target.value)
    end

    def call
      div(:class => 'sv-container lg:w-2/3 mx-auto') do
        c div(:class => 'pt-4')
        c div(:class => 'sv-card w-full') {
          c span(t(@setting[:title]), :class => 'sv-text text-xl sv-text-bold')
          c div(:class => 'pt-2')
          c span(t(@setting[:subtitle]), :class => 'sv-text')
        }
        c div(:class => 'pt-4')
        c div(:class => 'sv-card') {
          c label(:user, :language, t('Language'), :class => 'sv-text')
          c div(:class => 'pt-2')
          c select(:user, :language, Localized::SUPPORTED_LANGUAGES.map { |lang| [lang, lang]},
                   { :selected => Current.user.language },
                   { :class => 'sv-input', :data => { :motion => 'update_language' } })
          # {:class => 'sv-input', :data => { :motion => 'change->update_language' }})
        }
      end
    end
  end
end

so at the two byebug points there im trying to access cookies or session but i get error?

Screenshot 2020-09-12 at 09 58 05

Screenshot 2020-09-12 at 10 04 50

still no sure what i'm doing wrong?

btw this is what my connection.rb looks like, does that mean that i should have access to current_user as well (because that errors out too)?

# frozen_string_literal: true

module ApplicationCable
  # handles incoming connection and authentication
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user_from_cookies
    end

    private

    # TODO: might need to stop using the reject_unauthorized_connection check here as this stops
    # socket traffic for non logged in users (when it might be desirable like in case of Motion
    # components).
    def find_verified_user_from_cookies
      current_user = User.find_by(:id => cookies.encrypted[:user_id])
      current_user #|| reject_unauthorized_connection
    end
  end
end

I tried calling @session, @cookies, @current_user but those all returned nil as well.

@ernest4
Copy link
Author

ernest4 commented Sep 13, 2020

Ok so i got it to work, but just wanna double check if this is the right conventional way to go about this.

So I can't access session or cookies in with_current_user or update_language, but those methods can access the instance variables of component. So i just set the instance variable for current_user in initialize, then use that in around_action hook, which then sets it up to be accessible in update_language callback (i could just access @current_user in update_language of course, but i think i'll just extract the around_action logic to SvComponent so i can have consistent access to Current.user, like the rest of the app.)

Does that all sound how it should be?

You've mentioned that 'The session and cookies are available as instance methods...' and i was looking at the motion code spec a little bit and they test that they are available in the render (i.e. the call method), which seems like they are available there. However I need to access them in the actual component class methods, so not sure if that actually works?

module Account
  class LocalizationComponent < SvComponent
    include Motion::Component

    around_action :with_current_user

    def initialize
      # ...

      # TODO: store id only and use that to look up in with_current_user
      # as marshaling objects is more memory intensive !!
      @current_user = Current.user
    end

    map_motion :update_language

    def with_current_user(&block)
      Current.set(:user => @current_user, &block)
    end

    def update_language(event)
      # TODO: error / success handling
      Current.user.update(:language => event.target.value)
    end

    def call
         # ...
    end

@alecdotninja
Copy link
Contributor

So i just set the instance variable for current_user in initialize, then use that in around_action hook, which then sets it up to be accessible in update_language callback (i could just access @current_user in update_language of course, but i think i'll just extract the around_action logic to SvComponent so i can have consistent access to Current.user, like the rest of the app.)

👍 I don't see any issue with this approach. I think it is a good workaround until I figure something else out for the view context.

You've mentioned that 'The session and cookies are available as instance methods...' and i was looking at the motion code spec a little bit and they test that they are available in the render (i.e. the call method), which seems like they are available there. However I need to access them in the actual component class methods, so not sure if that actually works?

This is a really good point. I've yet to run across a situation where I wanted to use them outside of rendering, so I had not noticed. Unfortunately, with how ViewComponent works, this is not a simple fix. It happens because the current_user is being derived from the view context, and that is only available during render. 😞

@alecdotninja alecdotninja changed the title Session / cookies inaccessible session, cookies, and current_user are only accessible whlile rendering Sep 22, 2020
@alecdotninja alecdotninja added bug Something isn't working and removed question Further information is requested labels Sep 22, 2020
@alecdotninja alecdotninja removed their assignment Sep 22, 2020
@alecdotninja alecdotninja changed the title session, cookies, and current_user are only accessible whlile rendering session, cookies, and current_user are only accessible while rendering Sep 22, 2020
@ernest4
Copy link
Author

ernest4 commented Sep 22, 2020

Yeah.. current_user is accessed pretty frequently though for any app with auth. In the controller you normally authenticate the user, then authorise the user to change some resource that only belongs to him/her.

With motion, this all happens in the motion callback now, so that's why I need current_user there to authorize the update.

I'm basically turning motion components into overpowered 'React like' things :D difference is that while React used to push / pull state to/from Redux (while synchronizing with backend via AJAX JSON) motion components simply, cleanly and directly just update the models in question - and i'm loving it :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants