authentication, cant go through login page - ruby-on-rails-3

im trying create backend under password from database and i cant go through login page :P
My AdminController
class Backend::AdminController < ApplicationController
layout :layout
before_filter :authorize, :except => :login
def authorize
if session[:backend] != true
redirect_to backend_login_path
end
end
private
def layout
if session[:backend] == true
"admin"
else
"login"
end
end
def login
employee = Employee.authenticate(params[:name], params[:password])
if employee
session[:backend] = true
redirect_to backend_root_path, :notice => "Logged in!"
else
flash.now.alert = "Invalid login"
end
end
def logout
session[:backend] = nil
redirect_to backend_login_path, :notice => "Logged out!"
end
end
My Routes
match "backend/login" => "backend/admin#login"
match "backend/logout" => "backend/admin#logout"
My Login page
<% flash.each do |name, msg| %>
<%= content_tag :div, msg, :id => "flash_#{name}" %>
<% end %>
<h1>Log in</h1>
<%= form_tag backend_login_path do %>
<p>
<%= label_tag :name %><br />
<%= text_field_tag :name, params[:name] %>
</p>
<p>
<%= label_tag :password %><br />
<%= password_field_tag :password %>
</p>
<p class="button"><%= submit_tag "Log in" %></p>
<% end %>
when i go on url /backend im redirected to /backend/login, thats right
when i enter good login or bad login nothing happened and NO flashes appears thats strange ?
from webserver console output, i see the POST informations go through login method.. i dont know what is wrong ? thank you
edit: when i try go to URL /backend/logout im getting
Unknown action
The action 'logout' could not be found for Backend::AdminController
i really dont understand this :( im begginer

one of the conventions in rails is, that public methods in a controller are considered to be actions.
if you want to write methods for before_filter etc, make them private or protected so that rails will not expose them as actions.
on the flipside, you are not able to define actions as private.

Related

The error message when trying to login from devise is not showing in a Rails 7.0 application

I have a rails 7.0 application, in my application.html.erb
<body>
<%= yield %>
<div class="signin-container">
<div class="signin-container-inner">
<%- flash.each do |name, msg| -%>
<%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
<%- end -%>
</div>
</div>
</body>
When I visit signin page and add some wrong email or password the error message is not showing.
Since Ruby on Rails 7 uses :turbo_stream, we need to make some modifications to get what you need.
First, we let's add a new parent controller for Devise:
# frozen_string_literal: true
# app/controllers/turbo_devise_controller.rb
class TurboDeviseController < ApplicationController
class Responder < ActionController::Responder
def to_turbo_stream
controller.render(options.merge(formats: :html))
rescue ActionView::MissingTemplate => e
if get?
raise e
elsif has_errors? && default_action
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
else
redirect_to navigation_location
end
end
end
self.responder = Responder
respond_to :html, :turbo_stream
end
Second, we also need to tell Devise to use our new controller and also add a class to handle our errors:
# frozen_string_literal: true
# app/config/initializers/devise.rb
# ! Create custom failure for turbo
class TurboFailureApp < Devise::FailureApp
def respond
if request_format == :turbo_stream
redirect
else
super
end
end
def skip_format?
%w(html turbo_stream */*).include? request_format.to_s
end
end
Devise.setup do |config|
...
config.parent_controller = 'TurboDeviseController'
config.navigational_formats = ['*/*', :html, :turbo_stream]
config.warden do |manager|
manager.failure_app = TurboFailureApp
end
...
end
That's it.
More information about it: GoRails - How to use Devise with Hotwire & Turbo.js
Here is a workaround this issue that occurs when using Rails 7, Hotwire, Turbo, and Devise together. By passing data:{turbo: false} along with each Devise form, it can prevent Turbo from conflicting with the Devise authentication process. This should allow Devise to function properly while still using Hotwire and Turbo in your Rails application.
Here is an example of how you might use data:{turbo: false} on a Devise form:
<%= form_for(resource, as: resource_name, url: session_path(resource_name), data: {turbo: false}) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, autofocus: true %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Log in" %>
<% end %>
In this example, the data: {turbo: false} is being passed as an option to the form_for helper method. This tells Turbo to not apply any of its features to this form, which should prevent any conflicts with Devise's authentication process.

Best steps for trouble shooting rails app

What's the best (simplest) way to walk through MVC and check if everything is set up right?
I get a bit frazzled and I feel like there must be a really simple fix to error messages like these:
undefined method `invitations_path' for #<#<Class:0x00000105ad5cb8>:0x00000105820b30>
After adding small amounts of code to my app things break and I want to trouble shoot them myself.
Thanks for the tips!
EDIT
Perhaps troubleshooting the specific issue will lead way to a generalized approach,
Link_to is not linking Used <%= %> instead of <% %>.
The above error is generated when visting localhost:3000/invitation/new
view (in home/index.erb.html)
<% if #user.invitation_limit > 0 %>
<% link_to 'Send Invitations', new_invitation_path %>
(<%= #user.invitation_limit %> left)
<% end %>
view (in invitation/new.erb.html)
<%= error_messages_for :invitation %>
<% form_for #invitation do |f| %>
<p>
<%= f.label :recipient_email, "Friend's email address" %><br />
<%= f.text_field :recipient_email %>
</p>
<p><%= f.submit "Invite!" %></p>
<% end %>
controller
class InvitationController < ApplicationController
def new
#invitation = Invitation.new
end
def create
#invitation = Invitation.new(params[:invitation])
#invitation.sender = current_user
if #invitation.save
if logged_in?
Mailer.deliver_invitation(#invitation, signup_url(#invitation.token))
flash[:notice] = "Thank you, invitation sent."
redirect_to projects_url
else
flash[:notice] = "Thank you, we will notify when we are ready."
redirect_to root_url
end
else
render :action => 'new'
end
end
end
model
class Invitation < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
has_one :recipient, :class_name => 'User'
attr_accessible :recipient_email, :sender_id, :sent_at, :token
end
routes.rb
resources :home, :only => :index
resources :invitation
You can create request specs for each of your controller actions. Request specs follow the request all the way from the controller to rendering the view, and if there is an error it will show up in the request spec.
This may take time to set up, but will save you lots of time in the future, as you don't have to manually test every page when you want to roll out a new version of your website.

Rails - how do I show an Add Favourite button to users who are not logged in?

I'd like to display an "Add to Favourites" button to users that are not logged in so that they can see that logged in users can add posts (in this case) to their favourites.
Here's the current code:
routes.rb
resources :users do
resources :favourites
end
resources :favourites, only: [:create, :destroy]
posts_helper.rb (I'll move this application_helper once I get it working)
PostsHelper
def new_favourite
if signed_in?
return current_user.favourites.build
else
return Favourite.new
end
end
end
show.html.erb
<%= render :partial => 'shared/favourites/favourite_form', :locals => { :object => #post } %>
_favourite_form.html.erb
<% if signed_in? && current_user.favourited?(object) %>
<%= render partial: 'shared/favourites/unfavourite', locals: { object: object } %>
<% else %>
<%= render partial: 'shared/favourites/favourite', locals: { object: object } %>
<% end %>
_favourite.html.erb
<%= form_for(new_favourite, remote: signed_in?) do |f| %>
<div>
<%= f.hidden_field :object_id, :value => object.id %>
<%= f.hidden_field :object_type, :value => object.class.name.demodulize %>
</div>
<%= button_tag(:type => 'submit', :id => 'add_favourite') do %>
Add to Favourites
<% end %>
<% end %>
I want it to not do an ajax call (ie submit the add favourite form using a page reload) if the user is not signed in so that it will see the user is trying to access a protected page, will redirect them to login and upon success [when it redirects them back to where they were originally trying to go] it will add the favourite.
Can someone advise what I would need to change to make this possible.
EDIT:
Code updated to reflect current state.
After signing in it redirects back to No route matches [GET] "/favourites" because favourites are a nested resource under users.
EDIT 2:
The redirect works pretty much the same as in Rails Tutorial:
Redirect user to sign in page (signed_in_user):
http://ruby.railstutorial.org/book/ruby-on-rails-tutorial#code-correct_user_before_filter
Store location to redirect user back to:
http://ruby.railstutorial.org/book/ruby-on-rails-tutorial#code-friendly_forwarding_code
http://ruby.railstutorial.org/book/ruby-on-rails-tutorial#code-friendly_session_create
The only difference is that on my signin page I use a facebook omniauth link for the user to login but the redirection after logging in still works as expected.
Use a helper method instead of current_user.favourites.build?
<%= form_for(new_favourite, remote: true) do |f| %>
Helper:
PostsHelper
def new_favourite
if signed_in?
return current_user.favourites.build
else
return Favourite.new
end
end
end

Trying to create user with membership using Devise - Need help with controller

I have a standard devise installation, and I'm trying to add in functionality to add a user with a gym membership from an admin panel.
routes.rb
devise_for :users
resources :users
I am creating the user from the gym controller, so this is my gym action
def members
#gym = Gym.find(params[:id])
#user = User.new
#user.gym_users.build
#roles = Role.all
end
The gym_user is accepted in the user model
accepts_nested_attributes_for :gym_users
Then here is a portion of my form
<%= form_for #user do |f| %>
<%= f.label :email %><br />
<%= f.text_field :email %>
<% f.fields_for :gym_users do |builder| %>
<%= builder.label :item_id, "Membership Level" %><br />
<%= builder.collection_select(:item_id, #gym.membership_items, :id, :name, {:include_blank => true}) %>
<% end %>
<% end %>
What I'm having trouble with is my user controller where I actually create the user. This is the route for the membership page where the user is created
match 'gyms/:id/members' => 'gyms#members'
Finally, here's the create method on my users_controller
def create
#user = User.new(params[:user])
if #user.save
:notice => "User created successfully"
render :new
else
render :new
end
end
What I'm not sure is how to send back to that url when there is an error so that my model errors go with it, or redirect when it completes correctly.
I resolved this by making it an ajax call which eliminated the need for a redirect.

2 render templates (from devise) on one page (rails 3)

I've installed devise for my rails app, i can go to the sign in page or the sign up page. But I want them both on a welcome page...
So I've made a welcome_page_controller.rb with the following function:
class WelcomePageController < ApplicationController
def index
render :template => '/devise/sessions/new'
render :template => '/devise/registration/new'
end
end
But when i go to the welcome page i get this error:
NameError in Welcome_page#index
Showing /Users/tboeree/Dropbox/rails_projects/rebasev4/app/views/devise/sessions/new.html.erb where line #5 raised:
undefined local variable or method `resource' for #<#<Class:0x104931c>:0x102749c>
Extracted source (around line #5):
2: <% #header_title = "Login" %>
3:
4:
5: <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
6: <p><%= f.label :email %><br />
7: <%= f.email_field :email %></p>
8:
Does anybody knows a solution for this problem? Thanks in advance!
Does it have to do with the fact that it is missing the resource function? in the welcome_page controller? It's probably somewhere in the devise controller...?
Regards,
Thijs
Here's how I managed to did it.
I've put a sign up form in my home#index
My files:
view/home/index.html.erb
<%= render :file => 'registrations/new' %>
helper/home_helper.rb
module HomeHelper
def resource_name
:user
end
def resource
#resource = session[:subscription] || User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
def devise_error_messages!
return "" if resource.errors.empty?
messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
sentence = I18n.t("errors.messages.not_saved",
:count => resource.errors.count,
:resource => resource_name)
html = <<-HTML
<div id="error_explanation">
<h2>#{sentence}</h2>
<ul>#{messages}</ul>
</div>
HTML
html.html_safe
end
end
You need that part because Devise works with something called resource and it should be defined so you can call your registration#new anywhere.
Like that, you should be able to register. However, I needed to display errors on the same page. Here's what I added:
layout/home.html.erb (the layout used by index view)
<% flash.each do |name, msg| %>
# New code (allow for flash elements to be arrays)
<% if msg.class == Array %>
<% msg.each do |message| %>
<%= content_tag :div, message, :id => "flash_#{name}" %>
<% end %>
<% else %>
# old code
<%= content_tag :div, msg, :id => "flash_#{name}" %>
<% end %> #don't forget the extra end
<% end %>
I found this code here
And here's something I created: I saved my resource object if invalid in a session so that the user hasn't to fill every field again. I guess a better solution exists but it works and it's enough for me ;)
controller/registration_controller.rb
def create
build_resource
if resource.save
if resource.active_for_authentication?
# We delete the session created by an incomplete subscription if it exists.
if !session[:subscription].nil?
session[:subscription] = nil
end
set_flash_message :notice, :signed_up if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
else
set_flash_message :notice, :inactive_signed_up, :reason => resource.inactive_message.to_s if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords(resource)
# Solution for displaying Devise errors on the homepage found on:
# https://stackoverflow.com/questions/4101641/rails-devise-handling-devise-error-messages
flash[:notice] = flash[:notice].to_a.concat resource.errors.full_messages
# We store the invalid object in session so the user hasn't to fill every fields again.
# The session is deleted if the subscription becomes valid.
session[:subscription] = resource
redirect_to root_path #Set the path you want here
end
end
I think I didn't forget any code. Feel free to use whatever you need.
Also, you can add your sign in form in the same page (something like that:)
<%= form_for("user", :url => user_session_path) do |f| %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.submit 'Sign in' %>
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
<%= link_to "Forgot your password?", new_password_path('user') %>
<% end %>
Cheers !