Rails 4 routing parameter that looks like a controller - ruby-on-rails

I have a Entity model with an entity_type attribute that can be Hospital or Clinic.
I would like to be able to refer to things in the abstract:
/entities
/entities/us
/entities/us/mn+md
And the specific:
/hospitals
/hospitals/us
/hospitals/us/mn+md
I'm having difficulty with the routing. I can't seem to get the entity_type parameter to work.
routes.rb:
get "/:entity_type/(:country_code/(:region_code))" => "entities#index", :constraints => {
:entity_type=>["entities","hospitals","clinics"],
:country_code=>/[a-zA-Z]{2}([\+\,][a-zA-Z]{2})*/,
:region_code=>/[a-zA-Z]{2}([\+\,][a-zA-Z]{2})*/
}
...
# remaining RESTful routes
resources :entities
resources :apps
The entities_controller#index method:
def index
#entities = Entity.all
# probably a better way to do this
#entities = #entities.by_type(params[:entity_type]) if ( params[:entity_type].present? && params[:entity_type]!='entities')
# location-specific; works as expected
#entities = #entities.for_country(params[:country_code]) if params[:country_code].present?
#entities = #entities.for_region(params[:region_code]) if params[:region_code].present?
end
Corresponding entity.rb method:
# probably a better way to do this
def self.by_type(entity_type)
return where("entity_type='#{entity_type.singularize.titleize}'") if entity_type != 'entities'
end

Changed:
:entity_type=>["entities","hospitals","clinics"]
to:
:entity_type=>/(entities|hospitals|clinics)/

Related

Caching .com files on the filesystem in Rails3

I have a route like "http://example.com/sites/example.com"
get "/sites/:domainname", :to => 'controller#action', :constraints => { :domainname => /.*/ }
It works until the cached page public/sites/example.com generated (instead of public/sites/example.com.html), at which time it asks the user to save a .com file. How would I make the cached page saved as, e.g. public/sites/example.com.html? Or possibly have a different workaround.
The current workaround is to force the html extension in routes:
get "/sites/:domainname.html", :to => "sites#show", :as => :site, :constraints => { :domainname => /.*/ }
this results in public/sites/example.com.html being saved in the cache.
Looking at the source for, caches_page automatically assumes the extension of the cache file should be the "extension" it finds when the path contains a period (.). This logic is actually in another method, page_cache_file:
def page_cache_file(path, extension)
name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
unless (name.split('/').last || name).include? '.'
name << (extension || self.page_cache_extension)
end
return name
end
If needed, you could probably override this method to operate slightly differently within your controller, or else use the default ActionController::Base implementation. Here's a possible example:
class MyController < ApplicationController
class << self
private
# override the ActionController method
def page_cache_file(path, extension)
# use the default logic unless this is a /sites/:domainname request
# (may need to tweak the regex)
super unless path =~ %r{/sites/.*}
# otherwise customize logic to always include the extension
name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
name << (extension || self.page_cache_extension)
return name
end
end
end

Colons as divider with Rails Route?

I would like to use colons as divider in my rails route url instead of forward slashes. Is it possible to do the this?
I'm after something like the following
match '*page_path/:title ":" *section_path ":" :section_title' => 'pages#show'
so for the url food/fruit/apples:cooking:pie:apple_pie would return the parameters:
:page_path = "food/fruit"
:title = "apples"
:section_path = "cooking:pie"
:section_title = "apple_pie"
Is this possible in rails?
Here's an approach :
match 'food/fruit/:id' => 'pages#show' # add constraints on id if you need
class Recipe < ActiveRecord::Base
def self.from_param( param )
title, section_path, section_title = extract_from_param( param )
# your find logic here, ex: find_by_title_and_section_path_and_section_title...
end
def to_param
# build your param string here,
# ex: "#{title}:#{section_path}:#{section_title}"
# Beware ! now all your urls relative to this resource
# will use this method instead of #id.
# The generated param should be unique, of course.
end
private
def self.extract_from_param( param )
# extract tokens from params here
end
end
then in your controller:
#recipe = Recipe.from_param( params[:id] )
note that the use of use the built-in to_param method is optionnal.

rails mongoid url shortner

I've started creating a model based solution for creating short URLs, but I'm wondering if it wouldn't be better to do it in it's own collection (using mongoid) build an index for the tokens between models then search? Or if there's a gem that exists instead of rolling my own solution.
Right now i'm using Mongoid::Token which generates a unique token (ie cUaIxBu) for the particular collection and then using an additional letter (->(c)UaIxBu) to figure how which controller to route the particular request to.
Any ideas or pointers?
In this example alternatedoma.in/cUaIxBu would point to realdomain.com/cities/1234
routes
get '/:id' => 'home#tiny_url', :constraints => { :domain => 'alternatedoma.in' }
controller
def tiny_url
case params[:id].slice!(0)
when 'f'
#article = Article.find_by_token(params[:id])
redirect_to feature_url(#article)
when 'c'
#city = City.find_by_token(params[:id])
redirect_to city_url(#city)
when 'p'
#place = Place.find_by_token(params[:id])
redirect_to feature_url(#place)
end
end
We're employing an almost identical system in an application that I'm currently working on - and it seems to be working out okay (so far!). The only thing I could think of, is that you could boil down your LoC, as well as easily adding support for other models (if required) in the future:
supported_models = {:a => Article, :c => City, :p => Place}
prefix = params[:id].slice!(0).to_sym
if supported_models.has_key?(prefix)
#obj = supported_models[prefix].find_by_token(params[:id])
redirect_to send(:"#{supported_models[prefix].name.underscore}_url", #obj)
end
Obviously, this would require your routing helpers to follow the the same naming as your models. I.e: Article > article_url, City > city_url, etc.

link to nested ressource in each

I have an Array of various ActiveRecord Objects which are Objects of different Models. One of them is called Team which is a nested ressource of Department:
resources :departments do
resources :teams
end
So when I use this in the array.each like this:
array.each do |element|
link_to element.name, element
end
It throws an error that team_path doesnt exist whats logical because of nested ressources the route is called department_team_path but I cant call this method absolutely because I also treat Objets of other Models in this each.
One posibility I see is to add a Route called team_path whih refers to Team#Show but thats not pretty and also bad for the SEO. Is there another better possibility to link to this and other models in one course?
array.each do |element|
if element.is_a?(Team)
link_to element.name, url_for([element.department, element])
else
link_to element.name, element
end
end
as per Rails Guides. Alternatively, you can use resources :departments, :shallow => true but like you mentioned that will give undesirable results for SEO.
try this:
link_to element.name, url_for(element)
I wrote my own methods to deal with this problem. They are located in ApplicationHeler
def nested_name(object)
routes = Rails.application.routes.named_routes.to_a
name = nil
routes.each do |r|
name = r[0].to_s if r[1].requirements[:controller] == object.class.to_s.downcase.pluralize && r[1].requirements[:action] == "show"
end
name
end
def nested_object(object)
name = nested_name(object)
parent = name.gsub("_", "").gsub(object.class.to_s.downcase, "")
if object.class.reflect_on_association(parent.to_sym)
return [object.send(parent), object]
else
return object
end
end
So I can do the following:
array.each do |element|
link_to element.name, nested_object(element)
end
It works pretty good for me now,...

Rails3 How can I use :params in named scope?

I'm trying to display a list of milestones for a particular order. (Orders have many milestones.)
In my orders model, I have this:
scope :open, lambda {
joins("join milestones on milestones.order_id = orders.id").
where("order_id = ? AND milestone_status = ?", :params[:order_id], true).
group("orders.id")
}
The problem I'm having is getting the current order ID to work - :params[:order_id] is clearly wrong.
In my routes I have this:
resources :orders do
resources :milestones
end
And my url is as follows:
http://127.0.0.1/orders/2/milestones
How is this possible? I have tested the scope by replacing with an order ID manually.
-- EDIT --
As per advice below, I've put the following in my milestones controller:
#orders = Order.open( params[:order_id] )
And in my view, I have this:
<% #orders.each do |open| %>
But I get an error:
wrong number of arguments (1 for 0)
The full stacktrace is here: http://pastie.org/2442518
Define it like this:
scope :open, lambda { |order_id|
joins("join milestones on milestones.order_id = orders.id").
where("order_id = ? AND milestone_status = ?", order_id, true).
group("orders.id")
}
And call it on your controller like this:
def index
#orders = Order.open( params[:order_id] )
end

Resources