Rails routing / assets with proxied location - ruby-on-rails

OK so - I have a rails app, let's call it "railsapp", on a domain on a Dreamhost VPS (on which I have other domains, some 'static' and one that serves a perl/CGI script).
For obvious reasons, Passenger cannot be run on port 80, and is currently serving this app on port 8001. I have a proxy (configured in Apache) set up to route requests for http://DOMAIN/books to DOMAIN:8001/ (which, incidentally, works as DOMAIN/railsapp).
The issues:
1) No assets are served - the request for the stylesheets 404s, despite the fact that the stylesheets do in fact exist in [railsapp]/public/assets. I have tried every possible combination of precompiling, not precompiling, etc., and nothing seems to work.
2) Links aren't working exactly right. Accessed explicitly through port 8001, links work correctly, e.g., DOMAIN:8001/about shows the 'about' page and DOMAIN:8001/[railsapp]/[:id] brings me to the appropriate item. Similarly, accessed explicitly (i.e., by manually typing the address in the address bar), DOMAIN/books/[railsapp]/[:id] and DOMAIN/books/about work perfectly fine. But the links at DOMAIN/books (to the items identified by id) all point to DOMAIN/[railsapp]/[:id], which gives me a 404; the link to 'about', similarly pointing to DOMAIN/about, also does not work.
Now! If I were to throw caution to the wind and switch my VPS to use nginx instead of Apache, guess what? The pathing mostly works! Items identified by id are linked as DOMAIN/books/[railsapp]/[:id] and "about" is linked as DOMAIN/books/about, and this is great. The only issue in the rails app is that links back to root are inexplicably(?) rendered as links to 127.0.0.1:8001. This is annoying, but seems like it would be trivial to fix. The one problem with this is—you may remember this from the beginning of this post—the VPS also hosts a site that uses perl/CGI, and this is broken as hell given that nginx isn't really meant to deal with perl/CGI. As such, I'm sticking with Apache so that the CGI remains unbroken.
I have done the search for this issue, many times, in many different ways. I have probably seen whichever stackoverflow question or blog post you are thinking about linking me to. If you have any idea how to fix the pathing and assets issues, given the above information, let me know.

Try in application.rb inside "class Application < Rails::Application":
config.action_controller.default_url_options = {host: 'DOMAIN/books', port: 80}
config.action_controller.asset_host = 'DOMAIN/books'
config.serve_static_files = true # for rails4; for rails3 use .serve_static_assets = true
After that, restart your application.
Make sure that application uses *_url-helpers everywhere rather than *_path-helpers (i. e. root_url instead of root_path)

Related

Reverse Proxy with Heroku (for Wordpress) won't show full pages

I am trying to use a reverse proxy to serve a Wordpress blog running on EC2 at /blog from a rails app running on Heroku. I've used the rack-reverse-proxy gem and followed directions like this:
How can I use a subdirectory instead of a subdomain?
My app is running at http://myapp.com, my main target (wordpress) server is running at http://ec2-xx-xx-xx-xx.us-west-2.compute.amazonaws.com.
The pages on the target server all serve up fine when accessed directly. However, no matter what page I try to get through the reverse proxy, the HTML gets cut off. I've tried serving via both my own custom builds of Wordpress on Ubuntu (EC2) and on hosted examples like Bluehost. I've tried to access simple, handmade HTML and text test files, as well as the php files automatically generated by wordpress. Every time, the file is cut off.
Text files just get randomly cut off after ~1000 characters (this is variable)
Ditto for hand-coded HTML files
Wordpress files seem to get cutoff during the <head> (often mid-word) and render an empty <body>.
If a page should exist (e.g. http://ec2-xx-xx-xx-xx.us-west-2.compute.amazonaws.com/test-post, it will load (at least partially) at myapp.com/blog/test-post, so I don't know that rewrites are the issue.
I've tried enough variables on the wordpress/destination server that I'm inclined to think it's something on the Rails side (I have the same issue whether running "myapp" locally or on a Heroku test server). Beyond that, I'm stuck.
Any thoughts as to what I might be doing wrong? There's nothing in the Apache logs to indicate that anything is getting cut off. I'm pounding my head over this to no avail, and would really appreciate any help.
FIXED IT!
Digging into the rack-reverse-proxy gem, I found that I'm not the only person that had an issue with this:
Seeing some issues with Content-Length being corrupted, this ensures it matches body length as delivered.
The fix hasn't been merged into the master branch, but I forked my own copy, applied the fix, and it's working great. Hopefully it'll be merged into the master and nobody else will have to worry about this.
EDIT
One more thing that might be helpful for somebody else, if they are having problems with Visual Editing Mode not working under wordpress: Make sure that you also include this update to have the headers get passed in properly. The gem just hasn't really been updated in a couple of years, so it's up to you to go through and copy in fixes from other people. Applying those two fixes has everything working fine for me with a Heroku app/AWS&Ubuntu Wordpress setup.
Heroku will cut the request after a certain amount of time which would cause the behavior that you're seeing.
You basically have to pull the page from the wordpress blog and reserve it via Rack in the time for one request - think that could be the problem?

Rails: How to set port number for the asset hosts in development

Rails lets me set multiple asset hosts as an application configuration. Problem is, I've been writing the full host as an argument. That is, in development I've been appending the ":3000" to it. Which has worked fine, until the day I had to run 2 projects in parallel, and one of them add to go to a different port (let's say, 3001). As I already read, Rails app can't possibly know which port the web server is listening to. Is there a workaround for this?
If I understand your question, you can use asset_path helper instead of the full path by hand, and put different asset_hosts in production.rb and development.rb.
For the port issue, inside a controller method you can get it from the request, but, outside, I don't know a way to do that... you can try the PORT enviroment variable.
Hope it helps.

Rails, Cucumber, Capybara, Selenium, Pow, domains, subdomains and javascript

TL:DR - How can I make Cucumber request a page through the app but pretend that the request came from "http://mysubdomain.mydomain.dev", and run the javascript properly?
Ok, this may take a while to explain, but please bear with me...
I'm a 'relatively' experienced Rails dev, but only recently took the plunge into full BDD/TDD.
The page I'm testing in cucumber has some javascript in it for creating a new nested object (very similar to this RailsCast).
Now the problem lies in the fact that not only do I have variable sub-domains, I have variable Domains too (Just trust me on that one...)
So the app needs to query the request.host to find the current_domain and the current_subdomain before everything ties up in the background and it can carry on serving the right bits of the app.
I've managed to get all of the tests passing fine using the host! domain and Capybara.default_host = domain techniques in the background steps.
However, when I use the #javascript tag on the feature that tests a page with .js on it, I get Firefox grabbing focus, then trying to load the full url.
Now it also happens that I'm running Pow, and have those urls hooked up to the dev server. Unsurprisingly, they don't work as when the user tries to log in, it's looking in the dev DB. I tried running the suite after turning the pow server off, and it just timed out.
Surely the javascript webdriver shouldn't be actually accessing the url, but simply running the app itself and pretending that the host is what I tell it to be???
I'm obviously missing something here - How can I make Cucumber build the page internally, but pretend that the request came from "http://mysubdomain.mydomain.dev"?
edit: Jason - the variable domain trick is achieved exactly the same as subdomains... If you can query the db for an account based on request.subdomains.first, you can query for a domain through request.domain. You just have to double check a few things like capitalisation etc. to minimize the risk of a malformed url breaking stuff, and obviously you need to ensure that the domain record exists in the db first...
Oh - and cache the domain record requests with care...
It means that you can serve the same app, but with different styling and landing pages etc. Useful for PaaS apps with a broad customer base - you can re-brand it and sell it as a specific solution to one group's problems, even though it's the same guts underneath.
I am looking to do something very similar. I thought about adding an additional pow directory for test, then using the pow directives to override the environment. I think this is done within a ".powenv" inside your app directory. Here is an example of a quick fix:
echo export RAILS_ENV=cucumber > .powenv && touch tmp/restart.txt
But it would be better to do something dynamic so that before you start the test, you can tell pow what env to run, then when finished switch back, maybe even run the test server on a different port on a temp basis. Pow is the only great solution I know of so far for handeling subdomains.
EDITED: I have this working now in my environment with the following addition to my features/support/env.rb file.
# Switch Pow to For Cucumber Tests
Capybara.default_driver = :selenium # Subdomain testing will only work with pow and selenium
pow_config = "#{Rails.root}/.powenv" # Dont change, this is the Config Files Location.
pow_config_stash = "#{Rails.root}/.powenv_original" # This is what the config will be stashed as during testing.
Before do
# Set the default host
Capybara.app_host = "http://www.resipsa.dev"
# Stash the existing config
File.rename(pow_config,pow_config_stash) if File.exists? pow_config
# Write the new pow config
f = File.new("#{Rails.root}/.powenv", "w")
f.write "export RAILS_ENV=test"
f.close
# Touch tmp/restart.txt to force a restart
FileUtils.touch "#{Rails.root}/tmp/restart.txt"
end
After do
# Delete the temp config
File.delete(pow_config)
# Restore the Original Config
File.rename(pow_config_stash,pow_config) if File.exists? pow_config_stash
# Touch tmp/restart.txt to force a restart
FileUtils.touch "#{Rails.root}/tmp/restart.txt"
end

Overriding the Rails router

This is a weird one I know. I'm trying to tie together two Rails applications, a v3 and a v2.3.5
I want them to share the same domain, and in order to do that without changing the URLs in the older app, I'm trying to find a way to override the Rails router.
I want the newer app living at the root of the domain, and the older one under several directories. For example:
/ => app1 # v3
/users => app1
/employees => app2 # v2.3.5
/payrolls => app2
So, since app1 lives at root and I'm using Passenger, I only have to create symlinks in the public folder of app1 to the public folder of app2, like so:
app1/public/employees => app2/public
app1/public/payrolls => app2/public
And then I add RailsBaseURI /employees and RailsBaseURI /payrolls to Apache's config.
With that I can make old URLs of app2 work, but inside the app, the links point to a prefix. For example, /employees/1, /employees/employees/1, /payrolls/employees/1 all work, but links in the app point to /employees/employees/1 and /payrolls/employees/1 depending on which prefix I'm currently on.
So, I need to remove that prefix from the links so only the old URLs work.
I need to do this in order to release the newer app. In time I'll upgrade the older one to v3 and work directly on this issue, but right now any hack is ok just to make it work.
I don't expect a solution, but if you can point me in the right direction on where to look in Rails source, or maybe a simpler approach I'm not seeing, I'd be very grateful.
Turns out it's pretty easy just by overriding url_for in the ApplicationHelper like so:
def url_for(options = {})
original = super(options)
# I tried this in order to improve performance:
return original unless original.starts_with?('/employees/employees')
original.gsub('/employees/employees', '/employees')
end
However, this caused intermittent errors in the app that I wasn't able to pinpoint. I suppose it's because of the performance hit.
So in the end I was forced to use a different domain for the newer apps.
I'm not really sure if that's possible to use 2 different versions of rails on your routes, but another way to do that is using haproxy with 2 different servers.
# In haproxy.conf:
frontend rails
bind *:80
acl rails2 hdr_host(end) -i employees.mydomain.com
acl rails3 hdr_host(end) -i mydomain.com
use_backend rails2_server if rails2
use_backend rails3_server if rails3
default_backend rails3_server
backend rails2_server
server myrails2_server 192.168.1.1:3000 check
backend rails3_server
server myrails3_server 192.168.1.1:3001 check
Then point your DNS A record of mydomain.com to your haproxy IP and point employees.mydomain.com to mydomain.com/employees.
To test it out, you can add the IP address of your haproxy server and mydomain.com to your /etc/hosts file

Is there a way to change map.root to point to something other than '/'?

I have apache 2.2 with mod_rails running at http://localhost. I want to have my rails app at http://localhost/railsBlog. So, what I did was, I created a virtual host:
ServerName localhost
DocumentRoot /Library/WebServer/Documents
RailsEnv development
RailsBaseURI /railsBlog
Now, since the URL is http://localhost/railsBlog, the server views railsBlog as the controller I'm passing in, which is not what I want. So when I go to http://localhost/railsBlog/home/index. This won't get to my 'home' controller and 'index' view because it tries to go to 'railsBlog' controller (doesn't exist) and 'home' view (doesn't exist).
I think one way to solve this is to redefine map.root to be /railsBlog and things should be fine. But how?
Another way I could get around this would be to modify config/routes.rb to have:
map.connect 'railsBlog/:controller/:action/:id'
However, this would mean that I would have to change this file every time I deploy to a different location.
Or, is there any other way to get around this?
You can put a line like this in config/environment.rb (or one of the specific environment files)
config.action_controller.relative_url_root = "/railsBlog"
You should also symlink the publc directory to the root of the web directory, for example:
ln -s /rails/railsBlog/public /webroot/
This is all from the passenger documentation
Just to add to the previous answer... here is the url of the documentation:
http://www.modrails.com/documentation/Users%20guide.html#deploying_rails_to_sub_uri
Also, here is some information if you are running into errors with broken image, css, resource links...
http://www.modrails.com/documentation/Users%20guide.html#sub_uri_deployment_uri_fix
Basically it says you should always use the rails helper functions (image_tag, javascript_include_tag, and stylesheet_link_tag) instead of hand coding the urls. These will automatically generate the correct URL with the sub uri you have set.
This allows you to easily move the application to another sub uri or out of the sub uri configuration without changing all of your references.
One nice thing about this is that you can use one virtual server statement to deploy mutiple applications by having multiple RailsBaseURI lines. This came in handy for an app we were trying to build.

Resources