Quick Multiple calls to Firebase crashes Rails - ruby-on-rails-4

My controller pushes data to firebase on certain clicks.
class FirebaseController < ApplicationController
Firebase.base_uri = "https://firebaseProject.Firebaseio.com/"
def call_to_firebase
Firebase.push("firebase_channel", "firebase_data".to_json)
respond_to do |format|
format.json { render nothing: true, :status => 204 }
end
end
end
In case of quick successive calls to this controller, which is called on a click, my Puma server crashes immediately.
I am using Rails 4.0.0
Puma 2.6.0
Ruby 2.0.0
Below is a part of the huge log report generated.
ETHON: started MULTI
ETHON: performed EASY url= response_code=200 return_code=got_nothing total_time=2.663048
/Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/ethon-0.6.1/lib/ethon/multi/operations.rb:171: [BUG] Segmentation fault
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.3.0]
-- Crash Report log information --------------------------------------------
See Crash Report log file under the one of following:
* ~/Library/Logs/CrashReporter
* /Library/Logs/CrashReporter
* ~/Library/Logs/DiagnosticReports
* /Library/Logs/DiagnosticReports
the more detail of.
-- Control frame information -----------------------------------------------
c:0091 p:---- s:0489 e:000488 CFUNC :multi_perform
c:0090 p:0018 s:0484 e:000483 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/ethon-0.6.1/lib/ethon/multi/operations.rb:171
c:0089 p:0034 s:0479 e:000478 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/ethon-0.6.1/lib/ethon/multi/operations.rb:160
c:0088 p:0036 s:0474 e:000473 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/ethon-0.6.1/lib/ethon/multi/operations.rb:43
c:0087 p:0020 s:0470 e:000469 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/typhoeus-0.6.6/lib/typhoeus/hydra/runnable.rb:21
c:0086 p:0008 s:0466 e:000465 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/typhoeus-0.6.6/lib/typhoeus/hydra/memoizable.rb:51
c:0085 p:0104 s:0463 e:000462 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/firebase-0.1.4/lib/firebase/request.rb:50
c:0084 p:0019 s:0456 e:000455 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/firebase-0.1.4/lib/firebase/request.rb:20
c:0083 p:0019 s:0451 e:000450 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/firebase-0.1.4/lib/firebase.rb:34
.
.
.
c:0005 p:0027 s:0029 e:000028 METHOD /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/puma-2.6.0/lib/puma/server.rb:357
c:0004 p:0035 s:0022 e:000021 BLOCK /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/puma-2.6.0/lib/puma/server.rb:250 [FINISH]
c:0003 p:---- s:0016 e:000015 CFUNC :call
c:0002 p:0084 s:0011 e:000010 BLOCK /Users/siddharthbhagwan/.rvm/gems/ruby-2.0.0-p247/gems/puma-2.6.0/lib/puma/thread_pool.rb:92 [FINISH]
c:0001 p:---- s:0002 e:000001 TOP [FINISH]
.
.
.
[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html
Abort trap: 6
By quick I mean one click per second. This doesnt happen for slower clicks like 1 click per 2 seconds.
Pushing to firebase from the irb in a loop doesn't cause this error.
Thanks in Advance,
Cheers!

Are you using firebase-ruby gem? I submitted a bug fix for this issue today. You can hot patch it yourself by overriding the problematic method in the gem like so:
module Firebase
class Request
def process(method, path, body=nil, query_options={})
request = Typhoeus::Request.new(build_url(path),
:body => body,
:method => method,
:params => query_options)
response = request.run
Firebase::Response.new(response)
end
end
end
Or wait for the pull request to be accepted. The problem was in the gem's use of Typheous' Hydra.

Related

Ionic 3 Infinite Scroll 'cannot read property timestamp of null'

So I am using infinite scroll to load a very large reactive form in bits.
However I've noticed that if a form input event is triggered while the infinite scroll is loading other items this happens.
ERROR TypeError: Cannot read property 'timeStamp' of null
at InfiniteScroll._onScroll (infinite-scroll.js:229)
at SafeSubscriber.schedulerFn [as _next] (core.es5.js:3647)
at SafeSubscriber.__tryOrUnsub (Subscriber.js:238)
at SafeSubscriber.next (Subscriber.js:185)
at Subscriber._next (Subscriber.js:125)
at Subscriber.next (Subscriber.js:89)
at EventEmitterProxy.Subject.next (Subject.js:55)
at EventEmitterProxy.EventEmitter.emit (core.es5.js:3621)
at ScrollView.scroll.onScroll (content.js:378)
at ScrollView.setScrolling (scroll-view.js:52)
at ScrollView.scrollTo (scroll-view.js:401)
at Content.scrollTo (content.js:433)
at TextInput._jsSetFocus (input.js:524)
at TextInput._pointerEnd (input.js:496)
at Object.eval [as handleEvent] (TextInput.ngfactory.js:130)
at Object.handleEvent (core.es5.js:11998)
at Object.handleEvent (core.es5.js:12717)
at dispatchEvent (core.es5.js:8614)
at core.es5.js:9228
at HTMLDivElement.<anonymous> (platform-browser.es5.js:2648)
at HTMLDivElement.wrapped (raven.js:350)
at t.invokeTask (polyfills.js:3)
at Object.onInvokeTask (core.es5.js:3881)
at t.invokeTask (polyfills.js:3)
at r.runTask (polyfills.js:3)
at e.invokeTask [as invoke] (polyfills.js:3)
at p (polyfills.js:2)
at HTMLDivElement.v (polyfills.js:2)
console.(anonymous function) # console.js:32
defaultErrorLogger # core.es5.js:1020
ErrorHandler.handleError # core.es5.js:1080
IonicErrorHandler.handleError # ionic-error-handler.js:61
webpackJsonp.381.SentryErrorHandler.handleError # sentry-
errorhandler.ts:11
(anonymous) # core.es5.js:9232
(anonymous) # platform-browser.es5.js:2648
wrapped # raven.js:350
t.invokeTask # polyfills.js:3
onInvokeTask # core.es5.js:3881
t.invokeTask # polyfills.js:3
r.runTask # polyfills.js:3
e.invokeTask # polyfills.js:3
p # polyfills.js:2
v # polyfills.js:2
It's really driving me crazy because not even a try catch can stop this error from crashing the app.
Please I need help!!
This is a current ionic issue, that unfortunately doesn't look like it will be patched before v4.
bug(infinite-scroll): throws uncatchable error if scrollToTop is called before it is resolved
The quickest way to try and avoid the issue (it doesn't work everytime) is to wrap a setTimeout around the scrollToTop / scrollToBottom
setTimeout(function(){
this.content.scrollToBottom()
},200);
I was experiencing a similar issue, ran into this:
TypeError: Cannot read property 'enableEvents' of null
at EventEmitterProxy.enableScrollListener [as onSubscribe]
This would happen intermittently after a function on a different Ionic page that refreshed external content within our app. I never figured out exactly why, but I noticed that it was only happening when the infinite-scroll had been triggered at some point during user interaction with the original page.
In our case, I was setting the InfiniteScroll instance to a local, component-level variable so that I could use the .enable() hooks programmatically in other functions, like so:
VIEW:
<ion-infinite-scroll (ionInfinite)="lazyLoad($event)"> ...
CONTROLLER:
public lazyLoad(infiniteScroll: InfiniteScroll) {
// ASSIGN THE LAZY LOADER INSTANCE ON FIRST RUN SO IT CAN BE ENABLED / DISABLED ELSEWHERE
this.lazyLoader = infiniteScroll;
....
}
But, then we would run into the above TypeError randomly when refreshing content, so it was almost like there was a call somewhere trying to reference that variable. So I just set the variable to null when the page left the view, and now it works. Weirdness.
(NOTE: all calls to the instance of InfiniteScroll were wrapped in an if (this.lazyLoader && this.lazyLoader !== null) gate to begin with so I'm not really sure what went wrong)
ionViewWillLeave() {
this.lazyLoader = null;
}
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
#Component({...})
export class MyPage{
scrollToTop() {
this.navCtrl.setRoot('MyPage'); //so you will go to the top of page.
}
}
Go to infinite-scroll.js
Check if ev is null
If null return 3;
//Do nothing

Rails running methods inappropriately

A rails controller defines a before_action
before_action :set_intervento, only: [ ... ]
to
def set_intervento
#intervento = Intervento.find(params[:id])
end
But my action risultati_fatturati is not listed in the only block. yet error Couldn't find Intervento with 'id'=risultati_fatturatiis being generated where Rails justifying itself as having to run the set_intervento method. What else is at work here that is launching the method?
EDIT
routes show
resources :interventos do
collection do
get :indexmio
get :indexmio_programmati
get :passati
get :non_assegnati
get :programmati
get :domani
get :domani_e_dopo
get :settimana
get :planning
get :in_corso
get :successivi
get :successivo
get :passati_da_eseguire
get :articolo_adhoc
get :calendar
post :calendarc
get :settimanale
get :chiusi
post :ripresa
get :risultati_articolos
get :risultati_ordina_articolos
get :creare_checklist
post :neuter_ddt
post :cancel_ddt
get :elenco_ddt
get :dispatcher_check
get :manager_list
get :manager_comments
post :manager_check_required
get :preventivi_attivi
get :cerca_fatturati
get :non_fatturabili
post :risultati_fatturati
get :cerca_chiusi
post :risultati_chiusi
post :duplicate_intervento
get :ordinari_chiusi
get :ordinari_aperti
end
member do
get :pdfc
get :create_pdf_rapporto
get :refresh_pause_intervento
get :refresh_close_intervento
patch :update_successivo
patch :update_ddt
patch :materials_sucessivo
patch :materials_availability
patch :preventivare
patch :esito_preventivo
patch :scaduto_preventivo
patch :ripristina_preventivo
get :dispatcher_fix
patch :dispatcher_signature
get :correzione_fattura
end
end
Translating to
indexmio_interventos GET /interventos/indexmio(.:format) interventos#indexmio
indexmio_programmati_interventos GET /interventos/indexmio_programmati(.:format) interventos#indexmio_programmati
passati_interventos GET /interventos/passati(.:format) interventos#passati
non_assegnati_interventos GET /interventos/non_assegnati(.:format) interventos#non_assegnati
programmati_interventos GET /interventos/programmati(.:format) interventos#programmati
domani_interventos GET /interventos/domani(.:format) interventos#domani
domani_e_dopo_interventos GET /interventos/domani_e_dopo(.:format) interventos#domani_e_dopo
settimana_interventos GET /interventos/settimana(.:format) interventos#settimana
planning_interventos GET /interventos/planning(.:format) interventos#planning
in_corso_interventos GET /interventos/in_corso(.:format) interventos#in_corso
successivi_interventos GET /interventos/successivi(.:format) interventos#successivi
successivo_interventos GET /interventos/successivo(.:format) interventos#successivo
passati_da_eseguire_interventos GET /interventos/passati_da_eseguire(.:format) interventos#passati_da_eseguire
articolo_adhoc_interventos GET /interventos/articolo_adhoc(.:format) interventos#articolo_adhoc
calendar_interventos GET /interventos/calendar(.:format) interventos#calendar
calendarc_interventos POST /interventos/calendarc(.:format) interventos#calendarc
settimanale_interventos GET /interventos/settimanale(.:format) interventos#settimanale
chiusi_interventos GET /interventos/chiusi(.:format) interventos#chiusi
ripresa_interventos POST /interventos/ripresa(.:format) interventos#ripresa
risultati_articolos_interventos GET /interventos/risultati_articolos(.:format) interventos#risultati_articolos
risultati_ordina_articolos_interventos GET /interventos/risultati_ordina_articolos(.:format) interventos#risultati_ordina_articolos
creare_checklist_interventos GET /interventos/creare_checklist(.:format) interventos#creare_checklist
neuter_ddt_interventos POST /interventos/neuter_ddt(.:format) interventos#neuter_ddt
cancel_ddt_interventos POST /interventos/cancel_ddt(.:format) interventos#cancel_ddt
elenco_ddt_interventos GET /interventos/elenco_ddt(.:format) interventos#elenco_ddt
dispatcher_check_interventos GET /interventos/dispatcher_check(.:format) interventos#dispatcher_check
manager_list_interventos GET /interventos/manager_list(.:format) interventos#manager_list
manager_comments_interventos GET /interventos/manager_comments(.:format) interventos#manager_comments
manager_check_required_interventos POST /interventos/manager_check_required(.:format) interventos#manager_check_required
preventivi_attivi_interventos GET /interventos/preventivi_attivi(.:format) interventos#preventivi_attivi
cerca_fatturati_interventos GET /interventos/cerca_fatturati(.:format) interventos#cerca_fatturati
non_fatturabili_interventos GET /interventos/non_fatturabili(.:format) interventos#non_fatturabili
risultati_fatturati_interventos POST /interventos/risultati_fatturati(.:format) interventos#risultati_fatturati
cerca_chiusi_interventos GET /interventos/cerca_chiusi(.:format) interventos#cerca_chiusi
risultati_chiusi_interventos POST /interventos/risultati_chiusi(.:format) interventos#risultati_chiusi
duplicate_intervento_interventos POST /interventos/duplicate_intervento(.:format) interventos#duplicate_intervento
ordinari_chiusi_interventos GET /interventos/ordinari_chiusi(.:format) interventos#ordinari_chiusi
ordinari_aperti_interventos GET /interventos/ordinari_aperti(.:format) interventos#ordinari_aperti
pdfc_intervento GET /interventos/:id/pdfc(.:format) interventos#pdfc
create_pdf_rapporto_intervento GET /interventos/:id/create_pdf_rapporto(.:format) interventos#create_pdf_rapporto
refresh_pause_intervento_intervento GET /interventos/:id/refresh_pause_intervento(.:format) interventos#refresh_pause_intervento
refresh_close_intervento_intervento GET /interventos/:id/refresh_close_intervento(.:format) interventos#refresh_close_intervento
update_successivo_intervento PATCH /interventos/:id/update_successivo(.:format) interventos#update_successivo
update_ddt_intervento PATCH /interventos/:id/update_ddt(.:format) interventos#update_ddt
materials_sucessivo_intervento PATCH /interventos/:id/materials_sucessivo(.:format) interventos#materials_sucessivo
materials_availability_intervento PATCH /interventos/:id/materials_availability(.:format) interventos#materials_availability
preventivare_intervento PATCH /interventos/:id/preventivare(.:format) interventos#preventivare
esito_preventivo_intervento PATCH /interventos/:id/esito_preventivo(.:format) interventos#esito_preventivo
scaduto_preventivo_intervento PATCH /interventos/:id/scaduto_preventivo(.:format) interventos#scaduto_preventivo
ripristina_preventivo_intervento PATCH /interventos/:id/ripristina_preventivo(.:format) interventos#ripristina_preventivo
dispatcher_fix_intervento GET /interventos/:id/dispatcher_fix(.:format) interventos#dispatcher_fix
dispatcher_signature_intervento PATCH /interventos/:id/dispatcher_signature(.:format) interventos#dispatcher_signature
correzione_fattura_intervento GET /interventos/:id/correzione_fattura(.:format) interventos#correzione_fattura
interventos GET /interventos(.:format) interventos#index
POST /interventos(.:format) interventos#create
new_intervento GET /interventos/new(.:format) interventos#new
edit_intervento GET /interventos/:id/edit(.:format) interventos#edit
intervento GET /interventos/:id(.:format) interventos#show
PATCH /interventos/:id(.:format) interventos#update
PUT /interventos/:id(.:format) interventos#update
DELETE /interventos/:id(.:format) interventos#destroy
and the controller action
def risultati_fatturati
date = params[:intervento]
#date_start = Date.civil(date["dal(1i)"].to_i, date["dal(2i)"].to_i, date["dal(3i)"].to_i)
#date_end = Date.civil(date["al(1i)"].to_i, date["al(2i)"].to_i, date["al(3i)"].to_i)
if !params[:intervento][:invoicestate_id].blank?
#interventos = Intervento.joins(:signatures).where(['signatures.created_at >= ? AND signatures.created_at <= ? AND signatures.sequence = ? AND interventos.invoicestate_id = ?', #date_start.at_beginning_of_day, #date_end.at_end_of_day, "fine", params[:intervento][:invoicestate_id]]).paginate :page => params[:page], :per_page => 100
else
#interventos = Intervento.joins(:signatures).where(['signatures.created_at >= ? AND signatures.created_at <= ? AND signatures.sequence = ?', #date_start.at_beginning_of_day, #date_end.at_end_of_day, "fine"]).paginate :page => params[:page], :per_page => 100
end
render layout: "application"
end
so the action is within a collection and does not call #intervento. This is why I find it strange.
Edit #2
Side note: attempted to remove the before_action and find the member in each action, leading to identical results. Thus there is a deeper cause.
In an attempt to view things differently and understand via triangulation, I attempted a theoretically simpler process. That of running a partial for updating each record
<%= render "stato_fatturazione", intervento: intervento %>
whose form calls a specific action, which will invoke Rails's ujs
<%= form_for stato_fatturazione_intervento_path(intervento), method: :patch, remote: true, data: {disable_with: "Aggiornando..."} do |f| %>
Alas, notwithstanding the parameters, that have the proper path for editing, rails is attempting to patch the page with the array of records, which is by definition unpatchable, and leads to the same Couldn't find Intervento with 'id'=risultati_fatturati
Started PATCH "/interventos/risultati_fatturati" for ::1 at 2017-07-31 10:52:14 +0200
Processing by InterventosController#update as JS
Parameters: {"utf8"=>"✓", "/interventos/123/stato_fatturazione"=>{"invoicestate_id"=>"3", "in_zucchetti"=>"475", "fatturato"=>"1"}, "commit"=>"Aggiorna", "id"=>"risultati_fatturati"}
The rendered HTML does confirm the target action is being intercepted by some method:
<form data-disable-with="Aggiornando..." action="/interventos/risultati_fatturati" accept-charset="UTF-8" data-remote="true" method="post">

Rails 4 ActionMailer - How to capture connection and SMTP errors when using deliver_later

I am using ActionMailer deliver_later w ActiveJobs / Sidekiq using :
config.active_job.queue_adapter = :sidekiq
as I am testing in dev mode w MailCatcher, the only error I can catch is Errno::ECONNREFUSED, setting port 1026 instead of 1025
When I test deliver_now, I get the Errno::ECONNREFUSED error raised, which is fine
begin
MessageMailer.contact_me_email(#message).deliver_now
rescue Errno::ECONNREFUSED, Net::SMTPAuthenticationError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError => e
flash[:error] = "Problems sending mail. Please try again later"
# delete message or resend it ?
byebug
#message.destroy
format.html { render :new }
end
However, when I request a deliver_later, then the request is sore in the sidekiq mailers queue, and the error is not captured...
begin
MessageMailer.contact_me_email(#message).deliver_later(wait: 1.minute ) # in mailers queue
rescue Errno::ECONNREFUSED, Net::SMTPAuthenticationError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError => e
flash[:error] = "Problems sending mail. Please try again later"
# delete message or resend it ?
byebug
#message.destroy
format.html { render :new }
end
How can I capture and rescue from SMTP errors in this case ? I guess it's a Sidekiq responsibility to handle it ... any hint welcome..
ActiveJobs allows you to rescue errors that occur when the job is performed using the rescue_from method. See the api. You will need to implement this method in your ActiveJob subclasses.
Since ActionMailer uses DeliveryJobs to deliver the messages as jobs you will need to add a rescue_from method to this class to handle the exception.
For example, you could place in config/initializers/action_mailer.rb:
ActionMailer::DeliveryJob.rescue_from(StandardError) do |exception|
Rails.logger.error "Original record not found: #{#serialized_arguments.join(', ')}"
end
This will cleanly rescue any StandardErrors, and simply log them instead of raising an exception. I implemented this myself to quiet Rollbar.
Since it's a corner case, it's a good idea to test it as well:
spec/initializers/action_mailer_spec.rb:
RSpec.describe 'ActionMailer::DeliveryJob error recovery' do
it 'should log the deserialization errors' do
#user = create(:user)
MyCustomMailer.send_something(#user).deliver_later
#user.destroy
expect(Rails.logger).to receive(:error).at_least(:once)
Delayed::Worker.new.work_off
end
end

VCR is not recording cassettes on successful requests, only on failed ones

I have a simple test to fetch one Facebook object. I'm using Curl for the request.
it "gets an object from Facebook" do
VCR.use_cassette('facebook') do
url = "https://graph.facebook.com/<ID>?access_token=#{#access_token}&#{query_string}"
curl = Curl::Easy.perform(url)
expect(curl.body_str).to eql('<my object>')
end
end
My VCR configs are:
VCR.configure do |c|
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
c.hook_into :webmock
end
When I run the tests, it passes, and the following is logged:
[Cassette: 'facebook'] Initialized with options: {:record=>:once, :match_requests_on=>[:method, :uri], :allow_unused_http_interactions=>true, :serialize_with=>:yaml, :persist_with=>:file_system}
[webmock] Handling request: [get https://graph.facebook.com/<ID>?access_token=<TOKEN>&fields=%5B%22id%22,%22account_id%22,%22name%22,%22campaign_group_status%22,%22objective%22%5D] (disabled: false)
[Cassette: 'facebook'] Initialized HTTPInteractionList with request matchers [:method, :uri] and 0 interaction(s): { }
[webmock] Identified request type (recordable) for [get https://graph.facebook.com/<ID>?access_token=<TOKEN>&fields=%5B%22id%22,%22account_id%22,%22name%22,%22campaign_group_status%22,%22objective%22%5D]
But the cassette is not recorded and the dir is empty. I've tried :record => :all to same results.
Usually, people encountered this error when using incompatible hooks for the library they're using, but that's not the case. I'm using webmock and curb.
Curiously, the cassette is recorded when there's a failure in the request, e.g., the token is expired. When it's fixed, and I delete the file, it's not recorded again.
Have anyone had the same problem?
It turns out that my code was a little more complicated than above and was executing a callback after perfoming the request. Something like:
success_handler = Proc.new { return c.body_str }
curl.on_success do |easy|
success_handler.call(easy)
end
That bypasses VCR and the file is not written. Refactoring the code to not use callbacks works.

How do I access sinatra settings in custom extension specs?

I have an email helper extension I have written for my application. I use settings I have defined on the application like so:
set :mailgun_options, JSON.parse(File.open('config/mailer.json').read)[ENV['RACK_ENV']]
which reads configuration settings from a json file into the global Sinatra settings.
I then have my email helper which references these settings to create connections and what not.
require 'sinatra/base'
require 'faraday'
module Sinatra
module EmailHelper
def connect opts={}
connection = Faraday.new(:url => settings.mailgun_options['mailgun_url']) do |faraday|
if settings.mailgun_options['test']
faraday.adapter :test do |stubs|
stubs.post(settings.mailgun_options['domain'] + '/messages') {[ 200, {}, 'success' ]}
end
else
faraday.request :url_encoded
faraday.adapter Faraday.default_adapter
end
end
connection.basic_auth('api', settings.mailgun_options['key'])
return connection
end
def send_email params={}
connect.post((settings.mailgun_options['domain'] + '/messages'), params)
end
def send_password_reset_email email
template = File.open( File.expand_path( '../../helpers/email_helper/templates/password_reset.erb', __FILE__ ), 'r' ).read
send_email({
:from => 'REscour <noreply#' + settings.mailgun_options['domain'] + '>',
:to => email,
:subject => 'REscour.com Password Reset',
:text => ERB.new(template).result(binding)
})
end
end
end
So, when I am trying to write my specs to test the "connect" method, I get the following error:
undefined local variable or method `settings' for #<Class:0x007faaf9c9bd18>
My spec code is as follows:
module Sinatra
describe EmailHelper
let(:dummy_class) { Class.new { extend EmailHelper } }
it 'should connect to mailgun\'s api' do
app.set :mailgun_options, ::JSON.parse(File.open('config/mailer.json').read)['test']
puts dummy_class.connect
end
end
end
I'm not sure how to interact with the global Sinatra settings from my specs to have the local "settings" variable be defined during execution of the helper code.
I could have the architecture of my application wrong here for all I know. Does anyone have any experience in writing Sinatra extensions using the global settings and write specs for them?
The spec you've written seems to want to be a unit test, because you've not run the extension the way it would be used in an app. In the specs, try something like:
describe "connect" do
let(:app) { Sinatra.new do
helpers Sinatra::EmailHelper
configure do
:mailgun_options, ::JSON.parse(File.open('config/mailer.json')
end
get "/" do
connect
end
end
}
# now write specs for the `connect` helper
end
Using an anonymous class (Sinatra.new do…) lets you quickly set up an app that's convenient for a small test, but there are other ways too.
In the extension, you need to add:
helpers EmailHelper
as per the example in the docs.