How to urlize a link in Rails - ruby-on-rails-3

I have a user defined external URL that I'd like to turn into a link by using something similar to Django's urlize filter. How might one go about doing that?
I just need something to add in the preceding http:// or whatever if it's lacking.
Unless I missed it, link_to doesn't seem to do that.

Here's a simple helper method to prepend an http prefix if needed:
def url_with_protocol(url)
/^http/.match(url) ? url : "http://#{url}"
end
> url_with_protocol("google.com")
=> "http://google.com"
> url_with_protocol("http://google.com")
=> "http://google.com"
> url_with_protocol("https://google.com")
=> "https://google.com"

I can see a couple of solutions:
create a helper urlize(url) that adds http:// if it's missing
override the url getter on your model to add the http://
add a before_save callback in your model to add the http:// to the url, thus making sure that you have a valid url in your db
Personally, I just have some validations that check that the url entered is valid. Here, I would use the 3rd option.

Related

How to add additional variables in yii url

I'm working with Yii framework and i'm trying to implement "create pdf" button on all sorts of different url's.
My first plan was to simply add variable to url where "create pdf" button links:
'url' => Yii::app()->request->getUrl().'&pdf=true',
And it works fine on all links except when i enter directly to site like: www.example.com. In that case there is no index.php in url so button link is unusable as it looks like this:
www.example.com/&pdf=true
Is there Yii way to append variables to url or I need to do manual checks?
create your links like this :
Yii::app()->createUrl('controllerName/actionName', array('number' => 2, 'name'=>'john'));
//or this if you want it with http:://
Yii::app()->createAbsoluteUrl('controllerName/actionName', array('number' => 2, 'name'=>'john'));
you can add your parameter(s) to the original parameters with the CMap::mergeArray($_GET, array('pdf' => 'true'))
and use the Yii::app()->createUrl or your Controller's createUrl function:
http://www.yiiframework.com/doc/api/1.1/CApplication#createUrl-detail
http://www.yiiframework.com/doc/api/1.1/CController#createUrl-detail

Rails 3 optional scope url generation

I have the following defined in my routes.rb
scope '(:subdomain)' do
resource :highscore
end
now I can reach the same resource on these paths
/highscore
/test/highscore
however, when I generate a url using
highscore_path
it will always generate the /highscore path, however, i'd like it to generate a /test/highscore path when inside the test subdomain
i tried manipulating default_url_options or
highscore_path(:subdomain => 'test')
but it always omits the test. How do I work around this, preferrably without having to change all of my urls?
Turns out the specifier 'subdomain' was silly :( replace that with anything else and it'll work!
I've overwritten default_url_options in the application_controller with
def default_url_options
return {:identifier => 'test'}.merge(super)
end

Completely custom path with YII?

I have various products with their own set paths. Eg:
electronics/mp3-players/sony-hg122
fitness/devices/gymboss
If want to be able to access URLs in this format. For example:
http://www.mysite.com/fitness/devices/gymboss
http://www.mysite.com/electronics/mp3-players/sony-hg122
My strategy was to override the "init" function of the SiteController in order to catch the paths and then direct it to my own implementation of a render function. However, this doesn't allow me to catch the path.
Am I going about it the wrong way? What would be the correct strategy to do this?
** EDIT **
I figure I have to make use of the URL manager. But how do I dynamically add path formats if they are all custom in a database?
Eskimo's setup is a good solid approach for most Yii systems. However, for yours, I would suggest creating a custom UrlRule to query your database:
http://www.yiiframework.com/doc/guide/1.1/en/topics.url#using-custom-url-rule-classes
Note: the URL rules are parsed on every single Yii request, so be careful in there. If you aren't efficient, you can rapidly slow down your site. By default rules are cached (if you have a cache setup), but I don't know if that applies to dynamic DB rules (I would think not).
In your URL manager (protected/config/main.php), Set urlFormat to path (and toptionally set showScriptName to false (this hides the index.php part of the URL))
'urlManager' => array(
'urlFormat' => 'path',
'showScriptName'=>false,
Next, in your rules, you could setup something like:
catalogue/<category_url:.+>/<product_url:.+> => product/view,
So what this does is route and request with a structure like catalogue/electronics/ipods to the ProductController actionView. You can then access the category_url and product_url portions of the URL like so:
$_GET['category_url'];
$_GET['product_url'];
How this rule works is, any URL which starts with the word catalogue (directly after your domain name) which is followed by another word (category_url), and another word (product_url), will be directed to that controller/action.
You will notice that in my example I am preceding the category and product with the word catalogue. Obviously you could replace this with whatever you prefer or leave it out all together. The reason I have put it in is, consider the following URL:
http://mywebsite.com/site/about
If you left out the 'catalogue' portion of the URL and defined your rule only as:
<category_url:.+>/<product_url:.+> => product/view,
the URL Manager would see the site portion of the URL as the category_url value, and the about portion as the product_url. To prevent this you can either have the catalogue protion of the URL, or define rules for the non catalogue pages (ie; define a rule for site/about)
Rules are interpreted top to bottom, and only the first rule is matched. Obviously you can add as many rules as you need for as many different URL structures as you need.
I hope this gets you on the right path, feel free to comment with any questions or clarifications you need

How do I do a redirection in routes.rb passing on the query string

I had a functioning redirect in my routes.rb like so;
match "/invoices" => redirect("/dashboard")
I now want to add a query string to this so that, e.g.,
/invoices?show=overdue
will be redirected to
/dashboard?show=overdue
I've tried several things. The closest I have got is;
match "/invoices?:string" => redirect("/dashboard?%{string}")
which gives me the correct output but with the original URL still displayed in the browser.
I'm sure I'm missing something pretty simple, but I can't see what.
You can use request object in this case:
match "/invoices" => redirect{ |p, request| "/dashboard?#{request.query_string}" }
The simplest way to do this (at least in Rails 4) is do use the options mode for the redirect call..
get '/invoices' => redirect(path: '/dashboard')
This will ONLY change the path component and leave the query parameters alone.
While the accepted answer works perfectly, it is not quite suitable for keeping things DRY — there is a lot of duplicate code once you need to redirect more than one route.
In this case, a custom redirector is an elegant approach:
class QueryRedirector
def call(params, request)
uri = URI.parse(request.original_url)
if uri.query
"#{#destination}?#{uri.query}"
else
#destination
end
end
def initialize(destination)
#destination = destination
end
end
Now you can provide the redirect method with a new instance of this class:
get "/invoices", to: redirect(QueryRedirector.new("/dashboard"))
I have a written an article with a more detailed explanation.

Rails route to catch everything except assets

I'm trying to allow admin to create pages on the root path. So far i have:
get ':path' => "pages#show" ,:as =>:page, :path => /[^\.]+/
Basically i'm trying to ignore all paths with a dot in them (like .png). This does not seem to work as everything is rejected (i only want things in the public directory to be rejected, like fonts, icons, images..)
Thanks
As I explained in my comment above, "everything in public is directly rendered by the webserver" is NOT true if the desired asset does not exist. This will result in your catch-all route catching this undesired side-effect. This could cause a number of problems, as I explained. So, A specific catch-all route is needed to compensate for this:
get ':path' => "pages#show", :as => :page, :constraints => lambda{|req| req.path !~ /\.(png|jpg|js|css)$/ }
you can manipulate the regex how ever you see fit as my goal was just to get you on the right track by showing you that you can pass a block to the :constraints option. Also, I didn't just test req.format because that would exclude requests with header information for js format and would result in the catch all not working for these types of requests (not a usual case for a catch-all, but that's irrelevant). By using req.path instead, the header info is left intact/working and the path dictates whether or not this request is caught by this route.
I hope this helps you.
TESTING:
To test to see if your catch-all is actually catching what you want and not additional public resources, follow these steps. First put a debugger in your catch-all action, in your PagesController. Then make a request to a public file png/js/css file that DOES exist, like localhost:3000/images/example_image.png, and it should not hit your catch-all, as usual. Now, change the path to an image that doesn't exist, localhost:3000/images/no_image.png . If the request does not hit your debugger, your catch-all is not catching the image file request, and your ALL SET. If the request does hit your debugger, that means your catch-all is catching the image file request which means you need to revise your constraints in your catch-all.
By default dynamic segments don’t accept dots – this is because the
dot is used as a separator for formatted routes. If you need to use a
dot within a dynamic segment add a constraint which overrides this –
for example :id => /[^/]+/ allows anything except a slash.
http://guides.rubyonrails.org/routing.html#bound-parameters
So just removing the condition works. There might be another better solution to this problem though.