Rails - modifying/duplicating default "show" - ruby-on-rails

Is there anyway that I can modify the behavior of the default "show" ?
Currently when a user clicks on a button the main site
<%= link_to "ADD TO CART", product, {:class => "btn btn-primary", :target => '_blank', :method => "get"} %>
The user will be automatically redirected to a page with the appropriate information.
Now I'm trying to create a slightly different page (mobile friendly) and I've created a mobile_show page that works if I access it directly.
My question is how should I modify the link_to so that it points to /mobile/products/id instead of the current products/id ?
Update (Extra info):
In products_controller.rb`
# GET /mobile
# GET /mobile.json
def mobile
#products = Product.current
respond_to do |format|
format.html # mobile.html.erb
#format.json { render json: #products }
end
end`
# GET /mobile/products/1
# GET /mobile/products/1.json
def mobile_show
#product = Product.find(params[:id])
#product.update_attribute(:is_active, false)
respond_to do |format|
format.html # mobile_show.html.erb
#format.json { render json: #product }
end
end
In routes.rb
match '/mobile' => 'products#mobile'
match '/cashback' => 'products#cashback'
match '/mobile/products/:id' => 'products#mobile_show'
p/s: I'm very new to rails and web-development in general

You should actually create a namespace or controller specifically for mobile so it doesn't break REST
eg
app/controllers/mobile/products_controller.rb
or
app/controllers/mobile_products_controller.rb
But to use your method, you might want to add a name to your route so you can use it
match '/mobile' => 'products#mobile', :as => "mobile"
Then change your
<%= link_to "ADD TO CART", mobile_path, {:class => "btn btn-primary", :target => '_blank', :method => "get"} %>

based on your existing routes, you can change your route to
match '/mobile/products/:id' => 'products#mobile_show', as: :mobile_product
then in your view
<%= link_to "ADD TO CART", mobile_product_path(product), {:class => "btn btn-primary", :target => '_blank', :method => "get"} %>
UPDATE: is it good practice?
This actually depends on what you want to achieve. If you want to create a mobile version for your app, I suggest you look at a responsive ui. If that is not possible, the right way to handle this is to create a different set of controllers that will handle mobile requests.
These controllers should be placed under a different namespace.
namespace :mobile do
resources :products
end
This would expect you to have an app/controllers/mobile/products_controller.rb and handle mobile requests here.

Related

How do you recreate a remote link with the same .js update in Ruby on Rails?

Basically I want to make a link able to be refreshed dynamically from "Favorited" to "Removed", and at the same time give the user the option to go go back by reclicking the new button. The action does happen though, because when I refresh the page the updated button shows. Why does clicking the "Favorite this Classroom" link not work? The "Remove this Classroom" link seems to work. Thanks for your help
favorites_controller.rb:
def create
current_classroom.add_to_favorites(#classroom)
current_classroom.save
respond_to do |format|
format.js { }
format.html {redirect_to #classroom}
end
end
def destroy
current_classroom.remove_from_favorites(#classroom)
current_classroom.save
respond_to do |format|
format.js { }
format.html {redirect_to #classroom}
end
end
favorites/create.js.erb
$("#favorite_classroom").html("<%= escape_javascript(link_to 'Remove the Classroom', classroom_favorite_path(#classroom), :remote => true, :method => :delete) %>");
classrooms/_classroom_details.html.erb
<div id="favorite_classroom">
<% if loggedin_user.favorite_classroom?(#classroom) %>
<%= link_to 'Remove this Classroom', classroom_favorite_path(#classroom), :remote => true, :method => :delete %>
<% else %>
<%= link_to 'Favorite this Classroom', classroom_favorites_path(#classroom), :remote => true, :method => :post %>
<% end %>
routes:
classroom_favorites POST /classrooms/:classroom_id/favorites(.:format) {:action=>"create", :controller=>"favorites"}
classroom_favorite DELETE /classrooms/:classroom_id/favorites/:id(.:format) {:action=>"destroy", :controller=>"favorites"}
Error when I click on the "Favorite this Classroom" link:
ActionView::Template::Error (No route matches {:action=>"destroy", :controller=>"favorites"
Thanks!
You are using double-quotes twice in your js file. You can't do that. You need to rewrite it like this -
$("#favorite_classroom").html("<%= escape_javascript(link_to 'Remove the Classroom', classroom_favorite_path(#classroom), :remote => true, :method => :delete) %>");
Notice now that Remove the Classroom is enclosed in single quotes rather than double.
In addition, your route is incorrect because you have nested favorites within classrooms. When you write the link, you need to add the #favorite object that you are trying to delete:
= link_to 'Remove Favorite', classroom_favorite_path([#classroom, #favorite]), :remote => true, :method => :delete
Now the route you are trying to access is valid. That should get rid of the error.
I don't think you need the favorite controller, what you need is favorite/unfavorite action for classroom controller. Here's how it might look
classroom_controller.rb
respond_to :html, :js
def favorite
# ... find classroom by id
# do your stuff
#classroom.favorite = !#classroom.favorite
respond_with(#classroom)
end
views/classrooms/favorite.js.erb
<%# the message should be oposite to favorite %>
<% msg = #classroom.favorite ? "Remove this Classroom" : "Favorite this Classroom" %>
$("#favorite_classroom").html("<%= escape_javascript(link_to msg, [:favorite, #classroom], :remote => true) %>");
Here's the trick with path helper, [:favorite, #classroom] should be transleted to /classrooms/:id/favorite. If it won't work then try favorite_classrooms_path(#classroom)
And finally add to your routes.rb
resource :classrooms do
member { get :favorite }
end
Looks like you need to reference 'favorite_id' in your link to match your routes.
In your case, would probably be:
classroom_favorite_path([current_classroom, #classroom])

Deleting from a namespace

I have setup a admin namespace in order to access models in the admin area: /admin/pages
However i have the following problem
i cant get the delete function to work under Admin::PageController for example or any of my models.
Does anyone know how to do this.
I have the following:
Admin::PageController I have the following
def destroy
#page = Page.find(params[:id])
#page.destroy
respond_to do |format|
format.html { redirect_to admin_pages_url }
format.json { head :ok }
end
end
Then on my page index file where i want a link to delete the record i have the following: (/admin/pages)
<%=link_to admin_page_path(page), :class => 'ico del' do %>
<%='Delete'%>
<% end %>
Does not seem to work. Anyone know how to get this to work?
you have missed :method option in link_to call
link_to 'Delete', admin_page_path, :confirm => 'Are you sure?', :method => :delete
or
<%=link_to admin_page_path(page), :class => 'ico del',:method => :delete do %>
<%='Delete'%>
<% end %>
The link_to helper defaults to a GET request unless you specify additional attributes to tell it how you want it to be handled.
In this case, you need to set some extra arguments:
<%=link_to "Delete", admin_page_path(page), :class => "ico del", :remote => true, :method => :delete %>
What actually happens in the background is the Rails UJS (unobtrusive javascript adapter) captures the click event and sends the request via AJAX. So you should see it hit your server with a POST (but it passes in _method => delete as well) to delete the object.
I'm also assuming you have your routes set up correctly. Something like:
namespace :admin do
resources :pages
end

Share a partial among different resources

I have the following bit of code in my views:
- if admin?
.meta
Administrator options:
= link_to 'Edit This Post', edit_post_path(#post)
|
= link_to 'Delete This Post', #post, :method => 'delete', :confirm => 'Are you sure?'
I find that I use this same basic snippet a lot, but sometimes with different resources, and sometimes with more than one resource on a page. I'd like to extract this into a partial to DRY it up, but I need to write is so it could work with local or instance variables for any model. For instance, I need it to work with:
#post, post, #page, page
How do you do that?
You can cleanly write your partial to be independent of the given object:
- if admin?
.meta
Administrator options:
= link_to 'Edit', [:edit, object]
|
= link_to 'Delete', object, :method => 'delete', :confirm => 'Are you sure?'
And then you could do something like
render :partial => 'shared/admin_link', :locals => { :object => #post }
You can commit a variable to a partial using the :object or the :collection options in the render method. The :object option passes a single object to the partial that can contain anything. The :collection option is used to pass an Array of Objects that must be from the same Class.
For example you have your #post and pass it to the partial like this
<%= render :partial => 'layouts/my_partial', :object => #post, :as => :my_local_var %>
Then you can access the object by calling my_local_var in the partial. Using :collection is slightly different but you can read it on your own here: http://guides.rubyonrails.org/layouts_and_rendering.html
A Problem will be the usage of one partial in different Classes because of the routes names. Then you cant use the edit_modelname_path(#instance_var) you must use the routes Hash { :controller => params[:controller], :action => 'edit' } in the link_to method!
// For Link creation wihout params[:controller] :
Its just an Idea I didnt do this before but you can try this for example:
You render the partial and use :object => #instancevar, :as => :my_local_var you can use the following line for dynamic link creation:
<%= link_to "link text", { :controller => my_local_var.class.to_s.pluralize.downcase, :action => 'edit', :id => my_local_var.id } %>
Then you dont depend on the params[:controller] but are able to link to the vars edit link.

How do properly delete this?

Something is seriously not adding up here.. My page just refreshes, nothing happens, it never touches any of my debuggers hanging out on all my methods except for index.
my html:
<%- for image in #images %>
<%= image.attachment_file_name %>
<%-# link_to_delete image, :url => destroy_image_admin_wysiwyg_path(image.id) %>
<%= link_to 'delete', { :url => destroy_image_image_path(image.id) },
#:confirm => 'Are you sure?',
:post => true
%>
<br />
<% end %>
my controller
def destroy_image
debugger
#img = Image.find(params[:id])
#img.destroy
respond_to do |format|
format.html { redirect_to admin_image_rotator_path }
end
end
My routes:
map.resources :images, :member => { :destroy_image => :post }
My disgusting hack that works that I will replace as soon as I find something better
I moved the action over to a simpler controller I built myself.
Changed my routes to :
admin.resources :wysiwygs, :member => { :destroy_image => :post }
Changed my html :
<%= link_to 'delete', :controller => "wysiwygs", :action => "destroy_image" %>
But when I clicked on the link..it brought up.. the show action ?? fffffffffuuuuuuu
I retaliated by just moving my action to the show action, and passing a hidden field in my html..
<%= link_to 'delete', :controller => "wysiwygs", :action => "destroy_image", :hidden_field => {:value => image.id} %>
def show
# this was previously in destroy_image
#img = Image.find(params[:hidden_field][:value])
#img.destroy
respond_to do |format|
format.html { redirect_to admin_image_rotator_path }
end
end
It seems you're going down the wrong path here. If a before_filter is blocking your action, figure out why. Use skip_before_filter :filter_name if the filter is not needed.
Don't use show actions or HTTP GET for deletes. Even if it works, it will confuse things down the road. Use a DELETE verb:
map.resources :images, :member => { :destroy_image => :delete }
pass it in the link helper:
<%= link_to "delete", destroy_image_image_path(image), :method => :delete %>
And use ImagesController#destroy_image to perform the action. Better yet, consider using the standard RESTful ImagesController#destroy which map.resources gives you for free.
Not sure what was wrong in the first place, but in your second, working solution, i think you should write your link_to as follows:
link_to 'delete', :controller => "wysiwygs", :action => "destroy_image", :id => image.id
That at least would send you to the correct action.
Depending on your routes, you will have to make this a method => :post or not.
Check your rake routes output, it will show you what are the possible routes, and also what names they got, which in turn you can use as a method (add _path or _url at the end). Then it should be even easier to write something like:
link_to 'delete', wysiwygs_destroy_image_path(image)
Good luck!
You're doing a POST but your resource says that :destroy_image is only available via GET. Try changing your route to:
map.resources :images, :member => { :destroy_image => :post }
Also, take a look at your link_to. The second parameter takes a URL, not a hash that has a :url key. As mentioned elsewhere, depending on your Rails version you may need :method => :post instead of :post => true. In Rails 2.3.8, you would want this line instead:
<%= link_to 'delete', destroy_image_image_path(image), :method => :post %>

Problem with Delete Link?

When I click on the delete link I created, it doesn't do anything (even the flash[:notice] part) in the controller. Am I not calling the .delete? part correctly? The POST part works as I can add tips.
Link:
<%= link_to "Delete", :controller => "/admin", :action => "tips", :id => t.id, :method => :delete, :confirm => "Are you sure?" %>
Admin Controller
def tips
#tips = Tip.all
if request.post?
tip = Tip.new(params[:geek_tips])
if tip.save
flash[:notice] = "Saved!"
redirect_to :action => "tips"
else
flash[:notice] = "Error!"
end
elsif request.delete?
tip = Tip.find_by_id(params[:id])
tip.delete!
flash[:notice] = "Delete Message"
redirect_to :action => "tips"
end
end
Design issues aside, I think that your :method option is being interpreted as a query param. Can you see "method" in the URL if you hover on the link?
If so, try...
<%= link_to "Delete", {:controller => "/admin", :action => "tips", :id => t.id}, :method => :delete, :confirm => "Are you sure?" %>
Note the braces around the part that defines the URL of the request.
Regarding the design: Any time you have multiple actions in one controller method there is probably a design issue. In this case, instead of using one admin controller method to do multiple tips actions I would consider making a dedicated tips_controller controller to map to your Tip model.
If you used RESTful routes, that is, in config.rb you set...
map.resources :tips
...then you could use the create and destroy methods in your tips_controller for creating and deleting your tips respectively.

Resources