Favorites model for my rails app - ruby-on-rails

I have a user and guideline model.
I would like a user to be able to mark a guideline as their favourite and then view a list of all their favourite guidelines.
It's not quite right. I'm not sure if the 'favourite' action is adding a favourite correctly; or if it is adding correctly it's not displaying correctly in the favourites view (so 'show' action may not be right)...
*CONTROLLER*guidelines_controller.rb
def favourite
type = params[:type]
if type == "favourite"
#guideline= Guideline.find_all_by_id(params[:guideline_id])
current_user.favourite_guidelines << #guideline
redirect_to :back, notice: 'You favourited #{#guideline.name}'
elsif type == "unfavourite"
current_user.favourite_guidelines.delete(#guideline)
redirect_to :back, notice: 'Unfavourited #{#guideline.name}'
else
# Type missing, nothing happens
redirect_to :back, notice: 'Nothing happened.'
end
end
*CONTROLLER*favourites_controller.rb
def show
#user = User.find_by_profile_name(params[:id])
if #user
#guidelines = #user.favourite_guidelines.all
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
end
*ROUTES*routes.rb
get "guidelines/favourite"
get "favourites/show"
*MODEL*user.rb
has_many :guidelines
has_many :favourite_guidelines
*MODEL*favourite_guidelines.rb
attr_accessible :guideline_id, :user_id
belongs_to :user
*VIEWS*guidelines/index.html.erb
<% if current_user %>
<%= link_to "favourite", guidelines_favourite_path(guideline, type: "favourite"), method: "get" %>
<%= link_to "unfavourite", guidelines_favourite_path(guideline, type: "unfavourite"), method: "get" %>
*VIEWS*favourites/show.html.erb
<% if #guidelines %>
<% #guidelines.each do |guideline| %>
<div class="well">
<%= link_to guideline.title, guideline %>
<br />
<%= guideline.content %>
<br />
Added <%= time_ago_in_words(guideline.created_at) %> ago
</div>
<% end %>
<% end %>

As per your comment following returns nil:
#guideline= Guideline.find_by_id(params[:guideline_id]) #=> nil
current_user.favourite_guidelines << nil #Gives association mismatch error
I think params[:guideline_id] is nil. Post your params from log file.
Or try this:
Change your link to like this:
<%= link_to "favourite", guidelines_favourite_path(guideline_id: guideline.id, type: "favourite"), method: "get" %>
<%= link_to "unfavourite", guidelines_favourite_path(guideline_id: guideline.id, type: "unfavourite"), method: "get" %>
In your earlier case:
#guideline= Guideline.find_all_by_id(params[:guideline_id]) #=> []
current_user.favourite_guidelines << [] #=> Valid and inserting nothing

Related

How can I redirect to the object in an array

I have action index:
def index
if params['type'] == 'random'
#objects = Object.order("RANDOM()").limit(1)
else
#objects = Object.all.limit(1)
end
end
and create action:
def create
object = Object.find(params[:object_id])
comment = object.comments.create(params[:comment].permit(:body))
respond_to do |format|
format.html
format.js #ajax
end
if comment.save
redirect_to root_path(params[:object_id]) #doesn't work
else
flash[:error] = comment.errors.full_messages[0]
redirect_to root_path(params[:object_id]) #doesn't work
end
end
I can comment an object in my index page. When I put a comment, I want to redirect to the object that was commented.
With my code, the page is reloaded, but the next object is displayed, and I cannot see the comment. How can I redirect to the same object?
My root_path
<span class="random-icon"><%= link_to icon('random'), "http://localhost:3000/?type=random" %></span>
<div class="inner-container">
<% #objects.each do |object| %>
<h1 class="title"><%= object.title %></h1>
<p class="obj"><%= object.body %></p>
<h3 class="comments-title">Comments:</h3>
<div id="comments">
<% object.comments.each do |comment| %>
<div class="comments"> <%= comment.body %>
<span class="time-to-now"><%= distance_of_time_in_words_to_now(comment.created_at) %> ago</span>
</div>
<% end %>
</div>
<div id="error"><%= flash[:error] %></div>
<%= form_for([object, object.comments.build], remote: true) do |f| %>
<%= f.text_area :body, class: "text-area" %>
<p class="char-limit">255 characters limit</p>
<%= f.submit "Comment", class: 'button' %>
<% end %>
<% end %>
</div>
If params['type'] is true, Object.order("RANDOM()").limit(1) will always br reevaluated and usually return a new object. To ensure you return to the same object, you might want to store it in session and then check first in your index if there is a liked comment in your sessions, if so, #objects = Object.find(session[:comment_object_id])
def index
if session[:comment_object_id]
#objects = Object.find(session[:comment_object_id])
session.delete(:comment_object_id) # delete the session after use
elsif params['type'] == 'random'
#objects = Object.order("RANDOM()").limit(1)
else
#objects = Object.all.limit(1)
end
end
def create
object = Object.find(params[:id])
comment = object.comments.create(params[:comment].permit(:body))
respond_to do |format|
format.html
format.js #ajax
end
if comment.save
session[:comment_object_id] = :object_id # set the session here
redirect_to root_path # should work now
else
flash[:error] = comment.errors.full_messages[0]
redirect_to root_path #should work now
end
end
This is pretty easy, and you're very close.
Inside your create method, you've got the object you want to redirect to. So just use it directly inside the if comment.save like:
redirect_to object_path(object)
You can get a list of all these path "helpers" via the command:
rake routes
And in that listing, you should see, by the way, that root_path does not accept any arguments ... for future reference.

Rails 4 : Table Boolean Column Update Using "link_to "with a specific value "TRUE" always

In my customer controller the update method code is like bellow:
def update
#customer= Customer.find(params[:id])
if #customer.update_attributes(customer_params)
redirect_to customers_path
else
render 'edit'
end
end
In my view in customers index page I am planning to add a "link_to" link, if it is clicked, then that particular customers field "doc_delete" should be updated with value "TRUE".
<td><%= link_to "[Update", *************what is here ?******** method: :put %></td>
You can pass hidden params through button_to:
<%= button_to "Update", user, method: :put, params: { doc_delete: true } %>
This will create a micro-form, much like what Marwen alluded to. Whilst quite inefficient, it will be the best way to send data to your update action.
--
Another, more efficient, way would be to define a custom route/action:
#config/routes.rb
resources :customers do
patch :doc_delete, on: :member #-> url.com/users/:id/doc_delete
end
#app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def doc_delete
#customer = Customer.find params[:id]
redirect_to customers_path if #customer.update doc_delete: true
end
end
#app/views/customers/index.html.erb
<% #customers.each do |customer| %>
<%= link_to "Update", customer_doc_delete_path(customer) %>
<% end %>
You will need a form to do that for you
<% unless customer.doc_delete? %>
<%= form_for customer do |f| %>
<%= f.hidden_field_tag :doc_delete, true %>
<%= f.submit %>
<% end %>
<% end %>
Where to insert this form?
Well if you are rendering you costumers using:
<%=render #costumers %>
then you will add the form in the /customers/_customer.html.erb
If you are looping them manually:
<% #customers.each do |customer| %>
<%=customer.full_name %>
## Here you can add the form
<% end %>
An another way, you can use Ajax.
#app/views/customers/index.html.erb
<% #customers.each do |customer| %>
<% if !customer.doc_delete == true %>
<%= link_to "Update", customer_doc_delete_path(customer), remote: true %>
<% else %>
<%= Updated %>
<% end %>
<% end %>
#config/routes.rb
resources :customers do
patch :doc_delete, on: :member #-> url.com/customers/:id/doc_delete
end
#app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def doc_delete
#customer = Customer.find params[:id]
if #customer.update doc_delete: true
respond_to do | format |
format.js {render :nothing => true}
end
end
end
end
In my index.html
<td>
<%= hidden_field_tag 'delete_present', :value => "present" %>
<%=link_to "[update]", customer_path(customer, :doc_delete => true), :method => :put, :confirm => "Are you sure?" %>
</td>
In my customer controller
def update
if params[:doc_delete].present?
#customer= Customer.find(params[:id])
#customer.doc_delete=true
#customer.save
redirect_to customers_path
else
#customer= Customer.find(params[:id])
if #customer.update_attributes(customer_params)
redirect_to customers_path
else
render 'edit'
end
end
end

Multiple update forms for one model

I want to have multiple forms on one page. Let's make an example to understand what I want:
I have a page for my admins, let's say it's the admins#show page. My admin has to change his name on one form on this page and on another form his age. I know I could create one form but I want to have multiple forms (because this is just an example). So my admins#show page looks something like this:
<%= form_for #admin do |a| %>
<%= a.label :name %>
<%= a.text_field :name %>
<%= a.submit "Submit name change" %>
<% end %>
<%= form_for #admin do |e| %>
<%= e.label :age %>
<%= e.number_field :age %>
<%= e.submit "Submit age change" %>
<% end %>
But in my controller, I don't know really how this works and here is my problem. I think I have something like this, but how could I divide the form inputs in the update method?:
def edit
#admin = Admin.find(params[:id])
end
def update
#admin= Admin.find(params[:id])
if #admin.update_attributes(:name=> admin_params1[:name])
redirect_to #admin
else
render 'edit'
end
if #admin.update_attributes(:age=> admin_params2[:age])
redirect_to #admin
else
render 'edit'
end
end
private
def admin_params1
params.require(:admin).permit(:name)
end
def admin_params2
params.require(:admin).permit(:age)
end
Its a bit Unorthodox what you are doing, but as you insisted and only its an example, I guess you can handle the update method by doing like this
def update
#admin= Admin.find(params[:id])
if params[:commit] == "Submit name change"
if #admin.update_attributes(admin_params1)
redirect_to #admin
else
render 'edit'
end
elsif params[:commit] == "Submit age change"
if #admin.update_attributes(admin_params2)
redirect_to #admin
else
render 'edit'
end
end
end
Note: Not Tested!
Well, I think you could create other non-REST methods in the controller and then add named routes in your config/routes then add your two different forms similar to this;
<%= form_for :admin_name, url: admin_name_path, method: :post do |a| %>
<%= a.label :name %>
<%= a.text_field :name %>
<%= a.submit "Submit name change" %>
<% end %>
<%= form_for :admin_age, url: admin_age_path, method: :post do |e| %>
<%= e.label :age %>
<%= e.number_field :age %>
<%= e.submit "Submit age change" %>
<% end %>
Then something like this;
def update_age
#admin = Admin.find(params[:admin_age][:id])
if params[:admin_age]
#admin.update_attributes(:age=> params[:admin_age][:age])
redirect_to #admin
else
render 'edit'
end
end
def update_name
#admin = Admin.find(params[:admin_name][:id])
if params[:admin_name]
#admin.update_attributes(:name=> params[:admin_name][:name])
redirect_to #admin
else
render 'edit'
end
end
** not tested for bugs

Custom Validation error messages not appearing in view

I have some custom validations in my model for when the user submits a URL using simple_form, but I can't seem to get the error message associated with each custom validation to show in the view (yet the validations seem to work)?
The only error I see is the one defined in the create method. Any guidance would be appreciated....
Model
class Product < ActiveRecord::Base
validates :url, presence: true
validate :check_source, :must_contain_product_id
def check_source
valid_urls = ["foo", "bar"]
errors.add(:url, "Must be from foo or bar") unless valid_urls.any? {|mes| self.url.include? mes}
end
def must_contain_product_id
errors.add(:url, "Must be product page") unless self.url.include? "productID"
end
end
Controller
def create
#product = Product.new
if #product.save
flash[:success] = "Product added to your list"
redirect_to root_path
else
flash[:message] = "Sorry we can't add this product"
redirect_to root_path
end
end
View (using Simple_form)
# Various messaging I've tried
<% if flash[:success].present? %>
<div data-alert class="alert-box success">
<%= flash[:success] %>
</div>
<% end %>
<% if flash[:error].present? %>
<div data-alert class="alert-box">
<%= flash[:error] %>
</div>
<% end %>
<% if flash[:message].present? %>
<div data-alert class="alert-box">
<%= flash[:message] %>
</div>
<% end %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
# The actual form...
<%= simple_form_for Product.new do |form| %>
<%= form.input :url, maxlength: false %>
<%= form.button :submit, "Get Product", data: { disable_with: "Retrieving product ..." } %>
<% end %>
I had the same issue solved using:
errors.add(:base, "message here")
However this won't be assigned to a specific form input (e.g url) which was not necessary in my case
You're redirecting to the root_path in the create action regardless of whether or not the save is successful. Your view for the Product form contains divs for the flashes, but unless the view for the root_path renders flash divs as well you won't see them.
I think you neet to redirect to products, or to edit. Otherwise you are unable to see the error, because you are not on the page where it should be displayed.
Try
def create
#product = Product.new
if #product.save
flash[:success] = "Product added to your list"
redirect_to root_path
else
flash[:message] = "Sorry we can't add this product"
render 'edit'
end
end
errors.add(:base, "message")
This is the correct syntax to add message in custom validations.
See your code you are redirecting to root_path if you redirecting means values and variable will not come to your partial.
so you have to change your code like,
def create
#product = Product.new
if #product.save
flash[:success] = "Product added to your list"
redirect_to root_path
else
flash[:message] = "Sorry we can't add this product"
render :action => "new"
end
end
after that in model Instead of
errors.add(:url, "Must be from foo or bar")
Please try like this:
errors[:base] << ("Must be from foo or bar")

Incorrect param submitting

I have a form for casting your vote for your favourite image.
<%= form_for(#imagevote) do |f| %>
<% #miniature.collections(:photo).each do |collection| %>
<% if collection.photo.exists? %>
<td><div class="photo1">
<%= link_to image_tag(collection.photo.url(:thumb), :retina => true), collection.photo.url(:original), :retina => true, :class => "image-popup-no-margins" %>
<%= f.radio_button(:collection_id, collection.id) %>
<%= f.hidden_field :voter_id, :value => current_user.id %>
<%= f.hidden_field :voted_id, :value => collection.user_id %>
<%= f.hidden_field :miniature_id, :value => #miniature.id %>
<p>Painted by <%= link_to collection.user.name, collection.user %></p>
</div></td>
<% end %>
<% end %>
<%= f.submit "Vote" %>
<% end %>
Everything submits correctly except for the hidden_field :voted_id which for some reason duplicates the current_user.id.
UPDATE
I've tried logging in as another user and it seems that :voted_id is not duplicating current_user.id but rather that it is always "7" which was the :user_id I was using to test it before. Now logged in as user number 4 it is still entering the :voted_id as 7. I'm lost.
The link to the imagevotes view is as follows:
<%= link_to "See more and change your vote.", edit_imagevote_path(:miniature_id => #miniature, :voter_id => current_user.id) %>
Here is my image votes controller
class ImagevotesController < ApplicationController
respond_to :html, :js
def new
#imagevote = Imagevote.new
#miniature = Miniature.find(params[:miniature_id])
end
def edit
#imagevote = Imagevote.find_by_miniature_id_and_voter_id(params[:miniature_id],params[:voter_id])
#miniature = Miniature.find(params[:miniature_id])
end
def create
#imagevote = Imagevote.new(imagevote_params)
if #imagevote.save
flash[:success] = "Vote registered"
redirect_to :back
else
flash[:success] = "Vote not registered"
redirect_to :back
end
end
def update
#imagevote = Imagevote.find(params[:id])
if #imagevote.update_attributes(imagevote_params)
flash[:success] = "Vote changed."
redirect_to :back
else
redirect_to :back
end
end
private
def imagevote_params
params.require(:imagevote).permit(:collection_id, :voter_id, :voted_id, :miniature_id)
end
end
You only have one #imagevote object, but you are outputting the hidden fields inside your collection loop so you will have multiple fields in the form referencing the same attribute on the model: if you check the html that is generated, you should see multiple hidden fields with the same name attribute.
The way that browsers handle multiple inputs with the same name means that the param that comes through for :voted_id will always be the :user_id from the last collection.
It's difficult to say because you didn't provide your model and your loop code stripped.
I would guess that you loop over collection that belongs to the current_user. And in this case you will have current_user.id always be the same as collection.user_id. May be you wanted to see collection.photo_id?

Resources