Getting full URL as displayed in browser URL bar - ruby-on-rails

I have a rails app where I want to send people an email when they sign up. The email has a link to their photos portal so they can get started adding photos, etc..
class MyMailer < ActionMailer::Base
def welcome_email
# ...
link = photos_url # => www.myapp.com/photos
# ...
end
end
The problem is that when I push my code to Heroku and run it live, that link doesn't generate as expected.
The photos_url returns the URL relative to the localhost and ends up generating myapp.herokuapp.com/photos, which is incorrect.
What's even stranger is that if I pause the code at that point with binding.pry and try to see what photos_url is returning, it correctly returns www.myapp.com/photos as expected.
Any thoughts on how to resolve this? I'd hate to have to construct the URL myself from scratch, because that means I have to do it for every environment (localhost, staging, production, etc...)
Thanks!

ActionMailer isn't tied to the request/response cycle, thus it doesn't know what's the host the app is currently running on. Actually, emails are typically sent by some background worker processes which know nothing about the current request URL.
So to make it work you need to set the ActionMailer default_url_options.host option.
Add this into you config/environments/production.rb:
config.action_mailer.default_url_options = { host: 'www.yourapp.com' }

Related

Rails Twilio App hangs when sending text in heroku on run, but works in rail console on heroku (and locally in all environments)

I built a twilio-powered rails app that has a method which sends an SMS with parameters (to, from, body). The app works fine locally using ngrok in dev and production mode.
Heroku is a different story. Once it gets to the part where the text message is built it doesn't go past it and the logs don't show any problems. It's like it stalls out and twilio gives up on waiting for a response after 15 seconds. Here is the class that has the sms being sent:
require 'twilio-ruby'
class SmsActions
def self.compose_message(to, from, body)
account_sid = Rails.application.secrets.twilio_account_sid
auth_token = Rails.application.secrets.twilio_auth_token
#client = Twilio::REST::Client.new(account_sid, auth_token)
message = #client.account.messages.create({
from: from,
to: to,
body: body,
statusCallback: "http://fptracker.herokuapp.com/twilio/callback"
})
end
I have used "puts" statements to log and to confirm that it sees the account_sid and auth_token. Same thing to discover that it doesn't run anything below the message block.
The weird thing is I can run hoerku run rails, input the exact same code that sends the text (hard-coding the account_sid and token) and it works.
So I don't think it's missing credentials, I don't think it's the middleware (because it works manually in heroku), it's not the production env because it works locally in production. I have been working on this for 30+ hours and am totally stumped.
--EDIT
I noticed I didn't have the required code at the top of my notifications controller that was in the tutorial such as:
require 'twilio-ruby'
class NotificationsController < ApplicationController
include Webhookable
after_filter :set_header
skip_before_action :verify_authenticity_token
But after adding that it still doesn't work.
The second thing I noticed is that when I put the code to create the SMS directly in the controller, it works on heroku. I put the method in a different class in the same notifications_controller file and called and it works.
But when it gets called the intended way the path is this:
routes hits notifications#parse
parse regexes for message starting with 'test'
parse method then calls model method Message.auto_reply
Message.auto_reply gets my twilio number from secrets and has a few puts statements and then calls SmsActions.compose_message
compose message is the class listed above and creates the text message.
So compose_message is the class that works in local environments but not on heroku. It does get hit, as the logs show the puts statements i put in there, but it freezes/stops on that Twilio message creation in heroku. Something is happening between the controller and that method.
Solved. It had nothing to do with Twilio exactly, but me passing a nil value in the "from" variable when trying to create the text message. That value came from my ENV variables
from = Rails.application.secrets.twilio_number
When that is called from within the controller, it accesses the variable from environmental variables successfully, but it is nil when called from anywhere outside of the controller. I don't know why yet. So this block below would get stuck in limbo with that nil value but nothing in heroku logs showed me that. Maybe there is a way on Twilio's website to see that, but I doubt it if it can't associate it with a number? Though it does pass the account_sid.
message = #client.account.messages.create({
from: from, # this was passing in a nil value
to: to,
body: body,
statusCallback: "http://fptracker.herokuapp.com/twilio/callback"
})

Mechanize: not logged in the site

I'm trying to authorise to other site with gem mechanize
My code:
def login_zenit
agent = Mechanize.new
agent.get('http://mobile.zenitbet.com/') do |p|
f = p.forms.first
f.login = 'login'
f.fields[1].value = 'password'
f.submit
end
agent.get('http://mobile.zenitbet.com/')
redirect_to root_url
end
The problem is when I run login_zenit it doesn't work - I'm not authorising to the site in web browser. Although if I run this code in rails console it works perfectly. Where did I make a mistake? maybe there is a problem with redirect_to root_url?
Thanks!
In general is not possible. For this to work visiting your app would need to set the appropriate cookies for mobile.zenibet.com.
You do have those cookie values - they're inside the mechanize object, but even if you were to extract them you wouldn't be able to set them on the correct domain. If your app is being served from foo.com then the browser will let you set cookies on foo.com or any subdomain of it, but it won't let you set cookies on another arbitrary domain (see point 5 in section 5.3 of the rfc)
Unless your app runs on a subdomain of zenibet.com I think you are out of luck

How do I ignore a route in rails?

I have a route i.e. mysite.com:3000/new_route that I'd like to ignore, is this possible to do through rails and not server side?
I've read that this can be done through apache, however, my app is running on Heroku and that type of control isn't accessible to me. Therefore, I'm looking for another way to do this through Rails.
Thanks
update
I am using faye to have live notifications in my app, on localhost faye runs on port 9292 - localhost:9292/faye, all good in development mode, but in production it should point to mydomain.com/faye, there are no port numbers in production environment, and loading faye.js returns error not found
If you're talking about a resources route you don't want to be created:
resources :something, except: :new
However, I'm not exactly sure if this is what you meant by ignore.
You can define a route at the top of your routes.rb file that will redirect to some other page.
get '/new_route', redirect: '/'
By the time you ask Rails to process the route, it is already too late. If you ask rails to process a route, it will, either by returning a 404 of 500 error, or a page.
If you want the route to be processed by another application, it will need to be intercepted by your webserver (nginx or apache, or whichever one you're using). In their configuration, you just redirect that route to the other application, and every other route to the Rails app.
EDIT
Another option you have, is to forward your requests to a different server.
You add a route like
get 'faye/*query' => 'faye#get'
post 'faye/*params' => 'faye#post'
And then a controller
require 'faraday'
class FayeController < ApplicationController
APP = 'http://mydomain.com:9292'
def get
request_page :get
end
def post
request_page :post
end
private
def request_page(method)
conn = Faraday.new(:url => APP)
query = params.delete(:query)
response = conn.send method, query, params
render text: response.body.gsub(APP, 'mydomain.com/faye')
end
end
which will use Faraday to load the information from your other application.

Controlling the logging and sending of emails in different environments

In a Rails application I set up a new staging environment with the following parameters in its environments/ file:
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
However, when the system generates an email, it gets printed to the staging.log file instead of being sent. My SMTP settings work fine in other environments. What configuration am I missing to get the emails to actually send?
Edit: Yes, the staging box is set up with valid configuration for an SMTP server it has access to. It seems like the problem isn't with the SMTP settings (if it was, wouldn't I get errors in the logs?), but with the Rails configuration. The application is still redirecting emails to the log file (saying "Sent mail: ...") as opposed to actually going through SMTP.
Edit #2: It looks like the emails actually have been sending correctly, they just happen to print to the log as well. I'm trying to use the sanitize_email gem to redirect the mail to another address, and that doesn't seem to be working, which is why I thought the emails weren't going out. So I think that solves my problem, although I'm still curious what in ActionMailer's settings controls whether emails are sent, logged to the log file, or both.
Edit #3: The problem with sanitize_email boiled down to me needing to add the new staging environment to ActionMailer::Base.local_environments. I'll keep this question open to see if anyone can answer my last question (what determines whether ActionMailer's emails get sent out, logged to the log file, or both?)
Regarding your third edit, the logging is a function of which log level you have set for the app itself, not any particular setting in ActionMailer.
In Rails 2.3, ActionMailer::Base simply send the email to whatever logger has been configured, if any. The recipient is sent to the info log and the body of the email is sent to the debug log. (Comments are mine. The rest is straight out of the source code.)
def deliver!(mail = #mail)
raise "no mail object available for delivery!" unless mail
#
# Logging happens first (or not)
#
unless logger.nil?
logger.info "Sent mail to #{Array(recipients).join(', ')}"
logger.debug "\n#{mail.encoded}"
end
#
# And then we decide if an email should really get sent
#
begin
__send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
rescue Exception => e # Net::SMTP errors or sendmail pipe errors
raise e if raise_delivery_errors
end
return mail
end
Your environment.rb or staging.rb file should have a line that controls the log level. Something like the following:
config.log_level = :debug
This is all completely separate from the mailer configuration that you already found, which controls whether the email is sent or not.
config.action_mailer.perform_deliveries = true

How to create a url for notification in Rails

i want to send an email to user after he sign-up with code.for ex
http://192.168.1.51:3000/logins/activate/435546dgfd757676657 #link contains in an email
how can i create the above URL in my notifier model.
i know following way
url_for :controller=>'logins', :action=>'activate', :id=>'435546dgfd757676657' , :host=>'http://192.168.1.54:3000'
Which is working properly.
what i want that host should not be hard coded. How can i get host with port in a model.
In controller i can find it using follwing ways
request.host_with_port
Please provide me correct ruby way for doing same.
You can define the host in your environment.rb file.
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
As your host probably changes depending of your environment (development, test, production), it's better to put that config line inside the environment file.
After that, every link in emails will be made with that host. You don't have to provide it in the view anymore.

Resources