Using Devise in different controller: undefined method "resource=" - ruby-on-rails

I have Devise 4.2.1 and Rails 5.0.1. I'm trying to use Devise sign_in in a controller other than registrations. There were a bunch of methods that weren't defined, so I had to add them as helpers:
module ApplicationHelper
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
def build_resource(hash=nil)
self.resource = resource_class.new_with_session(hash || {}, session)
# ^the source of the error^
end
def resource_class
devise_mapping.to
end
end
But now I'm getting the following error:
NoMethodError in Static#show
undefined method 'resource=' for (class)
I don't have "resource=" written anywhere in my code, certainly not on the line the error points to (marked above). Where is my server getting that error from?
Also, I think I'm just using the standard sign_in code:
<%
build_resource({})
set_minimum_password_length
yield resource if block_given?
respond_with self.resource
%>
<% form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
...

I believe that your question has 2 answers: Why this error is happening, and how to have a sign_in partial to use on all your controllers:
Creating the sign_in partial
First of all, the sign_in method belongs to sessions, not registrations be careful to not replace the wrong controller :)
And you just added a few more code than needed for what you want. To create a partial sign_in to use anywhere, just use the default Devise form in it:
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %>
<% if devise_mapping.rememberable? -%>
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
<% end -%>
<%= f.submit "Log in" %>
<% end %>
That part with build_resource({}) isn't needed, as it'll be handled by Devise itself when you submit the form. And since build_resource isn't needed, you just need the following code on your ApplicationHelper:
module ApplicationHelper
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
This should be enough to have a sign_in form in any controller view. If you need more control over the login flow, you can check the Configuring Controller section on Devise Readme for more information on how to create a customizable controller.
Why your code throws a NoMethodError error
Your code throws an NoMethodError because you haven't defined a setter for the #resource instance variable. It should be either:
def resource=(value)
#resource = value
end
or with attr_writer :resource
I recommend this question for more info on the topic:
Why use Ruby's attr_accessor, attr_reader and attr_writer?

Related

Devise view specs

I'm used to writing view specs which check at least for something like:
expect(view).to receive(:title).with('Required page title here')
(title is a helper method I wrote to set the page title.) Now I'm trying to write specs for my Devise views which look something like:
- title 'Lost Password'
.row
.col-lg-6
= form_for resource, as: resource_name, url: password_path(resource_name) do |f|
= render 'layouts/errors', object: resource
.form-group
= f.label :email
= f.text_field :email, class: 'form-control', autofocus: true
= f.submit 'Send me the instructions', class: 'btn btn-primary'
%hr
= render 'devise/shared/links'
.col-lg-6
= render 'devise/shared/advantages'
resource and resource_name are defined by Devise.
If I run the following spec on the view:
require 'rails_helper'
describe 'devise/passwords/new', type: :view do
it 'sets the page title' do
expect(view).to receive(:title).with('Lost Password')
render
end
end
It says: undefined local variable or method 'resource'. I tried:
allow(view).to receive(:resource).and_return(User.new)
but then I get [snip, long class definition] does not implement: resource.
How do I make this work? I don't want to use Capybara for something as trivial as this.
You can provide these helpers yourself, by writing a simple implementation in your test helpers:
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end

Is it safe to edit user based on devise current_user helper?

I'm using Devise, Omniauth-twitter and Omniauth-facebook for my rails app authentication and i had to make my own controller for editing user parameters without needing a password for users with providers like facebook and twitter.
And instead of routing the user to his profile by his user id, I used the devise helper current_user to show and edit the current_user parameters
My question is.. is it safe to do that ?
I'm a beginner.. so when something is done that easy i worry about security vulnerabilities. Here's my code.
profile_controller.rb
class ProfileController < ApplicationController
before_action :authenticate_user!
def show
#user = current_user
end
def edit
#profile = current_user
end
def update
#profile = current_user
if #profile.update(profile_params)
redirect_to profile_path(#profile)
else
render 'edit'
end
end
private
def profile_params
params.require(:user).permit(:username,:first_name,:last_name,:gender)
end
end
routes.rb
get'profile' => 'profile#show'
get'profile/edit' => 'profile#edit'
patch'profile/edit' => 'profile#update'
edit.html.erb
<%= form_for #profile, url: {action: "edit"} do |f| %>
<%= f.text_field :username, autofocus: true %>
<%= f.text_field :first_name, autofocus: true %>
<%= f.text_field :last_name, autofocus: true %>
<%= f.text_field :gender, autofocus: true %>
<%= f.submit "Sign up" %>
<% end %>
Well if you are using Devise you could make user of their existing views rather than, you trying to implement them on your own. But, I don't see any security threats with your current approach it's just that it is a waste of time.
Take a look at the devise documentation and check the Configuring Views section,
https://github.com/plataformatec/devise

devise: update user on another controller without password

how can I update my user without the required password, password confirmation and current password?
I'm trying to update the user outside devise controller, my form is working with this helper:
module ContentHelper
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
and my form for editing:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, multipart: true }) do |f| %>
<%= devise_error_messages! %>
<%= f.file_field :personal_file, placeholder: "Upload file" %>
<%= f.submit %>
<% end %>
With this its appearing that I can't have my password blank and redirecting to user edit page:
Password is too short (minimum is 8 characters)
Password can't be blank
Current password can't be blank
You can just write up your own controller that updates User as you wish. Something like
class UserController < ApplicationController
before_filter :authenticate_user!
def update
current_user.update(user_params)
end
private
def user_params
params.require(:user).permit(:personal_file)
end
end
would do the trick. You don't need to think of the current_user as some magic devise thing but instead use it as any other model you have.
If you anyway wish to use Devise controller to do this, you should see this answer.

how to call devise methods

i create controller:
class SigninController < ApplicationController
def index
end
def create
#user = User.find_by_email(params[:user][:email])
if #user
....
sign_in(:user, #user)
else
#user = User.new(params...)
#user.save
...
end
end
end
Correctly i call sign_in method?
signin/index.html.erb:
<h2>Sign in</h2>
<%= form_for(User.new, :as => :user, :url => "signin") do |f| %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<br>
<div><%= f.submit "Sign in" %></div>
<% end %>
How to call devise sign_in and sign_up methods of devise?
Try this in app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
def after_create_path_for(resource)
sign_in(:user, resource)
end
end
That's just kind of a guess. I don't know if it will work.
You may but in most cases you should not create custom controller to sign in / out user. Devise has sessions_controller which implements this functionality for you. View devise documentation and wiki, it has plenty of documentation. Also you can found out there how to customise views and controller behaviour.
Update
If need to place both forms (login and registrations) on one view you can customise devise view. Form actions may point to different standard controllers. I suggest to use users/registrations/new form for base form and add uses/sessions/new form to it.

Usage Rails 3.0 beta 3 without ActiveRecord ORM

Just installed Rails 3.0 beta 3 in Windows 7.
And started playing with some easy examples
class SignupController < ApplicationController
def index
#user = User.new(params[:user])
if method.post? and #user.save
redirect_to :root
end
end
end
class User
def initialize(params = {})
#email = params[:email]
#passw = params[:password]
end
def save
end
end
<div align="center">
<% form_for :user do |form| %>
<%= form.label :email %>
<%= form.text_field :email %><br />
<%= form.label :password %>
<%= form.text_field :password %><br />
<%= form.submit :Register! %>
<% end %>
</div>
When I go to /signup I'm getting this error
NoMethodError in
SignupController#index
You have a nil object when you didn't
expect it! You might have expected an
instance of Array. The error occurred
while evaluating nil.[]
Is there a problem with constructor or what's wrong?Please, need your help!
I just won't use ActiveRecord or any other ORM.
You need another action to handle the post, possibly it is called create. This is how I would revise your controller:
def index
#user = User.new
end
def create
#user = User.new(params[:user])
if method.post? and #user.save
redirect_to :root
end
end
The error possibly because when the index is displayed, params variable had no content.

Resources