0

I have resources :tags in my routes.rb.

So when I go to /tags/ios, it shows me the correct Tag#Show view.

What I would like to happen is when the user goes to /tags/iosit shows it as /ios and likewise I want that generated path to always be /ios (and not have the leading /tags).

Here is an example of how I am rendering that link, within an each block:

<%= link_to "#{tag.name}", url_for(tag) %>
tybro0103
  • 48,327
  • 33
  • 144
  • 170
marcamillion
  • 32,933
  • 55
  • 189
  • 380

3 Answers3

0

well the problem here is namespace

if you define a route like

get "/(:slug)" => "Tags#show"

that will basically match anything where its a valid tag or not. The way I would do it would be

in your routes file:

  begin  
    Tag.all.each do |t|
      begin
        get "#{t.slug}" => "Tags#show"
      rescue
      end
    end
  rescue
  end

Then in your tags controller you can

 def show
    slug = request.env['PATH_INFO']
    @tag = Tag.find_by_slug(slug)
  end

My answer comes from another answer I wrote that you may be interested How to create app-wide slug routing for Rails app?

Community
  • 1
  • 1
j_mcnally
  • 6,928
  • 2
  • 31
  • 46
  • I am not using a slug. Just the `name` attribute of `Tag`. – marcamillion Mar 29 '13 at 20:10
  • That's a neat way to do it. What's with all the `rescue`s though... do you not trust your own code? – tybro0103 Mar 29 '13 at 20:19
  • This seems a bit heavy handed and inefficient, no?. On each request, I will be doing a `Tag.all.each`. In my case, I have 1300+ tags, so I can see that being pretty ugly, really quick - or am I missing something? – marcamillion Mar 29 '13 at 20:20
  • 1
    In production the routes are, for lack of a better word, compiled. That would only happen once when you start the server. – tybro0103 Mar 29 '13 at 20:21
  • you can probably remove the rescues, in a sense i don't trust my own code. For instance routes are loaded when you run rake for some reason, so rake db:migrate could fail when first deploying to a new server since t.slug and the tag table would not exist – j_mcnally Mar 29 '13 at 22:35
  • I think I found the best solution for me. Simple and elegant. I wrote it as an answer. – marcamillion Mar 29 '13 at 22:53
0

In your routes.rb

resources :tags
match '/:tag_name' => 'tags#show'

Then in your tags_controller#show action you have access to the tag name via:

params[:tag_name]

Be sure to put that at the end of your routes file, as it will catch everything. Also, since it will catch everything you should render a 404 if it's not a valid tag name:

def show
    unless @tag = Tag.where(name: params[:tag_name]).first
        raise ActionController::RoutingError.new('Not Found')
    end
end

To redirect /tags/ruby to /ruby:

match "/tags/:tag_name" => redirect("/%{tag_name}")

http://guides.rubyonrails.org/routing.html

tybro0103
  • 48,327
  • 33
  • 144
  • 170
  • When I am on a URL like this: `http://localhost:3000/tags/ruby-on-rails`, this is what the params looks like: `Request parameters {"action"=>"show", "controller"=>"tags", "id"=>"ruby-on-rails"}` – marcamillion Mar 29 '13 at 20:19
  • if you want it to match that, then simply use `match '/:id' => 'tags#show'` – tybro0103 Mar 29 '13 at 20:20
  • Ok....perfect...so now `/tags/ruby-on-rails` goes to the same place as `/ruby-on-rails`. But...the path generated in each link is still `/tags/ruby-on-rails`. Also...if the user goes to `/tags/ruby` I want the URL in the address bar to read `/ruby`. So I don't just want a match, but basically a URL re-write and the same in the `url_for` paths that are generated. – marcamillion Mar 29 '13 at 20:26
  • Updated the answer with the redirection part. Not sure about making `link_to @tag` work correctly off the top of my head. Make another post on here for that part. – tybro0103 Mar 29 '13 at 20:38
  • I even tried `match "/tags/:id" => redirect("/%{id}")`, but that doesn't work. I actually am not really looking for an official `redirect`. More like a route mask or something like that. – marcamillion Mar 29 '13 at 22:00
  • the issue here is doing anything else besides tags and conflicting with the "capture" group. What if i also want a page: /:page_name – j_mcnally Mar 29 '13 at 22:36
  • I think I found the best solution for me. Simple and elegant. I wrote it as an answer. – marcamillion Mar 29 '13 at 22:53
0

So even though I got lots of nice suggestions from these guys, I found what I think is the best solution for me.

All I am trying to do is basically make /tags/ruby be rendered as /ruby both in the actual URL on the links of all the tags, and in the URL bar.

I don't want to do anything that will add load to my app.

This is the solution that works for me:

resources :tags, path: "", except: [:index, :new, :create]

Using path:, you can specify what you want the path to display as. i.e. what do you want to appear before your resource. So if you wanted your URLs to look like myhotness/tags/ruby, then you would simply do path: "myhotness".

I didn't want anything in my path, so I just left it blank.

For what it's worth, you can even add a constraint to that route, like so:

resources :tags, path: "", except: [:index, :new, :create], constraints: { :id => /.*/ }
marcamillion
  • 32,933
  • 55
  • 189
  • 380