Caching .com files on the filesystem in Rails3 - ruby-on-rails

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

Related

Rails 4 routing parameter that looks like a controller

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)/

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 3 - default_url_options based on url being generated

In rails 3, is it possible to gain access to the controller/action of the URL being generated inside of default_url_options()? In rails 2 you were passed a Hash of the options that were about to be passed to url_for() that you could of course alter.
E.g. Rails 2 code:
==== config/routes.rb
map.foo '/foo/:shard/:id', :controller => 'foo', :action => 'show'
==== app/controllers/application.rb
def default_url_options options = {}
options = super(options)
if options[:controller] == 'some_controller' and options[:id]
options[:shard] = options[:id].to_s[0..2]
end
options
end
==== anywhere
foo_path(:id => 12345) # => /foo/12/12345
However, in rails 3, that same code fails due to the fact that default_url_options is not passed any options hash, and I have yet to find out how to test what the controller is.
FWIW, the above "sharding" is due to when you turn caching on, if you have a large number of foo rows in your DB, then you're going to hit the inode limit on unix based systems for number of files in 1 folder at some point. The correct "fix" here is to probably alter the cache settings to store the file in the sharded path rather than shard the route completely. At the time of writing the above code though, part of me felt it was nice to always have the cached file in the same structure as the route, in case you ever wanted something outside of rails to serve the cache.
But alas, I'd still be interested in a solution for the above, purely because it's eating at me that I couldn't figure it out.
Edit: Currently I have the following which I'll have to ditch, since you lose all other named_route functionality.
==== config/routes.rb
match 'foo/:shard/:id' => 'foo#show', :as => 'original_foo'
==== app/controllers/application.rb
helpers :foo_path
def foo_path *args
opts = args.first if opts.is_a?(Array)
args = opts.merge(:shard => opts[:id].to_s[0..2]) if opts.is_a?(Hash) and opts[:id]
original_foo_path(args)
end
define a helper like
# app/helpers/foo_helper.rb
module FooHelper
def link_to name, options = {}, &block
options[:shard] = options[:id].to_s[0..1] if options[:id]
super name, options, &block
end
end
and then do the following in your view, seems to work for me
<%= link_to("my shard", id: 12345) %>
edit: or customize the foo_path as
module FooHelper
def link_to name, options = {}, &block
options[:shard] = options[:id].to_s[0..1] if options[:id]
super name, options, &block
end
def foo_path options = {}
options[:shard] = options[:id].to_s[0..1] if options[:id]
super options
end
end

Ruby on Rails: Execute Logic Based on Selected Menu

I have a class that I use to contain select menu options for property types. It works fine. However, I need to be able to verify the selection and perform specific logic based on the selected option. This needs to happen in my Ruby code and in JavaScript.
Here is the class in question:
class PropertyTypes
def self.[](id)
##types[id]
end
def self.options_for_select
##for_select
end
private
##types = {
1 => "Residential",
2 => "Commercial",
3 => "Land",
4 => "Multi-Family",
5 => "Retail",
6 => "Shopping Center",
7 => "Industrial",
8 => "Self Storage",
9 => "Office",
10 => "Hospitality"
}
##for_select = ##types.each_pair.map{|id, display_name| [display_name, id]}
end
What is the best way to verify the selection? I need to perform specific logic and display user interface elements based on each type of property type.
Since I am storing the id, I would be verifying that the id is a particular property type. Something like:
PropertyTypes.isResidential?(id)
Then this method would look like this:
def self.isResidential?(id)
##types[id] == "Residential"
end
But now I am duplicating the string "Residential".
For JavaScript, I assume I would make an ajax call back to the model to keep the verification code DRY, but this seems like over kill.
Do I need to manually create a verification method for each property type or can I use define_method?
This seems so basic yet I am confused and burned out on this problem.
Thanks
===
Here's my solution:
class << self
##types.values.each do |v|
# need to remove any spaces or hashes from the found property type
v = v.downcase().gsub(/\W+/, '')
define_method "is_#{v}?", do |i|
type_name = ##types[i]
return false if type_name == nil #in case a bogus index is passed in
type_name = type_name.downcase().gsub(/\W+/, '')
type_name == v
end
end
end
It sounds like you can benefit from some Ruby meta-programming. Try googling "ruby method_missing". You can probably do something quick & dirty along the lines of:
class PropertyTypes
def method_missing(meth, *args, &block)
if meth.to_s =~ /^is_(.+)\?$/
##types[args.first] == $1
else
super
end
end
end
On the ruby side you could also use something like this to define dynamically these methods:
class << self
##types.values.each do |v|
define_method "is_#{v}?", do |i|
##types[i] == v
end
end
end

Trying to understand what Base.rakismet_binding is for

What does this part . . .
unless Rakismet::Base.rakismet_binding.nil?
{ :referrer => 'request.referer', :user_ip => 'request.remote_ip',
:user_agent => 'request.user_agent' }.each_pair do |k,v|
data[k] = eval(v, Rakismet::Base.rakismet_binding) || ''
end
end
of the following method do?
module InstanceMethods
def spam?
data = akismet_data
unless Rakismet::Base.rakismet_binding.nil?
{ :referrer => 'request.referer', :user_ip => 'request.remote_ip',
:user_agent => 'request.user_agent' }.each_pair do |k,v|
data[k] = eval(v, Rakismet::Base.rakismet_binding) || ''
end
end
self.akismet_response = Rakismet::Base.akismet_call('comment-check', data)
self.akismet_response == 'true'
end
I found other references to rakismet_binding in rakismet.rb:
class Base
cattr_accessor :valid_key, :rakismet_binding
and controller_extensions.rb:
def rakismet(&block)
Rakismet::Base.rakismet_binding = binding
yield
Rakismet::Base.rakismet_binding = nil
end
private :rakismet
But I have no idea what it's for.
The Kernel binding is a special object holding the context of a method call including all instance variables.
What rakismet(&block) method does, is to temporary assign the current binding (the ActionController instance) to a class variable so it can be accessible by any rakismet method calls and execute the content of the block.
The following code fragment
unless Rakismet::Base.rakismet_binding.nil?
{ :referrer => 'request.referer', :user_ip => 'request.remote_ip',
:user_agent => 'request.user_agent' }.each_pair do |k,v|
data[k] = eval(v, Rakismet::Base.rakismet_binding) || ''
end
end
checks whether a binding is available and if so, it tries to automatically collect some information from the current binding such as the ActionController#request.referer, the ActionController#request.remote_ip and so on.
In a few words, this is a workaround to collect some variables from your current ActionController request that otherwise won't be available to Rakismet.
The last code fragment pretty much indicates its intention - its to be used in block form and wraps the current binding.
If you look at the some unit tests for this class:
http://github.com/jfrench/rakismet/blob/master/spec/models/model_extension_spec.rb?raw=true
You can see how its used.

Resources