I have some code that looks like this:
class Wrapper
def get_email_body rdv
body = "<h1>There is an appointment available!</h1><br>"
body += "Title: <b>#{rdv['title']}</b><br>"
body += "Date: <b>#{rdv['start']}</b><br>"
body + "Link: <a href='#{rdv['link']}'>"
end
def send_email rdv
TARGET_EMAIL_ADDRESSES.each do|email|
Mail.deliver do
to email
from 'my_email_address'
subject 'Appointments available!'
html_part do
content_type 'text/html; charset=UTF-8'
body get_email_body(rdv)
end
end
end
end
end
When send_email is called with an object that represents an appointment, I get the exception:
/var/lib/gems/2.7.0/gems/mail-2.7.1/lib/mail/message.rb:1396:in `method_missing': undefined method `get_email_body' for #<Mail::Part:0x000055ba3e9f60c0> (NoMethodError)
I noticed that inside the block given to Mail.deliver, self is a #<Mail::Part:560, Multipart: false, Headers: <Content-Type: text/html; charset=UTF-8>> object.
I understand that the block is ran by an instance method of the Mail class, and that's the reason why self is different.
Is it possible to access the Wrapper object, self of the outer context, and call the get_email_body instance method ? preferably without making it a class method.
Thanks.
Evaluate the email body before calling Mail.deliver.
def send_email(rdv)
email_body = get_email_body(rdv) # evaluate
TARGET_EMAIL_ADDRESSES.each do |email|
Mail.deliver do
to email
from 'my_email_address'
subject 'Appointments available!'
html_part do
content_type 'text/html; charset=UTF-8'
body email_body # pass the value
end
end
end
end
Related
I'm currently getting an error when I make a GET request using httparty. The call works when I use curl. The error is as follows:
\"Authdate\":\"1531403501\"}" }, { "error_code":
"external_auth_error", "error_message": "Date header is missing or
timestamp out of bounds" } ] }
When I make the request via curl this is the header I use.
curl -X GET -H "AuthDate: 1531403501"
However, as you can see, the request changes from AuthDate to Authdate causing the error. Here is how I'm making the call:
require 'openssl'
require 'base64'
module SeamlessGov
class Form
include HTTParty
attr_accessor :form_id
base_uri "https://nycopp.seamlessdocs.com/api"
def initialize(id)
#api_key = ENV['SEAMLESS_GOV_API_KEY']
#signature = generate_signature
#form_id = id
#timestamp = Time.now.to_i
end
def relative_uri
"/form/#{#form_id}/elements"
end
def create_form
self.class.get(relative_uri, headers: generate_headers)
end
private
def generate_signature
OpenSSL::HMAC.hexdigest('sha256', ENV['SEAMLESS_GOV_SECRET'], "GET+#{relative_uri}+#{#timestamp}")
end
def generate_headers
{
"Authorization" => "HMAC-SHA256 api_key='#{#api_key}' signature='#{#signature}'",
"AuthDate" => #timestamp
}
end
end
end
any workaround this?
Headers are case-insensitive per the spec https://stackoverflow.com/a/41169947/1518336, so it seems like the server you're accessing is in the wrong.
Looking at Net::HTTPHeader, on which HTTParty is implemented
Unlike raw hash access, HTTPHeader provides access via case-insensitive keys
It looks like the class downcases the header keys for uniformity.
You'll likely need to look at a different networking library which doesn't rely on the net/http. Perhaps curb?
There is a work around this in the following article
https://github.com/jnunemaker/httparty/issues/406#issuecomment-239542015
I created the file lib/net_http.rb
require 'net/http'
class Net::HTTP::ImmutableHeaderKey
attr_reader :key
def initialize(key)
#key = key
end
def downcase
self
end
def capitalize
self
end
def split(*)
[self]
end
def hash
key.hash
end
def eql?(other)
key.eql? other.key.eql?
end
def to_s
def self.to_s
key
end
self
end
end
Then in the headers
def generate_headers
{
"Authorization" => "HMAC-SHA256 api_key='#{#api_key}' signature='#{#timestamp}'",
Net::HTTP::ImmutableHeaderKey.new('AuthDate') => "#{#timestamp}"
}
end
Has anyone seen this type of error before:
Undefined local variable or method ` ' for #<MyMailer:0x0000000c096530>
I am attempting to use ActionMailer to just send a basic email, but it keeps failing on the .deliver function.
Here is the necessary code:
def index
MyMailer.welcome_email(test).deliver
end
Controller code:
class ApplicationMailer < ActionMailer::Base
default from: "from#example.com"
layout 'mailer'
def welcome_email(user)
#user=user
puts "Message Should have sent"
mail(to: #user, subject: 'test')
end
end
Mailer code:
class MyMailer < ApplicationMailer
end
I'm learning how to work with HTTParty and API and I'm having an issue with my code.
Users/admin/.rbenv/versions/2.0.0-p481/lib/ruby/2.0.0/uri/generic.rb:214:in `initialize': the scheme http does not accept registry part: :80 (or bad hostname?)
I've tried using debug_output STDOUT both as an argument to my method and after including HTTParty to have a clue but with no success. Nothing gets displayed:
require 'httparty'
class LolObserver
include HTTParty
default_timeout(1) #timeout after 1 second
attr_reader :api_key, :playerid
attr_accessor :region
def initialize(region,playerid,apikey)
#region = region_server(region)
#playerid = playerid
#api_key = apikey
end
def region_server(region)
case region
when "euw"
self.class.base_uri "https://euw.api.pvp.net"
self.region = "EUW1"
when "na"
self.class.base_uri "https://na.api.pvp.net"
self.region = "NA1"
end
end
def handle_timeouts
begin
yield
#Timeout::Error, is raised if a chunk of the response cannot be read within the read_timeout.
#Timeout::Error, is raised if a connection cannot be created within the open_timeout.
rescue Net::OpenTimeout, Net::ReadTimeout
#todo
end
end
def base_path
"/observer-mode/rest/consumer/getSpectatorGameInfo"
end
def current_game_info
handle_timeouts do
url = "#{ base_path }/#{region}/#{playerid}?api_key=#{api_key}"
puts '------------------------------'
puts url
HTTParty.get(url,:debug_output => $stdout)
end
end
end
I verified my URL which is fine so I'm lost as to where the problem is coming from.
I tested with a static base_uri and it doesn't change anything.
The odd thing is when I do:
HTTParty.get("https://euw.api.pvp.net/observer-mode/rest/consumer/getSpectatorGameInfo/EUW1/randomid?api_key=myapikey")
Everything is working fine and I'm getting a response.
HTTParty doesn't seem to like the way you set your base_uri.
Unless you need it to be like that just add another attr_reader called domain and it will work.
require 'httparty'
class LolObserver
include HTTParty
default_timeout(1) #timeout after 1 second
attr_reader :api_key, :playerid, :domain
attr_accessor :region
def initialize(region,playerid,apikey)
#region = region_server(region)
#playerid = playerid
#api_key = apikey
end
def region_server(region)
case region
when "euw"
#domain = "https://euw.api.pvp.net"
self.region = "EUW1"
when "na"
#domain = "https://na.api.pvp.net"
self.region = "NA1"
end
end
def handle_timeouts
begin
yield
#Timeout::Error, is raised if a chunk of the response cannot be read within the read_timeout.
#Timeout::Error, is raised if a connection cannot be created within the open_timeout.
rescue Net::OpenTimeout, Net::ReadTimeout
#todo
end
end
def base_path
"/observer-mode/rest/consumer/getSpectatorGameInfo"
end
def current_game_info
handle_timeouts do
url = "#{domain}/#{ base_path }/#{region}/#{playerid}?api_key=#{api_key}"
puts '------------------------------'
puts url
HTTParty.get(url,:debug_output => $stdout)
end
end
end
I got a NoMethodError when trying to return a response in Sinatra. This is the error:
NoMethodError at /contact/book-me.php
undefined method `body=' for #<String:0x00000001911418>
/home/kerrick/.rvm/gems/ruby-1.9.3-p194/gems/sinatra-1.3.2/lib/sinatra/base.rb in body
response.body = value
/home/kerrick/.rvm/gems/ruby-1.9.3-p194/gems/sinatra-1.3.2/lib/sinatra/base.rb in invoke
body res
/home/kerrick/.rvm/gems/ruby-1.9.3-p194/gems/sinatra-1.3.2/lib/sinatra/base.rb in call!
invoke { dispatch! }
/home/kerrick/.rvm/gems/ruby-1.9.3-p194/gems/sinatra-1.3.2/lib/sinatra/base.rb in call
dup.call!(env)
/home/kerrick/.rvm/gems/ruby-1.9.3-p194/gems/rack-protection-1.2.0/lib/rack/protection/xss_header.rb in call
status, headers, body = #app.call(env)
[...]
This is the relevant code:
# Snipped, but basically populate the #error hash if the form wasn't filled out right
if #error.length == 0
#Snipped, but basically handle the success case
else
#response = ''
#error.each do |x, y|
#response << "<li>#{y}</li> \n"
end
return [400, #response]
end
Why is this happening?
Using #response as your return value in Sinatra is what's causing your problem. The same problem is documented on the Ruby on Rails OldWiki, so it's not just Sinatra specific. You should change your code to look something like this and it'll work:
else
#send_errors = ''
#error.each do |x, y|
#send_errors << "<li>#{y}</li> \n"
end
return [400, #send_errors]
end
Hi I have something like the folowing:
class TrialRequest
attr_accessor :trial_email
def initialize(email)
#trial_email = email
puts "Trial_email: #{trial_email}"
end
def create
#email = ::Gmail.connect!(gmail_name, gmail_password) do |gmail|
email = gmail.compose do
to 'trial#domain.com'
from trial_email
subject trial_email
text_part do
content_type 'text/html; charset=UTF-8'
body 'Sign me up.'
end
end
#binding.pry
gmail.deliver!(email)
end
end
end
The problem is that inside the compose block trial_email is not defined:
NameError: undefined local variable or method `trial_email' for #<Mail::Message:0x0000000431b830>
Is this a Ruby 1.9 issue or a gmail gem issue?
How should I go about making this method 'visible'/within the scope of the compose block?
Update:
This is an issue/feature of the gmail gem - ruby 1.9 blocks have changed but not this much!
In addition to the accepted answer, another workaround is to pass the data in as a method parameter:
def create(trial_email)
...
end
Looks like a GMail issue to me. Inside the blocks, self will be some object from the GMail gem so that you can have to, from, and similar DSL niceties available. You should be able to put self.trial_email into a local variable and then access that inside the blocks:
email_address = self.trial_email
#email = ::Gmail.connect!(gmail_name, gmail_password) do |gmail|
email = gmail.compose do
to 'trial#domain.com'
from email_address
subject email_address
#...
You're expecting (as you're entitled to) that the block should preserve the value of self, as it usually does. It looks like the gmail gem is using instance_exec here which allows it to change the value of self for the block to an instance of Mail::Message (which is why you can call to and from in that block even though you define no such methods)
While instance_exec is handy for producing nice DSLs, it is not without its downsides. Local variable scope isn't affected so you could store either trial_email or self in a local variable prior to the block and then use that local variable inside the block
The problem is that the block you pass to compose method is later passed to Mail.new and finally to Message.new (if I traced the chain correctly) and then this block is evaluated like that here:
instance_eval(&block)
As it's performed inside initialize method of a different object (instance of Message class) you do not have access to attributes of your TrialRequest object.
You can do the same thing without having any troubles like that:
email = gmail.compose
email.to = 'trial#domain.com'
email.from = trial_email
email.subject = trial_email
email.text_part do
content_type 'text/html; charset=UTF-8'
body 'Sign me up.'
end