Je suis nouveau sur Ruby on Rails et j'ai été énormément aidé par l'excellent livre de Michael Hartl: Ruby on Rails Tutorial. Je suis arrivé au chapitre 8 et je suis maintenant sur les exercices de ce chapitre. J'ai un problème (je suppose un "débutant" typique) avec l'exercice 1. Dans cet exercice, il est demandé "1 .Refactor le formulaire de connexion pour utiliser form_tag à la place de form_for. "J'ai essayé de chercher de l'aide avec ceci dans Stackoverflow, Google, Railscast et de nombreuses autres" recherches sur le Web "depuis deux jours maintenant et je ne semble pas trouver l'aide que je dois répondre à ce problème. Le fichier que j'essaye de modifier avec form_tag est ci-dessous:
<% provide(:title, "Sign in") %>
<h1>Sign in</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(:session, url: sessions_path) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Sign in", class: "btn btn-large btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>
</div>
J'utilise Rails 3.2.3 dans cette application. Quelqu'un peut-il m'orienter dans la bonne direction? Quelqu'un peut-il m'aider avec ce problème? Je serais très reconnaissant.
Voici l'implémentation qui utilise form_tag:
<% provide(:title, "Sign in") %>
<h1>Sign in</h1>
<div class="row">
<div class="span6 offset3">
<%= form_tag( url: sessions_path ) do %>
<%= label_tag :email %>
<%= text_field_tag :email %>
<%= label_tag :password %>
<%= password_field_tag :password %>
<%= submit_tag "Sign in", class: "btn btn-large btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>
</div>
J'utilise Rspec 2.9.0 et ci-dessous sont les tests qui échouent:
describe "signin page" do
before { visit signin_path }
it { should have_selector('h1', text: 'Sign in') }
it { should have_selector('title', text: 'Sign in') }
end
et
describe "with invalid information" do
before { click_button "Sign in" }
it { should have_selector('title', text: 'Sign in') }
it { should have_selector('div.alert.alert-error', text: 'Invalid') }
describe "after visiting another page" do
before { click_link "Home" }
it { should_not have_selector('div.alert.alert-error') }
end
end
et
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before do
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
it { should have_selector('title', text: user.name) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
it { should_not have_link('Sign in', href: signin_path) }
describe "followed by signout" do
before { click_link "Sign out" }
it { should have_link('Sign in') }
end
end
Voici mon fichier de routes:
SampleApp::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
get "users/new"
root to: 'static_pages#home'
match '/signup', to: 'users#new'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
match '/help', to: 'static_pages#help'
match '/about', to: 'static_pages#about'
match '/contact', to: 'static_pages#contact'
end
Je viens de terminer cet exercice également, donc je ne suis en aucun cas un expert; cependant, c'est le code qui a fonctionné pour moi et a passé tous les tests:
../app/views/sessions/new.html.erb
<% provide(:title, "Sign in") %>
<h1>Sign in</h1>
<div class="row">
<div class="span 6 offset 3">
<%= form_tag sessions_path do %>
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
<%= label_tag :password %>
<%= password_field_tag :password %>
<%= submit_tag "Sign in", class: "btn btn-large btn-primary" %>
<% end %>
<p>New User?<%= link_to "Sign Up Now", signup_path %> </p>
</div>
</div>
J'ai également dû changer le ../app/controllers/sessions_contoller
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user] = user.id
sign_in user
redirect_to user
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
sign_out
redirect_to root_path
end
end
Bien que cela fonctionne, je ne sais pas exactement pourquoi cela fonctionne; si quelqu'un pouvait expliquer pourquoi les changements dans le contrôleur sont nécessaires, ce serait très apprécié. Je sais que cela pourrait être posé comme une question distincte, mais elle est étroitement liée à OP et je suis sûr que nous trouverions tous les deux extrêmement utile pour comprendre non seulement comment faire en sorte que cela fonctionne, mais pourquoi cela fonctionne de cette façon. Voici les fichiers de vue et de contrôleur d'origine:
'Form_for' new.html.erb d'origine:
<% provide(:title, "Sign in") %>
<h1>Sign in</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(:session, url: sessions_path) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Sign in", class: "btn btn-large btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>
</div>
et le sessions_controller d'origine:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_to user
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
sign_out
redirect_to root_path
end
end
Les guides RoR expliquent comment fonctionne form_tag.
Je me suis fait prendre en ne nommant pas les champs correctement. Lorsque vous affichez la source de l'original, il vous montre le schéma de dénomination.
<%= form_tag(sessions_path) do %>
<%= label_tag 'session_email', 'Email' %>
<%= text_field_tag 'session[email]' %>
<%= label_tag 'session_password', 'Password' %>
<%= password_field_tag 'session[password]' %>
<%= submit_tag "Sign in", class: "btn btn-large btn-primary" %>
<% end -%>
Vous disposez d'un Session_Helper que vous n'utilisez pas dans votre implémentation.
MODIFIEZ vos méthodes d'assistance.
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
session[:user_id] = user.id
end
def sign_out
cookies.delete(:remember_token)
session[:user_id] = nil
end
Ensuite, vous pouvez simplement utiliser les méthodes appropriées dans votre contrôleur de session. Il s'agit d'une mise en œuvre plus soignée qui suit l'approche modulaire de la conception. Il semble également qu'il y ait des problèmes avec rspec.
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before do
fill_in "Email", with: user.email.upcase
fill_in "Password", with: user.password
click_button "Sign in"
end
it { should have_selector('title', text: user.name) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
it { should_not have_link('Sign in', href: signin_path) }
Même si tout est correct lorsque vous visitez la page, ces tests sont signalés. Je suppose que vous devez changer before do
parce que s'il faisait ce que vous voulez qu'il fasse, les tests passeraient.
Quoi qu'il en soit, je suis curieux de savoir quel est le problème avec rspec si quelqu'un a une idée!
Je pense que ce problème est avec user.email.upcase
. Vous n'avez pas besoin de .upcase
, lorsque vous le supprimerez, les tests passeront.