rails newbie - how to change querystring into route - ruby-on-rails

Am learning rails the way most do, by implementing a blog. I've just put tagging in and have got my article view to the point where it's displaying clickable tags when you display an article. The issue is that the links are coming out like this;
http://localhost:3000/articles?tagged_with=development
I would prefer not to have the querystring, and instead have something like;
http://localhost:3000/articles/tagged_with/development
I can't find anything relevant in the "routes inside out" guide on the rails site (lots of useful stuff in there, just not this!)
Complete code here:
https://github.com/mikeyhogarth/mikeyblog
Pertinant bits are;
the link in _article.html.erb:
<%= link_to tag, articles_path(:tagged_with => tag) %>
the articles index controller:
def index
if(params[:tagged_with])
#tag = params[:tagged_with]
#articles = Article.tagged_with #tag
else
#articles = Article.all
end
respond_to do |format|
format.html # index.html.erb
format.json { render json: #articles }
end
end
what is the rails best practice way of doing this? Do I need to implement a "tagged_with" action and create a helper or is there some rails routing magic that can sort this out in a jiffy?
EDIT: Eventually found the answer

Try adding something like this to your routes (untested):
match '/articles/tagged/:tagged_with' => 'articles#index', :as => :tagged_articles
Then:
link_to(tag, tagged_articles_path(:tagged_with=>"foobar"))

I found the answer eventually: I needed a "named route". If anyone else has this question, I just put this in my routes.rb file;
match "/articles/tagged_with/:tag" => "articles#index", :as => "articles_tagged_with"
then simply relpaced my "link_to" with this;
<%= link_to tag, articles_tagged_with_path(:tag => tag) %>,

Related

Rails Wicked Gem - Understanding routing

Okay, so I'm not really understanding nested routing in the wicked gem.
So far I have this. I'm not sure if everything is in the right folder or if I'm doing that right.
routes.rb
resources :events
resources :events do
resources :build, controller: 'events/build'
end
controllers/events_controller.rb
def create
#event = Event.new(event_params)
if #event.save
flash[:success] = "Event Created!"
redirect_to event_build_path(event_id: "event", id: #event.id)
# previously had redirect_to event_build_path without parameters)
else
render 'new'
end
end
controllers/events/build_controller.rb
class Events::BuildController < ApplicationController
include Wicked::Wizard
steps :details, :visibility
def show
#event = Event.find(params[:event_id])
render_wizard
end
end
views/build/details.html.erb
<%= form_for #event do |f| %>
#blab blah
<% end %>
I had the event_build_path without parameters at first and I had this error
No route matches {:action=>"show", :controller=>"events/build"} missing required keys: [:event_id, :id]
Had influence from this Rails wicked gem redirect with params but don't entirely understand the routing
I don't have an event_id set and I don't really understand how wicked keeps track of the step of via the id (or if its event_id).
As my object (event) is not created yet, what is "event_id" and the id at the end represent?
Not really an answer, but some clarifications. The thing you're trying to do is pretty hard, and requires a bunch of customizations to suit your own case. If you're not comfortable with wicked, or if that tutorial is nearly incomprehensible, it might be better to skip doing a wizard for now and come back and try it again in a month or so once you've had time to meditate on it.
Form
This is your wicked form
<%= form_for #event do |f| %>
#blab blah
<% end %>
Wicked works by doing two things, storing state in your url domain.com/build_pah/<step> and providing you with helper methods to easily manipulate the current state. Once you render the form you need to tell the browser where to submit info to when enter is pressed. Right now it is going to #event path, which isn't what we want. Instead we need to do something like:
<%= form_for #event, :url => wizard_path, :method => :put do |f| %>
<% end %>
This tells the form to go to the wizard_path url, this is a helper we provide. It also tells the form to submit using the PUT HTTP method, which should trigger your def update action inside of your Events::BuildController if it is set up correctly. On another note it doesn't look like Events::BuildController has an update action.
Event Controller
Your event controller looks fine, however you're redirecting
redirect_to event_build_path(event_id: "event", id: #event.id)
Wicked needs the id parameter to be the step you want to go to. So it should be:
redirect_to event_build_path(event_id: #event.id, id: :details)
or
redirect_to event_build_path(event_id: #event.id, id: Wicked::FIRST_STEP)
You can also get fancy and redirect to the index action which will do another redirect to the first step, but i always prefer being explicit.
Other Questions
Here is someone with a similar question: https://github.com/schneems/wicked/issues/141 take a look at their code, and their question. Try to understand what was wrong and how it was fixed. Then compare between what they're trying to do and what you're trying to do.
This question
It's hard to be more helpful without an explicit question. Breaking it down into I did this => I expected this => I got this instead , I tried to debug using this . Anywhoo, hope some of this was helpful. Maybe spin up another Rails example app and try to walk through my wicked tutorial in the readme, it will give you some more experience with what wicked does (and doesn't) do for you.

Rails: link_to calls custom method in controller

I am looking to use link_to to call a method in my controller. However, for some odd reason the route looks for the show method.
In my view:
<% #beverages.each do |beverage| %>
..
<%= link_to 'Archive', beverages_archive_path(:id => beverage.id) %>
..
<% end %>
In my config/routes.rb
match 'beverages/archive' => 'beverages#archive'
In my beverages_controller.rb
def archive
beverage = Beverage.find(params[:id])
respond_to do |format|
# format.html # show.html.erb
format.json { render json: beverage }
end
# beverage.update_attribute('archive', true)
end
When I click on the archive link in the view, the URL does change to: http://localhost:3000/beverages/archive?id=11, however I get the following error.
The error I get:
ActiveRecord::RecordNotFound (Couldn't find Beverage with id=archive):
app/controllers/beverages_controller.rb:46:in `show'
Any idea on what I am doing wrong? Your help is much appreciated!
PS. I also looked at Rails 3 link_to delete destory method calls show method?
but nothing seemed to work.
Have you tried this in your routes?
match 'beverages/:id/archive' => 'beverages#archive', as: :beverages_archive
This will create the beverages_archive_path method for you. Also, as you are looking for a specific beverage, use :id inside the route so that it knows where to take the id parameter from.
Apart from that, you can always tell a link specifically which controller and action to link to by doing:
link_to "Label", :controller => :my_controller, :action => :index
Taken from here: Ruby on rails 3 link_to controller and action
Use the other notation (not match) instead.
resources :beverages do
collection do
get :archive
end
end
Try this one out and let me know if something went wrong.
There's not quite enough information here to know why beverages_archive_path works in your app -- one problem is that your routes file does not define a name for your beverages#archive route. What you want is something like:
match 'beverages/archive' => 'beverages#archive', :as => :beverages_archive
or better yet use resourceful routing conventions like so:
resources :beverages do
collection do
get :archive
end
end
What's happening is that you have a beverages#show route that matches /beverages/:id, and this is what /beverages/archive matches (with :id => 'archive').

Rails - Add action to controller created by scaffold

I'm trying to add an action called rollback to controller.
As I've seen, the only things I should do is writting the new action:
def rollback
puts "ROLLBACK!"
respond_to do |format|
format.html # index.html.erb
format.json { render json: #components }
end
Modify the routes.rb file:
resources :components do
collection do
post :rollback, :as => 'rollback'
end
end
And calling the action from some view:
<%= link_to 'Rollback', rollback_components_path %>
But I get the following error:
Couldn't find Component with id=rollback
app/controllers/components_controller.rb:18:in `show'
That's because instead of going to rollback action, the controller thinks that we are trying to 'show' to component with id 'rollback'.
Something that it seems weird for me is that calling 'new' action rails uses new_component_path (without s, in singular), but if I write rollback_component_path it throws me an error and I cant see the view.
In your routes you require a POST, just clicking a link is by default a GET, so either write
resources :components do
collection do
get :rollback
end
end
and then the link_to will work as expected.
I am assuming the rollback operation is not idempotent, so a POST is semantically better in that case.
If you write your link as follows, then rails will create an inline form for you:
link_to 'Rollback', rollback_components_path, :method => 'post'
Hope this helps.
This will work
routes.rb
resources :components
match "components/rollback" => "components#rollback", :as => :rollback
In views
<%=link_to 'Rollback', rollback_path%>

Simple rails button question

Very noob question here.
I am trying to make a digg-like website and when they click a button I want a counter to go up, just like in digg. So I did:
<%=button_to("vote", :action => "vote")%>
and then in my controller I made a action:
def vote
#article = Article.find(params[:id])
#article.votes = #article.votes + 1
respond_to do |format|
format.html { redirect_to(#article.company) }
end
end
When I do that you I get the error:
No route matches {:action=>"agree", :controller=>"companies"}
What should I do?
In a terminal type "rake routes", then look at your routes to find what path you need to use to vote for an article.
Then use
<%= button_to "Vote", vote_path(:id => article.id) %>
Just change the "vote_path" to the path in your rake routes output.
If it's not already in your rake routes file, put something like this in
match "vote/:id" => "controler_name#vote", :as => :vote
Take a look at http://guides.rubyonrails.org/routing.html#adding-more-restful-actions

Question about URL friendly links

I am trying to get my urls to look like this:
example.com/posts/id_of_post/title_of_post
I have this in my controller:
match ':controller/:id/:link', :controller => 'posts', :action => 'show'
Say I have a list of posts.. how can I link to them?
<%= link_to 'Show', post %>
Just gives the usual /posts/id
On another note, at the minute I am making a url-friendly link when a post is created and storing it in the database. Would it be better to create on the fly? Is that possible/better?
I saw this in an answer to another question:
def to_param
normalized_name = title.gsub(' ', '-').gsub(/[^a-zA-Z0-9\_\-\.]/, '')
"#{self.id}-#{normalized_name}"
end
That would work if I could change the - to a /. Possible?
I recommend just doing this instead of the gsub stuff:
def to_param
"#{self.id}-#{title.parameterize}"
end
Downside is that if the title changes, the URL changes. Which is a downer.
So a lot of implementations will do
before_create :permanize
def permanize
permalink = title.parameterize
end
def to_param
"#{self.id}-#{permalink}"
end
This is what I did:
I added this to my post#create:
#post.link = (#post.title.parameterize)
I will give the user the option to edit the title for up to 5 mins after posting.
My route:
match "/posts/:id/:link" => "posts#show", :as => "story"
and my index view for posts
<%= link_to 'Show', story_url(post, post.link) %>

Resources