Rails 4 use link_to or button_to to run method? - ruby-on-rails

I am implementing a simple voting system and by clicking on a button a +1 is added. For example, if a question has 5 votes, it will just increase. I have written the method already, but I am not sure how to execute it by clicking on a link_to. Do I need to reconfigure my routes?
questions_controller.rb
def self.ping
#question = Question.find(params[:id])
#question.increment!(:amplify)
render_to do |format|
if #question.save
format.html { redirect_to #question }
end
end
end
routes.rb
resources :questions
post '/ping' => 'questions#ping', as: 'ping'

Your routes will need to support an id:
post '/ping/:id' => 'questions#ping', as: 'ping'
Or better yet, if you want it to be scoped within the question:
resources :questions do
post '/ping' => 'questions#ping', as: ping
end
However, I don't think you want a class method ping in your questions_controller. I think you just want an instance method:
def ping
#question = Question.find(params[:id])
#question.increment!(:amplify)
if #question.save
render_to do |format|
format.html { redirect_to #question }
end
end
end
If that doesn't work, what errors do you see in the logs?

Further to CDub's answer, you'll likely benefit from a member route (2.10):
Routes
#config/routes.rb
resources :questions do
member do
post :ping
end
end
This should provide these routes:
http://yourapp.com/questions/:question_id/ping
View
This URL will only be accessible from POST, and would be best accessed using a link_to:
<%= link_to "+1", question_ping_path(question.id), method: :post %>
Controller
You don't need to declare class methods in a controller
#app/controllers/questions_controller.rb
def ping
#question = Question.find(params[:question_id])
#question.increment!(:amplify)
render_to do |format|
format.html { redirect_to #question }
end
end
.increment! saves the record :)

Related

Routing Error: No route matches [PUT] "/goals/4/edit"

I am trying to create a button to change a model record attribute from false to true. I'm using a form_tag as follows:
=form_tag edit_goal_path(goal), method: :post do
=hidden_field_tag :purchased, value: true
=submit_tag "Purchase"
It's haml, but feel free to post suggestions with ERB. I'm getting the following error:
No route matches [POST] "/goals/4/edit"
Rails.root: /home/ben/rails_projects/hartwig
However, I already have the following route from resources:
PUT /goals/:id(.:format) goals#update
My controller looks as following:
def edit
#goal = Goal.find(params[:id])
end
def update
#goal = Goal.find(params[:id])
if #goal.update_attributes(goal_params)
redirect_to '/goals', notice: "Update successful!"
else
render '/'
end
end
def goal_params
params.require(:goal).permit(:item, :description, :picture, :purchased)
end
How do I get this to work? Or is there a better way to solve this?
Your question says:
I am trying to create a button to change a model record attribute from false to true
so why are you using a form for it? I think a better approach would be to create a link or button that will call an ajax method or a normal method with post route and update your attribute. You can achieve it by following these steps:
a. Create a route for your custom action where you'll update your attribute:
post 'purchase_update/:id' => "goal#update_purchase", as: update_purchase #post as you want to send your goal id
b. create your custom method inside your controller:
def update_purchase
#goal = Goal.find(params[:id])
#goal.update_attribute(:purchased, true)
respond_to do |format|
format.html {redirect_to your_path, notice: 'purchase updated'}
format.js {} #if you want to do something by ajax
end
end
c. Create your link that will call this method:
=link_to "Purchase", update_purchase_path(#goal), method: post
and if you want to do it by ajax then
=link_to "Purchase", update_purchase_path(#goal), method: post, remote: true
Another solution to your problem could be adding a new method to the Goal Controller:
in goals_controller.rb
def purchase
#goal.update_attribute(:purchased, true)
end
and also add on top (just add :purchase)
before_action :set_goal, only: [:show, :edit, :update, :destroy, :purchase]
in routes.rb change to
resources :goals do
member do
post 'purchase'
end
end
to add a new post routes to your goals
now you will have a purchase_goal_path that you can use in your view like this:
link_to 'Purchase', purchase_goal_path(#goal), method: :post

ruby-on-rails - link with custom action on object

I have a list of objects and for each object I have 2 links - "Delete" and "Done". Like this:
1) Buy tomatoes; Delete | Done
2) Wash the car; Delete | Done
But my custom action done don't work. I need your help, how can I make my action and what code should I write in file routes.rb?
Controller:
def done
#task = Task.where(id: params[:id])
#task.done = true
redirect_to "/tasks"
end
Link in view file:
<%= link_to "Done", {:controller => :tasks, :action => :done, :id => task.id}%>
Thanks!
In your controller:
def done
#task = Task.find(params[:id])
#task.done = true
#task.save! # Use the 'bang' method or check the return value
redirect_to tasks_path
end
In routes.rb:
resources :tasks do
get :done, on: :member
end
In your view:
<%= link_to 'Done', done_task_path(task.id) %>
You are assigning the value but not saving the data. So save the data using #task.save
def done
#task = Task.where(id: params[:id])
#task.done = true
#task.save
redirect_to "/tasks"
end

Relating posts to topics in ruby on rails forum application (high level overview)

Building a forum in ruby for fun and to learn the language. To start this off, i understand basic constructs, but I am very new to server-side languages and am primarily a front-end developer. I am trying to extend my skills.
I don't necessarily want you to code for me (although code examples would be appreciated), but I would like you to explain to me why my code is terrible, which I'm sure it is and tell me how to fix it. Just need some help understand how to relate two models togethers and how to set up that relation in the controllers.
Thanks!
Here are my two models:
Post model:
class Post < ActiveRecord::Base
belongs_to :topic
end
Topic model:
class Topic < ActiveRecord::Base
belongs_to :user
has_many :posts
end
Now here come the controllers. These are where I am really lost. I got the topic creation working, and I tried to just copy what I did in the topic controller. I pretty much knew it wasn't going to work, but I am sorta lost. Here it is...
Topic Controller
class TopicsController < ApplicationController
def index
#topics = Topic.order("sticky desc")
end
def show
#topic = Topic.find(params[:id])
end
def new
#topic = Topic.new
end
def create
#topic = Topic.new(topic_params)
#topic.user = current_user
if #topic.save
redirect_to #topic, notice: "Created topic."
else
render :new
end
end
def edit
#topic = Topic.find(params[:id])
end
def update
#topic = Topic.find(params[:id])
if #topic.update_attributes(topic_params)
redirect_to topics_url, notice: "Updated topic."
else
render :edit
end
end
def destroy
#topic = Topic.find(params[:id])
#topic.destroy
redirect_to topics_url, notice: "Destroyed topic."
end
private
def topic_params
params.require(:topic).permit(:name, :post_content, :sticky)
end
end
Posts Controller
class PostsController < ApplicationController
def index
#posts = Post.order("sticky desc")
end
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
redirect_to topics_url, notice: "Post created."
else
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
if #post = Post.find(params[:id])
redirect_to topics_url, notice: "Updated post."
else
render :edit
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to topics_url, notics: "Post removed."
end
private
def post_params
params.require(:posts).permit(:content, :created_at, :updated_at)
end
end
I don't believe the views are an issue, and I will post a new question if there is once I get the controller logic figured out.
Again, any help would be appreciated. Please just no comments like, "you should really start back at the beginning", or "you aren't experienced enough, learn this first", because I know I am not experienced, hence why I am here asking you all.
You can show how you would code it, or just explain the logic that needs implemented to me, either is appreciated!
Thanks a ton everyone!
EDIT
I am getting a routing error actually. So obviously the routing is wrong, wasn't sure if it had something to do with the controller code tho: Here is the specific error. (this occurs when I try to click into a topic (I can edit and destroy topics, just not click into them)
Routing Error
No route matches {:action=>"new", :controller=>"posts"}
Try running rake routes for more information on available routes.
Here is my routes files so far:
Forum::Application.routes.draw do
get 'signup', to: 'users#new', as: 'signup'
get 'login', to: 'sessions#new', as: 'login'
get 'logout', to: 'sessions#destroy', as: 'logout'
resources :sessions
resources :topics do
resources :posts
end
resources :users
root to: 'topics#index'
end
Serve is a little Rack-based web server that makes it simple to serve ERB or HAML from any index. Serve is an optimal apparatus for building HTML models of Rails applications. Serve can likewise deal with SASS, Textile, and Markdown in the event that the suitable diamonds are introduced.
enter image description here

Generating the edit path for a nested resource referenced by multiple models

In routes.rb:
resources :cars do
resources :reviews
end
resources :motorcycles do
resources :reviews
end
In ReviewsController:
before_filter :find_parent
def show
#review = Review.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #review }
end
end
def edit
#review = Review.find(params[:id])
end
# ...
def find_parent
#parent = nil
if params[:car_id]
#parent = Car.find(params[:car_id])
elsif params[:motorcycle_id]
#parent = Motorcycle.find(params[:motorcycle_id])
end
end
Generating the "show" link for a Review is simply (this works):
= link_to "Show", [#parent, #review]
Similarly I would like to reference a generic edit path for a Review, something like (this does not work):
= link_to "Edit", [#parent, #review], :action => 'edit'
Does anyone know if this is possible or, if not, how this might be accomplished?
link_to 'Edit Review', [:edit, #parent, #review]
It turns out the answer I am looking for can be found with the URL helper "edit_polymorphic_path" (see: http://rubydoc.info/github/rails/rails/master/ActionDispatch/Routing/PolymorphicRoutes). In order to get the link I am attempting above I was able to accomplish this with:
edit_polymorphic_path([#parent, #review])
I think what you need here is a polymorphic assocation. Ryan Bates at Railscasts.com explains it perfectly.
http://railscasts.com/episodes/154-polymorphic-association
It will make it easy for you to have things like:
User, Manager, Note
A user can have many notes
A manager can have many notes
A note can belong to a user OR a manager
users/1/notes/edit
managers/1/notes/edit
The Railscast will explain how to do it :)
EDIT:
def edit
#reviewable= find_reviewable
#reviews= #reviewable.reviews
end
private
def find_reviewable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
Then in your link to, it would be something like:
link_to 'Edit Review', edit_review_path([#reviewable, :reviews])
^^ Not tested.

Rails 3 nested resources route error

I'm not being able to redirect the user to the page I want after he creates a new "service" resource.
Here's the routes.rb:
resources :wsps do
resources :services
end
The html form:
<%= form_for([#wsp,#service]) do |f| %>
Services_controller.rb:
def new
#wsp = current_wsp
#service = #wsp.services.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #service }
end
end
def create
#wsp = current_wsp
#service = #wsp.services.build(params[:service])
if #service.save
redirect_to wsp_service_path
end
end
The wsp_service_path goes to /wsps/1/services and the error:
No route matches {:action=>"destroy", :controller=>"services"}
What am I doing wrong? Why cant't I use "wsp_service_path"?
Thank you.
You can use wsp_service_path (while you should be use wsp_service_url since you are in a controller). All you are missing is arguments. wsp_service_path (or _url) are going to expect two arguments: a wsp and a service. Once you provide those two, it works.
redirect_to wsp_service_url(#wsp, #service)

Resources