RoR 3, Passenger, Relative URLs, and helper methods such as root_url - ruby-on-rails

I'm really struggling to understand the routing behaviour I'm seeing with a Rails 3 app as it relates to Passenger and routing helper methods. Here's a quick overview:
When a user registers for my site, one of the things I'd like to do is send out a verification email to make sure the user has submitted a valid email address. So, I set up mailer to send the user a URL with some sort of magic token, thereby proving that they have received the email I sent. The URL in the body of the email is generated via a routing helper method (in this case, verify_url("a1b2c3") ) and the URL is stuffed into the email appropriately as:
http://localhost:3000/verify/a1b2c3
This all works great under localhost:3000 without a hitch. So, like a responsible developer, the next thing I do is move my app to my remote QA environment and verify that everything still works. My hosting provider uses Passenger, and I have it set up so that my QA environment can be accessed as follows:
http://my.url/qa
This is done by setting RailsBaseURI to /qa in the appropriate .htaccess file. So, I'm trying to register for the site in my QA environment, and I'm hoping that the helper method verify_url generates the following URL:
http://my.url/qa/verify/a1b2c3
However, this is what I get instead:
http://my.url/verify/a1b2c3
Notice the distinct lack of reference to the 'qa' path? I've scoured various blogs and manuals looking for an answer, and I've seen the following suggestions:
Set an environment variable such as ENV['RAILS_RELATIVE_URL_ROOT']
Set ActionController::Base.relative_url_root in my environment/qa.rb
Add a map to my config.ru to initialize my app under a particular path
Wrap my routes in a scope
However, each of these seems either to be deprecated (the first two options), or ends up producing redundant path info (the second two options) as follows:
http://my.url/qa/qa/verify/a1b2c3
Can anyone tell me the proper way to set this up, such that when I call verify_url("a1b2c3"), I'm actually getting the proper URL, including the relative path for my QA environment? Ideally, I'd want to be able to set this in environments/qa.rb somehow, such that I don't have to change my app at all when moving from one environment to the next.
Thanks in advance!

I would dismiss the whole /qa/ and move the staging to a subdomain like http://qa.my.url/ Otherwise you will have to make changes to the code that might affect production in a negative way.
Update
I've ignored the the fact that you wanted to use those routes in emails. In this case you will have to set :host in the url helper:
verify_url(:id=>"a1b2c3", :host=>"qa.my.url")
To cut down the amount of changes you would have to make once you go into production, I would suggest you define a constant in an initializer (in /path/to/app/config/initializers/my_host.rb):
MY_HOST = 'qa.my.url'
After that you could call verify_url in your email templates like so:
verify_url(:id=>"a1b2c3", :host=>MY_HOST)
Once you go into production, change the MY_HOST constant and you won't have to worry about the templates.
In my views I never use the *_url helpers, only the *_path ones to circumvent this issue.

After spending a little time with the ActionMailer docs, it turns out that there is an even easier solution, at least for what I am looking to accomplish. Wukerplank definitely answered my question, but it seems that I wasn't quite asking the right question. :-)
The only time I really need to see the fully qualified URL is when I'm working within a mailer, right? Otherwise, paths work just fine. Well, it turns out that ActionMailer allows you to specify default url options on a per-environment basis. In my environments/development.rb config file, I now have the following:
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
When I move to my QA environment, I just need to tell Passenger that I'm running from QA, and have something like this environments/qa.rb config:
config.action_mailer.default_url_options = { :host => 'qa.my.url' }
That way, the code doesn't have to change one bit between environments, and thus, I really don't care what domain, subdomain, or port I'm running on... It just works. Hope this helps someone else in the future. Thanks!

Related

better way to configure the email address with rails application

Currently, I'm working on the application which is developed in rails 3* and ruby 1.9.3. I have configured some email address in the initializers section as YML file for each environments.
But, the requirement is keep on changing (but it'll happened every month 1 or 2 times) that need to add/remove the email address from the configuration. Hence, I need to restart the server on every changes. Because, I configured those address in the initializers.
Is there any better way to handle this situation?
If you want to change the email without re-deploying / restarting the server, you can always create a Email model and persist it to the database. By adding a current field / column (boolean value) and a scope scope :current, -> { where(current: true) } you can access the email via Email.current.first.address, for instance. You might need to ensure that one and only one 'current' Email object ist present at any given time.
edit
creating a model does not mean you have to create UI for it. Just use the console to change the email if you have to.
the configuration and models are loaded at startup, and if you don't want to do any reloading in production, which is slow and not recommended, you have to use the DB for persistence.
if you really want to go down the "reload" route, set config.cache_classes = true in config/environments/production.rb and specify the email in a constant outside the config directory (in some model or controller) like this EMAIL = 'whatever#email.com'. You would have to change the production code on each production machine, without a server restart. Sounds very hacky. Look into zero-downtime deploys Ă  la Github for a more elegant solution. Redeploying should be cheap and painless.

Configure URL for domain name

I have a Rails app, and now I'm going to deploy it, but before I need to solve the issue I faced.
Some of my controller methods and links use object_url method.
And it returns http://localhost:3000/...
Of course, after deployment that won't work.
How I can point Rails, that it should work as http://domain.com/... ?
You don't have to care about it. Rails will handle this automatically. It recognizes user request and takes the domain name from it. Just deploy it. Set the DNS and you will see.

Is it possible to remove the .herokuapp part of the url in a rails application running on heroku

Hello wonderful world of the stack!
This is my first deployment using Heroku (I am just using their free service at the moment).
I have set up my domain to point to Heroku and renamed my application and everything works fine. the one thing I would like to change however is the format of the final url.
For example, after typing in:
www.example.com
The page loads and the url changes to:
example.herokuapp.com
Is there any way that I can change this behaviour so that the url remains the same?
I have tried using the help pages supplied by Heroku and various searches (usually similar searches to this question title) but I am not sure if I am searching for the correct thing as I seem to only get unrelated results.
Thanks in advance for any light you may be able to shed on this matter
This sound like a problem with your DNS Domain setup rather than heroku. Read about how to configure your domains properly for heroku use here.
It sounds to me as if you are doing some kind of 'web redirection' (custom stuff that each hosting provider offers) with your domain, when you should set CNAME or A Records.
Which provider hosts your domain / where did you 'buy' it?

How do I serve multiple rails apps on one domain (and sub)?

This is kind of weird but I'd like to serve multiple websites on the same domain. If possible, we want to avoid subdomains to keep urls simple for our users - no need for them to know it's two separate apps. This is purely for keeping the code bases separate. Any ideas?
For example:
Rails App 1 (Refinery CMS) serves:
http://example.com/
http://example.com/about
http://example.com/pricing
Rails App 2 (our real App) serves:
http://example.com/account
http://example.com/store
http://example.com/listings
We use ruby 1.9.2, ruby on rails, refinery cms, apache and passenger.
If you're using Passenger, check out the Deploying to a sub URI portion of the manual - it's quite simple to set up an app on a sub-URI. You may need to set config.action_controller.relative_url_root in your app configuration, as well.
Edit: I misread the question; not one app per URI, but one app serving some (but not all) endpoints. This is actually moderately easy to do with some basic rewrites, as well.
Deploy your Rails app to, let's say, /railsapp (but without setting relative_url_root). Now, in .htaccess:
RewriteRule ^account/(.*)$ railsapp/account/$1 [L]
This will internally remap /account/* to /railsapp/account/*, so as long as you set up a rewrite per path your Rails app handles, it should work fine.
Subdomains make it easier (thus why most sites have shop.example.com), but you could probably use rewrite rules with name based virtual host routing. How exactly to do that I'm not sure. More of a Apache rewrite question for SuperUser.
A word of warning if you are using SSL you might have issues arise.
You could set it up to first hit one app where you expect most URLs would work and if it 404s you could instruct it to try the other app next, though this will be slower than routing per route but it will work without having to hardcode a route for every page that is created on say, Refinery CMS.
Currently I'm also working on a same kind of CMS. In my case also I need multiple sub domains, like
www.test1.mydomain.com
www.test2.mydomain.com
www.test3.mydomain.com
www.test4.mydomain.com
here is what I did
in rails 3 (if you are on rails3) you can get the sub domain by using request object. (If you are on rails 2.x you can use sub domain_fu plugin)
In my case I have used a before filter to get the sub domain, after that I load the site according to the sub domain
For development use the following public domain "lvh.me"
http://tbaggery.com/2010/03/04/smack-a-ho-st.html
this was very useful for me http://railscasts.com/episodes/221-subdomains-in-rails-3
let users have their domains forwarded to your subdomain (with masking)
ex : www.clientdomain.com --> http://client.mydomain.com
hope this helps
cheers
sameera

Can I look at Mailer views without actually sending an e-mail?

I think this may be a common situation… I'm working on a password recovery system for a Rails app that sends a link to the user to trigger a new password form. Pretty standard stuff.
On my development server I don't have any mail-sending software enabled or configured (sendmail, SMTP settings, etc.) In config/environments/development.rb I have config.action_mailer.raise_delivery_errors = false to suppress any errors that arise since I don't have a local mail server enabled. This is all good and fine.
However, I would like to view the content of the e-mails while in production without actually sending the mail. I know that it's possible to kind of do this through testing, asserting that the sent (or faux-sent) mail has the correct content. Is there any way to reroute views or something, just temporarily in production, to view an HTTP-served version of the e-mail rather than blindly making assertions?
If you have a UserMailer setup with a "password_reminder" method, you can call create_password_reminder instead of deliver_password_reminder and it will create the message without actually sending. Then you could send the output to the log file:
Where you would have:
UserMailer.deliver_password_reminder
You can replace with:
logger.info UserMailer.create_password_reminder.encoded
Or if you want to send it to a file, you can do that as well.
That being said, the production environment really isn't the place for this sort of thing. I've never had a need to do this, because my mailers have full test coverage. I'd look into that option instead, but I gave the answer you asked for because I don't know your full situation. Happy coding :)

Resources