I'm trying to get the controller's "destroy" to work correctly and I'm wondering what the correct set up should be.
The error that I'm getting is
ActiveRecord::RecordNotFound in AuthenticationsController#destroy
Couldn't find Authentication without an ID
My controller looks like
class AuthenticationsController < InheritedResources::Base
def destroy
#authentication = current_user.authentications.find(params[:id])
#authentication.destroy
redirect_to(:back)
end
database table
create_table "authentications", :force => true do |t|
t.integer "user_id"
t.string "provider"
t.string "uid"
t.string "secret"
t.string "token"
end
I have tried other parameters such as :user_id
How can I get users to destroy their tokens? (with the option to re-authenticate later)
You're not passing id to controller
try
<%= link_to "Disconnect Your Authentication", {:controller=>'authentications', :action=>'destroy', :id=>current_user.authentication_id} %>
or use path helper with #autentication argument as option.
(You will need to edit your routes file)
If you're wanting to destroy all authentications for a user, you could certainly change your controller's destroy method to be:
def destroy
current_user.authentications.destroy_all
end
A more conventional approach would be to destroy a particular authentication. In that case the link_to method needs a path that includes an id parameter (which will end up as your params[:id] value in the controller). You can imagine a view snippet like the following that displays all a user's authentications, each with a destroy link:
<ul>
<% current_user.authentications.each do |a| %>
<li>
<%= a.provider %>
-
<%= link_to 'Disconnect Your Authentication', authentication_path(a), :method => :delete %>
</li>
<% end %>
</ul>
This assumes current_user is a helper and that your routes are set up on your authentication model. The authentication_path helper uses the a authentication instance to generate a path, complete with an id parameter.
Related
I am building my first useful Rails application for a teacher to post assignments and students to upload their work (jpeg|pdf) on the website using Carrierwave.
Tutorial I want to replicate: http://www.tutorialspoint.com/ruby-on-rails/rails-file-uploading.htm
Error: "`undefined method submits_path' for #<#:0x007f7260dbc7a0> Did you mean? submit_path"
Error message: Extracted source (around line #14):
13 <div class = "well">
14 <%= form_for #submits, html: { multipart: true } do |f| %>
15 <%= f.label :name %>
16 <%= f.text_field :name %>
17 <%= f.label :attachment %>
model: submit.rb
class Submit < ActiveRecord::Base
mount_uploader :attachment, AttachmentUploader # Tells rails to use this uploader for this model.
validates :name, presence: true # Make sure the owner's name is present.
end
controller: submit_controller.rb
class SubmitController < ApplicationController
def index
#submits = Submit.all
end
def new
#submit = Submit.new
end
def create
#submit = Submit.new(submit_params)
if #submit.save
redirect_to submits_path, notice: "The assignment #{#submit.name} has been uploaded."
else
render "new"
end
end
def destroy
#submit = Submit.find(params[:id])
#submit.destroy
redirect_to submits_path, notice: "The assignment #{#submit.name} has been deleted."
end
private
def submit_params
params.require(:submit).permit(:name, :attachment)
end
end
routes:
Rails.application.routes.draw do
resources :submit, only: [:index, :new, :create, :destroy]
get 'welcome/index'
get 'welcome/about'
root 'welcome#index'
end
schema:
ActiveRecord::Schema.define(version: 20160903040246) do
create_table "submits", force: :cascade do |t|
t.string "name"
t.string "attachment"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
rake routes :
Prefix Verb URI Pattern Controller#Action
submit_index GET /submit(.:format) submit#index
POST /submit(.:format) submit#create
new_submit GET /submit/new(.:format) submit#new
submit DELETE /submit/:id(.:format) submit#destroy
welcome_index GET /welcome/index(.:format) welcome#index
welcome_about GET /welcome/about(.:format) welcome#about
root GET / welcome#index
Thanks in advance to all the wonderful people here who are willing to lend a helping hand
You're using #submits insted of #submit, here:
<%= form_for #submits, html: { multipart: true } do |f| %>
Shoul be:
<%= form_for #submit, html: { multipart: true } do |f| %>
Which is the variable you have created in the controller, here:
def new
#submit = Submit.new
end
EDITED ANSWER
I'm gonna take a step back here and point some problems you're facing due to Rails Convention.
The Rails philosophy is Convention Over Configuration. That means that if you follow some conventions, Rails will be able to do what you expect it to do it without configuring anything (or almost anything).
Besides the error I pointed above, all the other errors you have listed in the answers are because you created your controller in the singular SubmitController instead of SubmitsController. This changes the convention expected by Rails, so you would have to some configurations by hand.
Since you're following a tutorial or something, my advice is to take a step back and re-create your controller with the right conventions.
I am using Rails 4 and have the following error.
Routing Error
No route matches [POST] "/logs/1/meals/13/edit
I’m passing form_for the model object using :meal and the edit page is rendering correctly. However, Rails does not seem to be checking whether or not the meal object has already been saved, so it keeps trying to send the form to the #create action and tries make a POST request instead of sending the form to the update action and making a PUT request when I hit submit.
How do I get the form_for to recognize that I am trying to update an existing object and that PUT is needed instead of POST? Everything else is working and I’ve run all of my migrations. I’m pretty new to Rails, and I’ve spent almost all day trying to figure this out on my own. Please help!
And just to note, when I tried to pass in the model object as #meal.log instead of :meal, Rails was no longer able to recognize :calorie_estimate or :meal_description. Passing the model object as #meal.log left me with a no method error.
meals/edit.html.erb
<h3> EDIT MEAL </h3>
<%= form_for(:meal) do |f| %>
<div id="meal-form">
<%= f.text_field :calorie_estimate, class: 'meal-form-fields', :placeholder => "Calorie Estimate" %>
<%= f.text_field :meal_description, class: 'meal-form-fields', :placeholder => "Food Description" %>
<div class="submit-form" style="width: 75px; height: 15px;">
<%= f.submit 'UPDATE', :class => 'submit-form-text' %>
</div>
</div>
<% end %>
meals_controller.rb
class MealsController < ApplicationController
include MealsHelper
def create
#meal = Meal.new(meal_params)
#meal.log_id = params[:log_id]
#meal.save
redirect_to log_path(#meal.log)
end
def edit
#meal = Meal.find(params[:id])
end
def update
#meal = Meal.find(params[:id])
#meal.update(meal_params)
redirect_to log_path(#log)
end
def meal_params
params.require(:meal).permit(:calorie_estimate, :meal_description)
end
end
possible routes:
Prefix Verb URI Pattern Controller#Action
root GET / logs#index
log_meals GET /logs/:log_id/meals(.:format) meals#index
POST /logs/:log_id/meals(.:format) meals#create
new_log_meal GET /logs/:log_id/meals/new(.:format) meals#new
edit_log_meal GET /logs/:log_id/meals/:id/edit(.:format) meals#edit
log_meal GET /logs/:log_id/meals/:id(.:format) meals#show
PATCH /logs/:log_id/meals/:id(.:format) meals#update
PUT /logs/:log_id/meals/:id(.:format) meals#update
DELETE /logs/:log_id/meals/:id(.:format) meals#destroy
logs GET /logs(.:format) logs#index
POST /logs(.:format) logs#create
new_log GET /logs/new(.:format) logs#new
edit_log GET /logs/:id/edit(.:format) logs#edit
log GET /logs/:id(.:format) logs#show
PATCH /logs/:id(.:format) logs#update
PUT /logs/:id(.:format) logs#update
DELETE /logs/:id(.:format) logs#destroy
routes.rb
Rails.application.routes.draw do
root to: 'logs#index'
resources :logs do
resources :meals
end
end
schema.rb
ActiveRecord::Schema.define(version: 20160128205351) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "logs", force: :cascade do |t|
t.string "entry_date"
t.integer "calorie_goal"
t.string "notes"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "meals", force: :cascade do |t|
t.integer "calorie_estimate"
t.string "meal_description"
t.integer "log_id"
t.datetime "created_at"
t.datetime "updated_at"
end
end
The issue is that you're using nested resources, hence you're confused about which #objects to pass to your form_for.
#app/views/meals/edit.html.erb
<%= form_for [#log, #meal] do |f| %>
As you have it presently, passing :meal is ambiguous - Rails cannot discern the route / method to send its submission to, as it doesn't have that data available.
If you wanted to update an object, you'll have to pass the appropriate data to the form, including the object's id:
<%= form_for :meal, url: { controller: "meals", action: "update", id: "5" }, method: :put do |f| %>
Such as Rails is object orientated, you'll be best passing the actual object to your form_for:
<%= form_for #meal ...
--
The issue you have is that you have a nested resource:
resources :logs do
resources :meals #-> url.com/logs/:log_id/meals/:id
end
This means you need to pass both the Log and Meal values to your form:
#app/controllers/meals_controller.rb
class MealsController < ApplicationController
def edit
#log = Log.find params[:log_id]
#meal = Meal.find params[:id]
end
def update
#log = Log.find params[:log_id]
#meal = Meal.find params[:id]
#meal.update meal_params
end
end
#app/views/meals/edit.html.erb
<%= form_for [#log, #meal] do |f| %>
If your controller is creating an instance variable called #meal you should use that instead of the symbol. So write:
form_for #meal do |f|
and then rails can query the instance variable to see whether it is a new_record? (in which case it will POST the data) or an existing record (in which it will b e a PATCH most likely).
To build a nested route, you will need to set an instance variable #log (I do not see that in your code, but you probably do that already), and then you can write:
form_for [#log, #meal] do |f|
which will calculate the correct path.
The error you get:
Routing Error
No route matches [POST] "/logs/1/meals/13/edit
indicates that your form does a POST instead of a PUT.
To make this work, just add method: :put to the form_for declaration:
<%= form_for(:meal, method: :put) do |f| %>
Having tried to access the 'answer1' attribute on my #results object via:
#results.answer1
...I looked online, and tried the solutions suggested here...
get attribute of ActiveRecord object by string
..but I can't seem to access the attributes of my passed ActiveRecord #results object.
Initially the user is directed to the /quizzes/new view, the QuizzesController#new action which looks like this:
def new
#user = current_user
#quiz_answer = current_user.quiz_answers.build
end
#quiz_answer is, therefore accessible to the view and passed into the form_for there. EDIT: Here is my form (a partial rendered as part of quizzes/new.html.erb):
<%= form_for(#quiz_answer) do |f| %>
<p>
<%= f.check_box(:answer1) %>
<%= f.check_box(:answer2) %>
<%= f.check_box(:answer3) %>
<%= f.check_box(:answer4) %>
<%= f.check_box(:answer5) %>
<%= f.check_box(:answer6) %>
<%= f.check_box(:answer7) %>
<%= f.check_box(:answer8) %>
</p>
<p>
<%= f.submit("Get my results!") %>
</p>
<% end %>
When the user clicks submit on the form the QuizAnswers#create action is triggered which redirects to results_path (the index action in the ResultsController).
#results is therefore accessible to the view because of the index action in ResultsController:
def index
# in order to access all the results in our view...
#results = current_user.quiz_answers
end
In my results/index view, this
<p><%= #results %></p>
outputs the following to the page:
#<QuizAnswer::ActiveRecord_Associations_CollectionProxy:0x5191b30>
...so the object is not nil.
But when I try to access the 'answer1' attribute of #results, via:
<p><%= #results[answer1] %></p>
OR
<p><%= #results.read_attribute(answer1) %></p>
...I get the following error:
undefined local variable or method `answer1' for #<#<Class:0x72384d8>:0x71b6d10>
Finally, in my routes.rb, I define the following reources:
resources :quizzes
resources :results
resources :quiz_answers
resources :users do
resources :posts
end
But when I include 'resources :quiz_answers' as part of 'resources :user' (immediately below the 'resources :posts' line) I get the following error:
undefined method `quiz_answers_path' for #<#<Class:0x5310618>:0x5411b80>
...when I go to the quizzes/new page.
So my question is: If quiz_answers needs to be a resource included as part of the user resource, how do I pass current_user.quiz_answers to form_for? And if it DOESN'T need to be 'part of' the user resource, how do I access the attributes of quiz_answers?
Once again, if there's anything I'm presuming or doing wrong, please feel free to explain the 'Rails way' of doing it.
EDIT
I think I've been asked for the models, controllers and migration, so here you go:
users controller:
class UsersController < ApplicationController
def show
#user = current_user
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
#user.update_attributes(user_params)
if #user.save
redirect_to(#user)
else
render 'edit'
end
end
private
# Using a private method to encapsulate the permissible parameters is just a good pattern
# since you'll be able to reuse the same permit list between create and update. Also, you
# can specialize this method with per-user checking of permissible attributes.
def user_params
params.require(:user).permit(:name, :age, :email, :section)
end
end
quiz answers controller:
class QuizAnswersController < ApplicationController
def new
#user = current_user
#quiz_answer = current_user.quiz_answers.build
end
def create
redirect_to results_path
end
private
def post_params
params.require(:quiz_answer).permit(:body, :user_id)
end
end
results controller:
class ResultsController < ApplicationController
def index
# in order to access all the results in our view...
#results = current_user.quiz_answers
end
end
schema.rb (let me know if this is what you need, migration-wise):
ActiveRecord::Schema.define(version: 20141002130233) do
create_table "posts", force: true do |t|
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
add_index "posts", ["user_id"], name: "index_posts_on_user_id"
create_table "quiz_answers", force: true do |t|
t.integer "user_id"
t.string "answer1"
t.string "answer2"
t.string "answer3"
t.string "answer4"
t.string "answer5"
t.string "answer6"
t.string "answer7"
t.string "answer8"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "quiz_answers", ["user_id"], name: "index_quiz_answers_on_user_id"
# Could not dump table "users" because of following NoMethodError
# undefined method `[]' for nil:NilClass
end
And the QuizAnswer model:
class QuizAnswer < ActiveRecord::Base
belongs_to :user
end
The User model is pretty long, but it DOES include:
has_many :posts
has_many :quiz_answers
I hope that helps!
All of the comments are perfectly valid i.e. you are setting an CollectionProxy to the variable #results but your main issue is that the create action in your QuizAnswersController does nothing except redirect to results_path.
Although Rails will do a lot of work for you, you have to process the parameters which are submitted by your form in the create action.
There's quite a lot in issue with your code here so I'd suggest you read this part of the Rails Guide on Action Controller. You'll also need to adjust the post_params method in your QuizAnswersController as you only permit the body and user_id attributes to be mass-assigned meaning you won't be able to do anything with the answer1 etc. attributes unless you assign them manually.
Having said that, what do they do in the form? As far as I can see, they will just be checkboxes setting a true or false value?
Your QuizAnswersController needs to look something like this.
class QuizAnswersController < ApplicationController
...
def create
#quiz_answer = current_user.quiz_answers.new(post_params) #from private method
if #quiz_answer.save
redirect_to results_path
else
#error handle here
end
end
private
def post_params
params.require(:quiz_answer).permit(:answer1, :answer2) #add other attributes here
end
end
EDIT: You say you are permitting a body attribute on the QuizAnswer model but that's not one of its attributes according to your DB schema so I've updated the code.
Since #results is a relation that includes all quiz_answers that belong to the current user:
#results = current_user.quiz_answers
your results/index.erb view will contain something like:
<%- #results.each do |quiz_answer| %>
<%# ... some code shows answer data like %>
<h4>Quiz Answer</h4>
<p><%= quiz_answer.answer1 %></p>
<p><%= quiz_answer.answer2 %></p>
<p><%= quiz_answer.answer3 %></p>
<p><%= quiz_answer.answer4 %></p>
<p><%= quiz_answer.answer5 %></p>
<p><%= quiz_answer.answer6 %></p>
<p><%= quiz_answer.answer7 %></p>
<p><%= quiz_answer.answer8 %></p>
<%- end %>
If no quiz_answers are assigned to the current user, that form will be skipped in output. And probably you should add name field to quiz_answer table then you can add it to above form as:
<p>Name: <%= quiz_answer.name %></p>
To assign the newly created quiz_answer add to create action inside the QuizAnswersController assignment to the current user, like follows:
class QuizAnswersController < ApplicationController
def create
quiz_answer = QuizAnswer.create(params.require(:quiz_answer).permit(:answer1)
current_user.quiz_answers << quiz_answer
redirect_to results_path
# require 'pry'
# binding.pry
end
end
After the quiz_answer.save! please make sure that the current_user.quiz_answers isn't empty by checking its #size. You can debug your it by using pry gem. Just add it to Gemfile, then insert to required place as a breakpoint, and uncomment the two lines with require, and binding.
I'm having trouble updating columns in my table with a button in Rails.
I've looked around stackoverflow for answers, but can't seem to get anything working...
In my app, I want users to be able to "rent" and "return" pins.
When they click the "Rent" button, I want it to change the pin's columns.
Example: rented: => true, renter_id: 1"
In my Pins controller:
def rent
#pin = Pin.update_attribute(:renter_id => #user.id)
end
In my View:
<%= button_to "Rent Now!", {:controller => "pins", :action => "rent", :renter_id => #user.id }, :class => 'btn btn-success btn-lg btn-block'%>
Schema:
create_table "pins", force: true do |t|
t.string "description"
t.string "link"
t.boolean "rented"
t.integer "renter_id"
end
One problem is that update_attribute is an instance method. But you are trying to call it as a class method on Pin. Perhaps do a controller like so.
def rent
#pin = Pin.find(params[:id])
if #pin.update_attributes(renter_id: #user.id)
#handle success
else
#handle failure
end
end
Also make sure that your routes are properly set up as a post request.
update_attribute takes 2 arguments, the attribute and the value
So it should be like this
update_attribute(:renter_id, #user.id)
If you want to update multiple attributes at once or if you want validations to be triggered, use update_attributes
update_attributes(:attr1 => value1, :attr2 => value2)
I followed railscast #250 Authentication from Scratch & got everthing wworking fine. Now I'm trying to only display edit & destroy links on my index page to admin user's.
I've set up mu User database with a admin boolean field & tried putting a simple if statement in the view of another model (hikingtrails) to only display certain links to admin users but I get this error when I try it out, undefined method 'admin?' for nil:NilClass
Database Schema
create_table "users", :force => true do |t|
t.string "email"
t.string "password_digest"
t.boolean "admin"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
User Model
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation#, :admin
validates :email, :uniqueness => true
has_secure_password
end
Application Controller
class ApplicationController < ActionController::Base
protect_from_forgery
# fetch the currently logged-in user record to see if the user is currently logged in
# putting this method in ApplicationController so that it’s available in all controllers
private
def current_user
# checks for a User based on the session’s user id that was stored when they logged in, and stores result in an instance variable
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
# to give access to this method from all the views, the helper_method makes it a helper method
helper_method :current_user
# basic authorization, user must be logged in!
def authorize
redirect_to login_url, alert: "You must be logged in to perform this action" if current_user.nil?
end
end
views/hikingtrails/index.html.erb
<% if current_user.admin? %>
<%= link_to t('.edit', :default => t("helpers.links.edit")),
edit_hikingtrail_path(hikingtrail), :class => 'btn btn-mini' %>
<%= link_to t('.destroy', :default => t("helpers.links.destroy")),
hikingtrail_path(hikingtrail),
:method => :delete,
:data => { :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')) },
:class => 'btn btn-mini btn-danger' %>
<% end %>
current_user will be nil if a user is not logged in according to your code. So you need to do this:
<% if current_user && current_user.admin? %>
or using the try method Rails adds to all objects.
<% if current_user.try(:admin?) %>
as Dogbert said, current_user will be nil if the user is not logged in.
I would suggest two other alternatives:
1) in the current_user method return a special type "guest" user instead of nil. Il will be useful in case you want to do something else with it later, for example in response to some user action.
As inspiration, look at how Ryan Bates explains the Ability class of his gem cancan: link.
The first thing he does is creating an unitilized (and not persisted in DB) user. An that Ability class will be instantiated each time Rails will parse an ERB template with that kind of user verification.
So, you could do:
def current_user
#current_user ||= ((User.find(session[:user_id]) if session[:user_id]) || User.new)
end
So, if (User.find(session[:user_id]) if session[:user_id]) returns nil, the #current_user will be set to an uninitialized User with no identity in DB.
2) define a new metod just to check if the user is an admin, for example:
# your unmodified current_user implementation
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def is_an_admin?
if current_user && current_user.admin?
end
So that you can use it in this way:
<% if is_an_admin? %>
<div>
<%= do stuff....%>
...It might be an extra method call, but it might also make your code more readable.
I know this is old, but if someone is googling the error as I did, there is actually no error in Rails Tutorial, but they forgot to highlight one thing they added.
Listing 9.54
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
Note that they added :destroy action here, not added before, which makes sure that the user is logged to perform destroy action and just then checks if he's an admin
before_action :admin_user, only: :destroy
Correction:
As of the time of this edit 12/14/2015, the rails tutorial now adds the :destroy action in Listing 9.53. If you miss that one as I did, you will get this error.
it looks like in your User model:
attr_accessible :email, :password, :password_confirmation#, :admin
admin is commented out, you need to delete the #.