user1051849
user1051849

Reputation: 2337

Rails 4 Route Parameters Defined by Keywords

We're trying to set up rails routes with the parameters separated by more then just forward-slash symbols.

As an example:

someexample.com/SOME-ITEM-for-sale/SOME-PLACE

For the following path we'd like to extract SOME-ITEM and SOME-PLACE strings as parameters whilst identifying which controller to run it all against with the "-for-sale/" part.

I've been playing with variations on :constraints => {:item => /[^\/]+/} constructs but without any success. Am I looking in the right place? Thanks!

UPDATE

In the end I went with this solution:

get ':type/*place' => 'places#index', as: :place , :constraints => {:type => /[^\/]+-for-sale/}

And then recovered the full "SOME-ITEM-for-sale" sting for parsing in the controller using

params[:type]

Hope that helps someone!

Upvotes: 1

Views: 253

Answers (2)

user1051849
user1051849

Reputation: 2337

In the end we went with this solution:

get ':type/*place' => 'places#index', as: :place , :constraints => {:type => /[^\/]+-for-sale/}

The router command only gets activated if the :type parameter contains "-for-sale" in the string

And then we recovered the full "SOME-ITEM-for-sale" sting for parsing in the controller using

params[:type]

Hope that helps someone!

Upvotes: 0

Richard Peck
Richard Peck

Reputation: 76774

friendly_id is what you want:


#Gemfile
gem 'friendly_id', '~> 5.1.0'

$ rails generate friendly_id
$ rails generate scaffold item name:string slug:string:uniq
$ rake db:migrate

#app/models/item.rb
class Item < ActiveRecord::Base
   extend FriendlyId
   friendly_id :name, use: [:slugged, :finders]
end

The above will give you a slug column, which FriendlyId will look up any requests you send to the app:

#config/routes.rb
resources :items, path: "" do
   resources :places, path: "" #-> url.com/:item_id/:id
end

Although the params will still be id (unless you use the param option of resources, but FriendlyId will override both your routes and model to use the slug instead:

<%= link_to "Item Place", items_place_path(@item, @place) %> #-> url.com/item-name-information/place-name-information

Update

If you wanted to have a "dynamic" routing structure, you'll be able to use the following (this requires the history module of FriendlyId):

#config/routes.rb
#...
get '/:item_id/:place_id', to: SlugDispatcher.new(self), as: :item #-> this has to go at the bottom 

#lib/slug_dispatcher.rb
class SlugDispatcher

  #http://blog.arkency.com/2014/01/short-urls-for-every-route-in-your-rails-app/
  ##########################################

  #Init
  def initialize(router)
    @router = router
  end

  #Env
  def call(env)
    id     = env["action_dispatch.request.path_parameters"][:item_id]
    slug   = Slug.find_by slug: id
    if slug
      strategy(slug).call(@router, env)
    else
      raise ActiveRecord::RecordNotFound
    end
  end

  ##########################################

  private

  #Strategy
  def strategy(url)
    Render.new(url)
  end

  ####################

  #Render
  class Render

    def initialize(url)
      @url = url
    end

    def call(router, env)
      item        = @url.sluggable_type.constantize.find @url.sluggable_id
      controller  = (@url.sluggable_type.downcase.pluralize + "_controller").classify.constantize 
      action      = "show"
      controller.action(action).call(env)
    end

  end

  ####################

end

This won't work out the box (we haven't adapted it for nested routes yet), but will provide you the ability to route to the appropriate controllers.

Upvotes: 1

Related Questions