How to get ruby on rails production mount-point in

2019-07-16 19:26发布

问题:

tl;dnr: My page links are fine, but links erb-olated into non-page text (email) lack the production app mount-point.

detail: My RoR app uses slightly different URLs in dev and production: in dev, the top-level entity is the resource model:

http://localhost:3000/ENTITY/1/edit

But in production, the app is mounted one level down, so URLS have an extra term:

https://server.example.com/APP/ENTITY/1/edit

This works fine for most cases: url_helpers like edit_entity_url return the URLs as I've shown them. Frankly, I'm not clear exactly how that works; I suppose Phusion Passenger and Rack work it out?

However, when I send email (such as to confirm a password reset), it's not working: edit_entity_url never adds the APP mount-point. Short of my own conditional logic in ERB, how can I get that "/APP" in there?

Things I've tried:

  • Setting `Rails.application.routes.default_url_options[:host] to include "/registry" (no effect)
  • using <%= link_to 'Reset', edit_entity_url(...) %> (no help)

Version info:

  • actionmailer (4.0.2)
  • Rails 4.0.2
  • ruby 2.1.0p0
  • rack (1.5.2)
  • passenger (4.0.35)

The Apache "sites-enabled" file that ties things together (and the only place I can find that the "/registry" token is defined):

<VirtualHost *:80>
    ServerName server.example.com
        DocumentRoot /var/www
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        <Directory /var/www/>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>

    Alias /registry /app01/capapp/registry/current/public
    <Location /registry>
        PassengerBaseURI /registry
        PassengerAppRoot /app01/capapp/registry/current
    </Location>
    <Directory /app01/capapp/registry/current/public>
        Allow from all
        Options -MultiViews
    </Directory>

    ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
    <Directory "/usr/lib/cgi-bin">
        AllowOverride None
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
        Order allow,deny
        Allow from all
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log

    LogLevel warn

    CustomLog ${APACHE_LOG_DIR}/access.log combined

    Alias /doc/ "/usr/share/doc/"
    <Directory "/usr/share/doc/">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride None
        Order deny,allow
        Deny from all
        Allow from 127.0.0.0/255.0.0.0 ::1/128
    </Directory>

</VirtualHost>

The Rails config/environments/production.rb file.

Registration::Application.configure do
  config.cache_classes = true
  config.eager_load = true
  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true
  config.serve_static_assets = false
  config.assets.js_compressor = :uglifier
  config.assets.compile = false
  config.assets.digest = true
  config.assets.version = '1.0'
  config.log_level = :debug
  config.i18n.fallbacks = true
  config.active_support.deprecation = :notify
  config.log_formatter = ::Logger::Formatter.new
  Rails.application.routes.default_url_options[:host] = "server.example.com"
end

Registration::Application.config.middleware.use ExceptionNotification::Rack,
:email => {
  :email_prefix => "[Registration]",
  :sender_address => %{"Admin" <XXX@example.com>},
  :exception_recipients => %w{XXX@example.com}
}

View for the email (app/views/user_mailer/password_reset.text.erb):

To reset your password click the URL below.  

<%= edit_password_reset_url(@user.password_reset_token) %>

If you did not request your password to be reset please ignore this email 
and your password will stay as it is.  

Yes, it's odd to base the URL on the reset_token not the user_id, but it's handled in the controller. Structure based on Railscast #274

回答1:

Well, this is a disgusting hack, but I "solved" this by manual intervention. In app/views/user_mailer/password_reset.text.erb, I now say:

To reset your password click the URL below.  

<%  url = root_url.chomp('/') %>
<%  url += "/registry" if Rails.env.production? %>
<%= url + edit_password_reset_path(@user.password_reset_token) %>

Still interested if anyone has a better idea!



回答2:

You can pass the :script_name parameter to url_for (or other methods based on url_for) to set the mount point. I use

url_for( :only_path => false,
:host => some.host,
:script_name => Rails.application.config.relative_url_root
...

and set config.relative_url_root = '/your-mount-point' in the config.

More details about it here: how-to-get-ruby-on-rails-production-mount-point-included-in-url-helpers-for-email