I'm running into an issue mocking a method within a class using Ruby's MiniTest::Mock and stub feature and it's driving me insane. What I am trying to do is set up a way to get an access token by calling a Ruby API which will get the actual access token from a 3rd party site.
This is the class all work will be done in.
class ThisClass
attr_reader :hauler
def initialize(hauler)
#hauler = hauler
end
def get_access_token
access_token = Rails.cache.read("access_token_#{hauler.id}")
if access_token.blank?
access_token = ext_access_token
Rails.cache.write("access_token_#{#hauler.id}", access_token, { expires_in: 3600 })
end
access_token
end
def ext_access_token
# call to external url to get access_token
# Successful response will be { "data": { "authToken": "new-token"} }"
url = URI.parse("http://www.fake-login.com/auth/sign_in")
res = Net::HTTP.post_form(url, "userName" => #hauler[:client_id], "password" => #hauler[:client_secret])
json_response = JSON.parse(res.body)
json_response["data"]["authToken"]
end
end
The test is as follows
class ThisClassTest < ActiveSupport::TestCase
test "Get Access Token" do
hauler = haulers(:one)
tc = ThisClass.new(hauler)
mock_client = MiniTest::Mock.new
mock_client.expect :ext_access_token, "\"{ \"data\": { \"authToken\": \"new-token\"} }\""
ThisClass.stub(:ext_access_token, mock_client) do
puts tc.get_access_token
end
assert_equal true, true
end
end
When I run my tests, I get the following error
Error:
ThisClassTest#test_Get_Access_Token:
NameError: undefined method `ext_access_token' for class `ThisClass'
I'm clearly doing something wrong since all I want is for the ext_access_token method to return the same data string so I can run logic against it, but very much failing. ThisClass is fairly simplistic but outlines the setup I'll be going with moving forward for more complex methods based on the return from the external site.
The test can't find ext_access_token method because it's looking for a class method on the ThisClass, rather than an instance method.
So what you need is something like
tc.stub :ext_access_token, mock_client do
puts tc.get_access_token
end
Related
I trying to use websockets to receive real time update on trading orders. If it sent "closed" then to run some function.
The problem is It only run in "one time". I have tried but still in the dark.
EDIT. I try to make it in class but I don't know why I can't pass variable(self.status) to another function pending(self) in same class
class check:
async def handler(self):
async with websockets.connect('wss://ftx.com/ws/') as ws:
# authentication things
async for data in ws:
message = json.loads(data)
search = dictpy.DictSearch(data=message, target='closed')
self.status = search.result
print(self.status) # It contain 'closed'
async def pending(self):
print(self.status) # but It didn't even recieve from handler
if self.status == [['data.status', {'status': 'closed'}]]:
t3.start()
t4.start()
t3.join()
t4.join()
I tried everything and I couldn't test the validators. I want to test a flask app that has validators.
This is the code I am trying to test.
class Createform(FlaskForm):
name = StringField('Name:', validators=[DataRequired(), Length(min=2, max=50)])
submit = SubmitField('Submit')
def validate_name(self, name):
name = Chef.query.filter_by(name=name.data).first()
if name:
raise ValidationError ('This name is taken')
I also could not test the following;
#app.route('/createchef', methods = ['POST','GET'])
def createchef():
createform = Createform()
if createform.validate_on_submit():
chef = Chef(name=createform.name.data)
db.session.add(chef)
db.session.commit()
return redirect(url_for('read'))
return render_template('createchef.html', form=createform)
Hi guys currently i'm building reply message using ruby, here is my code
def reply(user_id, subject, to, body, thread_id)
client = google_client user_id
token = Token.find_by_user_id(user_id)
access_token = token.access_token
gmail = Google::Apis::GmailV1::GmailService.new
gmail.authorization = client
message = Mail.new
message.date = Time.now
message.subject = "#{subject}"
message.from = token.email
message.to = "#{to}"
message.thread_id = "#{thread_id}"
message.part content_type: 'multipart/alternative' do |part|
part.html_part = Mail::Part.new(body: "#{body}", content_type: 'text/html; charset=UTF-8')
end
# attach file
open('/Users/jaisanasrulloh/Downloads/image.png') do |file|
message.attachments['image.jpg'] = file.read
end
msg = message.encoded
message_object = Google::Apis::GmailV1::Message.new(raw:message.to_s)
gmail.send_user_message('me', message_object)
end
but i got error like below:
NoMethodError: undefined method `thread_id=' for #<Mail::Message:0x007fcedb6b9c78>
My question is how to add thread_id inside Mail::Message object ? Some person using class RMail::Message like this one
is there exist parameter thread_id inside Mail::Message ? if there exist how to add it?
thread_id is a part of the Gmail message object not the Mail object.
Add it here
message_object = Google::Apis::GmailV1::Message.new(raw:message.to_s, thread_id:thread_id)
and remove this:
message.thread_id = "#{thread_id}"
I am trying to use Ruby Sinatra to create a simple proxy for a specific web page. I can do it in C#, I just can't seem to work it out for Sinatra, the C# code is below:
<%# WebHandler Language="C#" Class="Map" %>
using System;
using System.Web;
using System.Net;
using System.IO;
public class Map : IHttpHandler {
static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[0x1000];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
output.Write(buffer, 0, read);
}
public void ProcessRequest(HttpContext context)
{
string gmapUri = string.Format("http://maps.google.com/maps/api/staticmap{0}", context.Request.Url.Query);
WebRequest request = WebRequest.Create(gmapUri);
using (WebResponse response = request.GetResponse())
{
context.Response.ContentType = response.ContentType;
Stream responseStream = response.GetResponseStream();
CopyStream(responseStream, context.Response.OutputStream);
}
}
public bool IsReusable {
get {
return false;
}
}
}
The Ruby Sinatra code I have tried is as follows:
require 'rubygems'
require 'sinatra'
get '/mapsproxy/staticmap' do
request.path_info = 'http://maps.google.com/maps/api/staticmap'
pass
end
I am assuming that the Sinatra one does not work (get a 404) as is is only passing the request to pages in the same domain. Any hep would be greatly appreciated.
EDIT:
With the Tin Man's help I've come up with a nice succinct solution, which works well for me:
get '/proxy/path' do
URI.parse(<URI> + request.query_string.gsub("|", "%7C")).read
end
Thanks for all the help.
If you want your Sinatra app to retrieve the URL, you'll need to fire up a HTTP client of some sort:
get '/mapsproxy/staticmap' do
require 'open-uri'
open('http://maps.google.com/maps/api/staticmap').read
end
I think this will work and is about as minimal as you can get.
You could use HTTPClient if you need more tweakability.
Also, I think that Rack can do it. Sinatra is built on top of Rack, but it's been a while since I played at that level.
I still need to find a way to extract the contentType from the response
From the Open-URI docs:
The opened file has several methods for meta information as follows since
it is extended by OpenURI::Meta.
open("http://www.ruby-lang.org/en") {|f|
f.each_line {|line| p line}
p f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
p f.content_type # "text/html"
p f.charset # "iso-8859-1"
p f.content_encoding # []
p f.last_modified # Thu Dec 05 02:45:02 UTC 2002
}
For your purposes something like this should work:
content_type = ''
body = open("http://www.ruby-lang.org/en") {|f|
content_type = f.content_type # "text/html"
f.read
}
I haven't tested that, but I think the return value of the block will be assigned to body. If that doesn't work then try:
content_type = ''
body = ''
open("http://www.ruby-lang.org/en") {|f|
content_type = f.content_type # "text/html"
body = f.read
}
but I think the first will work.
With the help of the Tin Man and TK-421 I've worked out a solution, see the Sinatra route below:
get '/proxy/path' do
require 'open-uri'
uri = URI.parse(<URI>)
getresult = uri.read
halt 200, {'Content-Type' => getresult.content_type}, getresult
end
Just replace the <URI> with the page you require, and you're good to go.
After some more playing this is what I've come up with:
get '/proxy/path' do
URI.parse(<URI> + request.query_string.gsub("|", "%7C")).read
end
As mentioned else where you need to require 'open-uri' at the top of the code. The reason for the gsub is that for some reason the parse fails if they are left in, and my browser doesn't encode them automatically.
I'm building a web frontend to a server-side application, using Pylons 1.0.
Right now I'm writing the first form, and I'm facing a problem concerning validation.. Using FormEncode and the #validate decorator I can easily validate the user input from a client-side perspective, but when I submit the data to the server, it may perform additional checks and eventually throw back exceptions that I need to show to the user.
My question: is there a concise way to integrate/emulate this exception handling into the FormEncode/validate flow? For example, redisplay the form with filled fields and an error message, as would happen if the exception had come from the #validate itself?
Here's what I have at the moment:
def edit(self, id):
return render('/edit_user.mako')
#validate(schema=form.UserForm(), form="edit")
def add_user(self):
if request.POST:
u = helpers.load_attributes(User(), self.form_result)
try:
model.save_to_server(u)
except MyBaseException, exc:
helpers.flash(unicode(exc))
return self.edit()
In this way, in case of a server-side exception I can see the "flash" message but the form of course will have empty fields :/
I like to implement:
from formencode import htmlfill
def create(self):
if request.params:
try:
Post.validate(request.paramse)
post = helpers.load_attributes(Post(), request.params)
model.save_to_server(post)
flash('OK', 'success')
redirect(...)
except InvalidException as e:
for key, message in e.unpack_errors().iteritems():
flash(message, 'error')
return htmlfill.render(render('/blogs/create.html'), request.params)
where my Post.validate:
#staticmethod
def validate(data):
schema = PostSchema()
schema.to_python(data)
In this way, if is the first time (request.params empty) html fills form with nothing, when user send datas html fills form with request.params
Another way (inspired by this answer) is to write a decorator similar to #validate that would catch the desired exceptions and use htmlfill to display their message:
def handle_exceptions(form):
def wrapper(func, self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except MyBaseException, e:
request = self._py_object.request
errors = { "exception" : unicode(e) }
params = request.POST
decoded = params.mixed()
request.environ['REQUEST_METHOD'] = 'GET'
self._py_object.tmpl_context.form_errors = errors
request.environ['pylons.routes_dict']['action'] = form
response = self._dispatch_call()
# If the form_content is an exception response, return it
if hasattr(response, '_exception'):
return response
htmlfill_kwargs2 = {}
htmlfill_kwargs2.setdefault('encoding', request.charset)
return htmlfill.render(response, defaults=params, errors=errors,
**htmlfill_kwargs2)
return decorator(wrapper)
The decorator would be used like:
#handle_exceptions("edit")
#validate(schema=form.UserForm(), form="edit")
def add_user(self):
if request.POST:
u = helpers.load_attributes(User(), self.form_result)
model.save_to_server(u)