I'm wondering if there is a more cleaner or elegant way of translating multiple routes to one controller action using Rails.
#routes.rb
get 'suggestions/proxy', to: 'suggestions#index'
get 'suggestions/aimee', to: 'suggestions#index'
get 'suggestions/arty', to: 'suggestions#index'
...
#suggestion_controller.rb
case request.env['PATH_INFO']
when '/suggestions/proxy'
#suggestions = Suggestion.all.where(:suggestion_type => 'proxy')
when '/suggestions/aimee'
#suggestions = Suggestion.all.where(:suggestion_type => 'aimee')
when '/suggestions/arty'
#suggestions = Suggestion.all.where(:suggestion_type => 'arty')
...
else
#suggestions = Suggestion.all
end
I've read this this post, but I kept getting errors when using it.
It's not a big deal if there's not a lot to be done here. I'm building a website on a video game I like playing called Dirty Bomb and there is a total of 19 mercenaries that need to be listed, so that's why I wanted a more cleaner way of doing this.
Thanks.
Absolutely there is. You can use a parameter directly in your route. Even further, you can then use that parameter directly in your query, rather than using a case statement.
#routes.rb
get 'suggestions/:type', to: 'suggestions#index'
# suggestions_controller.rb
def index
#suggestions = Suggestion.where(suggestion_type: params[:type])
end
It's always a better practice to base your controller actions after parameters, rather than doing any interpretation of the path or request objects.
Hope it works!
Related
I recently upgraded from rails 3 to rails 4 on one of our legacy apps, the problem is that rails 4 doesn't allow the same path name on two URLs even if they take a different number of arguments.
E.g. we used to do this:
get "object/:id/data/:dataid" => "object#data", as: :object_data
get "object/:id/data/:dataid/:extra" => "object#data", as: :object_data
but in rails 4 having two object_data_paths is not allowed. I'm wanting to accomplish the same thing as before. It seems like one method would be to call the second one something new, but use the same method, i.e.:
get "object/:id/data/:dataid/:extra" => "object#data", as: :object_data_extra
But this does seem like a worse solution than before. Any other ways I can do this? Thoughts on why we have to do this?
There are two solutions. The first is to use an optional path segment:
get "object/:id/data/:dataid(/:extra)" => "object#data", as: :object_data
The other is to wrap it in a helper:
get "object/:id/data/:dataid" => "object#data", as: :object_data_1
get "object/:id/data/:dataid/:extra" => "object#data", as: :object_data_2
# And in application_helpers.rb or somewhere similar
def object_data_path(id, dataid, extra=nil)
if extra
object_data_2_path(id, dataid, extra)
else
object_data_1_path(id, dataid)
end
end
I can't use any of the gems for creating clean Urls in rails. Instead I am rolling out my own implementation. I have created the following entry in routes.rb
match "/:slug" => "cleanurls#index"
Where cleanurl is a controller for handling all such requests. In the cleanurl controller:
class CleanurlsController < ApplicationController
def index
slug = params['slug']
url = Url.where(:slug => slug).first
case(url.url_type)
when 'profile'
user_id = url.id.to_i
#profile = Profile_info.getProfileDetails(user_id)
render '/profiles/index'
end
end
end
I have created the table urls which stores the slug,id (as relevant) and the type of page. Right now I have only the profile page to deal with but in the future I will have different types of pages with clean urls.
My first Question:
1) Is this implementation the right approach? And is this okay from a performance perspective given the tables have all the right indexes.
I am making the profile url like this:
def self.makeProfileUrl(id,name)
name = name.strip.titleize
extension = User.where(:name => name).count - 1
slug = name.split(" ").join("-")
if extension != 0
slug += "-#{extension}"
end
Url.create(:slug => slug, :id => id.to_i, :url_type => 'profile')
end
I am using extension to append a count in case their are users who share the same name.
Question:
Is this the right way to create the slug and ensure it being unique? Fetching the count of a name from the other table does not seem right.
Answering the question #1:
I don't know the details of what's your overall goal, but if you'd like
to have such URLs that are based on records from the database - then yes: it's
a good approach.
Answering question #2 (regarding slugs):
I'd rather use something much more elaborate and well tested like:
https://github.com/norman/friendly_id
My 50 cents about some other things:
Is this one of your first projects in Ruby/Rails? If so - congratulations! :)
I'm asking because I noticed that you're using camel case here and there...
Also:
user_id = url.id.to_i
Why do you call this #to_i method here? Did you set up this id as a string
or something?
Hope this helps
I am trying to get rid of some scope-prefixes I am currently using in my app.
At the moment my Routes look like this (simplified example):
scope 'p'
get ':product_slug', as: :product
end
scope 't' do
get ':text_slug', as: :text
end
which for example generates these paths:
/p/car
/t/hello-world
Now I want the paths to work without the prefixed letters (p & t). So I restrict the slugs to the existing database entries (which btw works great):
text_slugs = Text.all.map(&:slug)
get ':text_slug', as: :text, text_slug: Regexp.new( "(#{text_slugs.join('|')})"
product_slugs = Product.all.map(&:slug)
get ':product_slug', as: :product, product_slug: Regexp.new( "(#{product_slugs.join('|')})"
The problem:
This is a multi-tenant app which means that someones text_slug could be another ones product_slug and vice versa. That's why I have to filter the slugs by the current site (by domain).
A solution would look like this:
text_slugs = Site.find_by_domain(request.host).texts.all.map(&:slug)
get ':text_slug', as: :text, text_slug: Regexp.new( "(#{text_slugs.join('|')})"
But request isn't available in routes.rb and I everything I tried won't work.
The direct call to Rack::Request needs the correct env variable which doesn't seem to be present in Application.routes, otherwise this could work:
req = Rack::Request.new(env)
req.host
I really tried alot and am thankful for any hint!
You may be able to use advanced constraints for this: http://guides.rubyonrails.org/routing.html#advanced-constraints.
class SlugConstraint
def initialize(type)
#type = type
end
def matches?(request)
# Find users subdomain and look for matching text_slugs - return true or false
end
end
App::Application.routes.draw do
match :product_slug => "products#index", :constraints => SlugConstraint.new(:product)
match :tag_slug => "tags#index", :constraints => SlugConstraint.new(:tag)
end
BTW - You may run into problems with testing, but that's another issue...
I want to have urls like this:
www.example.com/topic1/...
www.example.com/topic2/...
www.example.com/topic3/...
And these should be served using the TopicController.
The values topic1, topic2, topic3, .. are coming from the table in the database (topics).
Is this possible?
What will my route look like then? These topics will be added ofcourse, it isn't something that is static in nature.
Try:
match '*a/' => 'topic#show' # assume the action is show
params[:a] will equal topic1 etc.
The closest solution I can think of would be to define a route such as
match "/topic/:name" => "topic#process_topic"
and the corresponding action in the TopicController
def process_topic
#topic = Topic.find_by_name(params[:name])
case #topic.name
when topic1
...
when topic2
...
end
end
Simple question - how do I get the path or full URL of the current action INCLUDING the query string?
I wish to save it to the session variable like so:
def show
#thingy = Thingy.find(params[:id])
session[:some_var] = current_url
...
end
At the moment I'm doing the following, but it seems a bit heavy-handed (especially the specifying of query string params individually):
def show
#thingy = Thingy.find(params[:id])
session[:some_var] = thingy_path(#thingy, :q1 => params[:q1], :q2 => params[:q2])
...
end
request.url is probably what you are looking for.
access params variable,it will give you query as well as controller and action.
By using request object you can dig more deeper if you want.