Creating a link to subscribe to a product? - ruby-on-rails

I need help on figuring how to make a link for my Product that enables users to subscribe to it. I first have my Subscription model:
class Subscription < ActiveRecord::Base
attr_accessible :subscribable_id
belongs_to :subscriber, :class_name => "User"
belongs_to :subscribable, :polymorphic => true
end
Then my Product model:
class Product < ActiveRecord::Base
attr_accessible :name, :price
belongs_to :user
has_many :subscriptions, :as => :subscribable
end
My plan is to make my view, similar to the DELETE method a link to click to subscribe to a product. Here is my routes, controller and then view:
resources :products do
post :subscribe_product, :on => :collection
end
ProductsController:
def subscribe_product
#product = Product.find(params[:id])
# Not sure what goes here next?
# Something like: user.subscriptions.create(:subscribable => product)
end
View:
<table>
<% for product in #products %>
<tbody>
<tr>
<td><%= product.name %></td>
<td><%= product.price %></td>
<td><%= link_to 'Delete', product, :confirm => 'Are you sure?', :method => :delete %></td>
<td><%= link_to 'Subscribe', :controller => "products", :action => "subscribe_product", :id => product.id %></td>
</tr>
</tbody>
<% end %>
</table>
Right now this gives a strange error:
ActiveRecord::RecordNotFound in ProductsController#show
Couldn't find Product with id=subscribe_product
Theirs 2 things,
Creating the method to subscribe.
Making the link correct.
How would I do these two?

By default link_to uses GET, so your router thinks you are trying to go to ProductsController#show with the first param being the ID
http://yoursite.com/products/subscribe_product/5
This is a get request to the products controller with an id param of subscribe_product.
If you pass :method => :post to your link_to helper, it will issue a post request, which is what your router is expecting.
<%= link_to 'Subscribe', :controller => "products", :action => "subscribe_product", :id => product.id, :method => :post %>
Without posting your user model, I can't know for sure, but the method will look like this:
#product.subscriptions.create(:user_id => user.id)
# user.id would be current_user.id, or whatever you are storing the current user as

Your subscribe_product path uses POST, so you'll want to change your link to use that method:
<%= link_to 'Subscribe', {:controller => "products", :action => "subscribe_product", :id => product.id}, :method => :post %>
Your action will probably look something like this:
#product.subscriptions << Subscription.new(:user_id => current_user.id)

Related

Removing ActiveStorage attachments in Rails 5

I have an app with products - each product has things like notes/FAQs/attachments.
I can delete the notes & FAQs successfully, but not the Active Storage attachments.
Could somebody please assist? I've tried using a separate method in the Products controller but that didn't work, so my current line is using an Uploads controller.
The current error I am getting is:
NameError in UploadsController#destroy
uninitialized constant Upload
Uploads controller:
class UploadsController < ApplicationController
load_and_authorize_resource :nested => :product
def destroy
#product = Product.find(params[:id])
#upload = #product.ActiveStorage::Attachment.find(params[:id])
#upload.purge
redirect_back(fallback_location: products_path)
end
end
Products view:
<% #product.uploads.each do |upload| %>
<% if can? :destroy, upload %>
<td><%= link_to t('X', :default => t("X")),
product_upload_path(#product, upload),
:method => :delete,
:data => { :confirm => t('.confirm', :default => 'Are you sure you want to delete this attachment?') },
:id =>'delete-faq' %></td>
<% end %>
<% if upload.variable? %>
<span><%= image_tag upload.variant(resize: "100x100"), class: "other-image" %></span>
<% elsif upload.previewable? %>
<span><%= link_to image_tag(upload.preview(resize: "100x100"), class: "other-image"), rails_blob_path(upload), target: "_blank" %></span>
<% else %>
<span><%= link_to image_tag("paper.jpg", size: "100x100", class: "other-image"), rails_blob_path(upload), target: "_blank" %></span>
<% end %>
<% end %>
routes:
resources :products do
resources :notes
resources :faqs
resources :uploads
end
I know this is not the most RESTful solution but I got it working, so am adding this as an answer in case it helps anybody else tearing their hair out.
I have checked the logs and it does remove both the attached image and the blob.
In my case, a product has_many uploads.
routes.rb:
resources :products do
resources :uploads do
match '/remove', to: 'products#remove', via: 'delete'
end
end
products controller (yes, I know, putting it here is not ideal)
def remove
#product = Product.find(params[:product_id])
#upload = #product.uploads.find(params[:upload_id])
#upload.purge
redirect_to product_path(#product), notice: "Upload was successfully removed."
end
products show view:
<% #product.uploads.each do |upload| %>
<%= link_to "X", product_upload_remove_path(#product, upload),
:method => :delete,
:data => { :confirm => t('.confirm', :default => 'Are you sure you want to delete this upload?') },
:id =>'delete-faq', :class => 'delete' %></td>
<% end %>
Hopefully this will help others or give a foundation for a more RESTful solution.

Rails, How to delete an association in has_many through

I Have a scenario, which I am not able to figure out,
Profile Product
customer1 Iphone
customer2 Iphone
Now customer1 sells his iphone, so i will have to delete the association between customer1 and iphone but not the profile or product. I have an intermediate table for many to many relationship called product_profiles with product_id and profile_id as fields. Hence i need to delete id's from product_profiles table.
Product model
has_many :product_profiles
has_many :profiles, :through => :product_profiles
Profile model
has_many :product_profiles
has_many :products, :through => :product_profiles
Product controller
def destroy
if ProductProfile.where(:product_id => params[:id]).where(:profile_id => ?).destroy //how to get the profile_id from view when clicked on delete button
redirect_to admin_products_path
flash[:success] = "Product Successfully Deleted!"
else
flash[:error] = "Sorry! Could not complete the request, please try again!"
redirect_to :action => 'index'
end
end
View file
<% #products.each do |b| %>
<tr>
<td><%= #profile.name %></td>
<td>
<% b.sub_categories.each do |p| %>
<%= p.name + ',' %>
<% end %>
</td>
<td><%= b.name %></td>
<td>
<%= link_to "<span class='glyphicon glyphicon-edit'></span>".html_safe, edit_admin_product_path(b) %>
</td>
<td>
<%= link_to "<span class='glyphicon glyphicon-trash'></span>".html_safe, admin_product_path(b), :method => :delete, :title => "Delete Brand", "data-confirm" => "Do you really want to delete?" %>
</td>
</tr>
<% end %>
product = Product.find_by_id(product_id)
product.product_profiles.delete(product_profile_id)
Delete is not going to destroy your objects only the association between them.
Source:
http://neyric.com/2007/07/08/how-to-delete-a-many-to-many-association-with-rails/
You can pass parameters using the second argument of the path helper.
<%= link_to "<span class='glyphicon glyphicon-trash'></span>".html_safe, admin_product_path(b, :profile_id => #profile.id), :method => :delete, :title => "Delete Brand", "data-confirm" => "Do you really want to delete?" %>
In your controller you would then be able to access params[:profile_id]

How do I route to a new rails controller action

I'm new to Rails and have the encountered this problem.
I have a books_controller where I have defined a new action called download:
def download
#book = Book.find(params[:id])
return send_file #book.get_filepath, :type => 'application/pdf', :filename => #book.get_filename
end
My routes.rb is like:
resources :books
match '/books(/download(/:id))', :controller => "books", :action => "download"
I would like to create a URL like /books/download/10 to call the download action.
I'm not sure how I can create that link. I have tried reading the Rails Guide on routing, but am very confused by it.
I have tried
<td><%= link_to books_download_path(book) %></td>
and it clearly doesn't work.
undefined method `books_download_path' for #<#<Class:0x682ac40>:0x482cad8>
I'd appreciate any help on this.
Thanks
P.S. Maybe /books/10/download makes more sense than /books/download/10
index.html.erb
<table>
<% #recent_books.each do |book| %>
<tr>
<!-- <td><%= image_tag(book.get_thumbnail) %></td> -->
<td><%= truncate(book.get_title, :length => 30) %></td>
<td><%= book.get_author %></td>
<td><%= book.get_summary %></td>
<td><%= truncate(book.get_filename, :length => 30) %></td>
<!-- <td><%= link_to 'Download', book %></td> -->
<td><%= link_to "Download", download_book_path(book) %></td>
</tr>
<% end %>
<table>
routes.rb
Caliberator::Application.routes.draw do
resources :authors
resources :books do
get :download, :on => :member, :to => 'books#download'
end
end
For Rails 3, try this
resources :books do
get :download, :on => :member, :to => 'books#download'
end
Now, in your views, you can use.
<%= link_to 'Download', download_book_path(book) %>
This will generate books/10/download type links.
Try following
match '/books(/download(/:id))', :controller => "books", :action => "download", :as => "books_download"
Ref:- naming_routes
You have to do it like this :
<%= link_to "Download", download_books_path + "/#{book.id}" %>
For rails 3, you should write:
resources :books do
member do
get 'download'
end
end

How to set up routes correctly

I can untrash all the messages checked in index view without any problem.
But in show view, the link generated show the link to /messages/discard.3
this 3 could be ID.
How can I make it work? it should be linked to messages/discard/3
My files are like these
routes.rb
get "messages/received"
get "messages/sent"
get "messages/trash"
get 'messages/:id' => 'messages#show', :as => :show_messages
match 'messages/new/:username', :to => 'messages#new', :as => :new_messages
match 'messages/deliver' => 'messages#deliver', :via => :post
match 'messages/discard' => 'messages#discard', :via => :post, :as => :discard_messages
match 'messages/untrash' => 'messages#untrash', :via => :post
view 1 (index.html.erb)
<%= form_tag(:action=> discard) do %>
<% #messages.each do |m| %>
<tr>
<td><%= check_box_tag "checked_items[#{m.id}]",m.id %></td>
<td><%= m.last_message.id %></td>
<td><%= 'unread' if m.is_unread?(current_user) %></td>
<td><%= m.last_message.created_at %></td>
<td><%= m.last_sender.username %></td>
<td><%= link_to m.subject, show_messages_path(m) %></td>
</tr>
<% end %>
view 2 (show.html.erb)
<%= link_to 'Trash', discard_messages_path(#messages) %>
messages_controller.rb
def discard
conversation = Conversation.find_all_by_id(params[:checked_items].keys)
if conversation
current_user.trash(conversation)
flash[:notice] = "Message sent to trash."
else
conversations = Conversation.find(params[:conversations])
conversations.each { |c| current_user.trash(c) }
flash[:notice] = "Messages sent to trash."
end
redirect_to :back
end
You can try change it to:
match 'messages/discard/:id' => 'messages#discard', :via => :post, :as => :discard_messages
However I think what you should do is to change your all routes to:
resource :messages do
member do
#code
end
collection do
#code
end
end
It's easier to read and maintain

Action method is :id with link_to?

When I try to subscribe to a product by clicking the link:
<%= link_to "Subscribe", :controller => "products", :action => "subscribe_product", :id => product.id, :method => :post %>
I get this error and notice the parameters are wrong.
ActiveRecord::RecordNotFound in ProductsController#show
Couldn't find Product with id=subscribe_product
{"id"=>"subscribe_product", "method"=>"post"}
My subscribe_product method in my ProductsController is:
def subscribe_product
#product = Product.find(params[:id])
#product.subscriptions.create(:subscriber_id => current_user.id)
end
My route:
resources :products do
post :subscribe_product, :on => :collection
end
These are the associations:
class User
has_many :products
has_many :subscriptions, :foreign_key => :subscriber_id
class Product
belongs_to :user
has_many :subscriptions, :as => :subscribable
class Subscriptions
belongs_to :subscriber, :class_name => "User"
belongs_to :subscribable, :polymorphic => true
Users subscribe in another controller:
PagesController
def index
#product_history = current_user.products
end
end
pages/index.html.erb
<% for product in #product_history %>
<%= product.name %>
<%= product.price %>
<%= link_to "Subscribe", :controller => "products", :action => "subscribe_product", :id => product.id, :method => :post %>
<% end %>
So why is my action method being seen as the ID instead?
Try :
resources :products do
post :subscribe_product, :on => :member
end
It will generate routes like :
subscribe_product_product POST /product/:id/subscribe_product(.:format) {:action=>"subscribe_product", :controller=>"products"}
and use path like in view :
subscribe_products_path(product.id)
Since you're passing an id, the subscribe_product route should be a member route. Try this, and let me know what you get:
resources :products do
member do
post 'subscribe_product'
end
end
In the controller (to get around non-mass-assignable attributes):
def subscribe_product
#product = Product.find(params[:id])
subscription = Subscription.new
subscription.subscriber_id = current_user.id
#product.subscriptions << subscription
end
Please try this. Change your route to :
resources :products do
post :subscribe
end
Then change your link like :
<%= link_to "Subscribe", subscribe_products_path(:id => product.id), :method => :post %>

Resources