stack level too deep in rspec - ruby-on-rails

When I try to perform following code on specs it gives me stack level too deep. Works fine in the console.
def order_fulfillments_without_receipts
#order_fulfillments_without_receipts = []
OrderReconciliation.includes(:order_fulfillment).
where(data_entry_status: OrderReconciliation.data_entry_statuses[:pending_entry]).
find_in_batches do |group|
group.select do |reconciliation|
select_reconciliation?(reconciliation)
end
end
#order_fulfillments_without_receipts
end
def select_reconciliation?(reconciliation)
order_fulfillment = reconciliation.order_fulfillment
receipt_urls_empty = order_fulfillment.get_receipt_urls.empty?
order_fulfillment_id = order_fulfillment.id
#order_fulfillments_without_receipts << order_fulfillment_id
receipt_urls_empty || order_fulfillments_without_receipts.include?(order_fulfillment_id)
end
end
How should I fix it to avoid stack level too deep?

You have a bug in your code, last line of the select_reconciliation? method after the || you have order_fulfillments_without_receipts but I think you meant #order_fulfillments_without_receipts
Without the # you're calling the order_fulfillments_without_receipts method, hence the infinite loop.
Why this is happening in your tests and not in your console must be to do with what receipt_urls_empty is in each case, in your tests it's false and in your console it's true.

Related

How to DRY a list of functions in ruby that are differ only by a single line of code?

I have a User model in a ROR application that has multiple methods like this
#getClient() returns an object that knows how to find certain info for a date
#processHeaders() is a function that processes output and updates some values in the database
#refreshToken() is function that is called when an error occurs when requesting data from the object returned by getClient()
def transactions_on_date(date)
if blocked?
# do something
else
begin
output = getClient().transactions(date)
processHeaders(output)
return output
rescue UnauthorizedError => ex
refresh_token()
output = getClient().transactions(date)
process_fitbit_rate_headers(output)
return output
end
end
end
def events_on_date(date)
if blocked?
# do something
else
begin
output = getClient().events(date)
processHeaders(output)
return output
rescue UnauthorizedError => ex
refresh_token()
output = getClient().events(date)
processHeaders(output)
return output
end
end
end
I have several functions in my User class that look exactly the same. The only difference among these functions is the line output = getClient().something(date). Is there a way that I can make this code look cleaner so that I do not have a repetitive list of functions.
The answer is usually passing in a block and doing it functional style:
def handle_blocking(date)
if blocked?
# do something
else
begin
output = yield(date)
processHeaders(output)
output
rescue UnauthorizedError => ex
refresh_token
output = yield(date)
process_fitbit_rate_headers(output)
output
end
end
end
Then you call it this way:
handle_blocking(date) do |date|
getClient.something(date)
end
That allows a lot of customization. The yield call executes the block of code you've supplied and passes in the date argument to it.
The process of DRYing up your code often involves looking for patterns and boiling them down to useful methods like this. Using a functional approach can keep things clean.
Yes, you can use Object#send: getClient().send(:method_name, date).
BTW, getClient is not a proper Ruby method name. It should be get_client.
How about a combination of both answers:
class User
def method_missing sym, *args
m_name = sym.to_s
if m_name.end_with? '_on_date'
prop = m_name.split('_').first.to_sym
handle_blocking(args.first) { getClient().send(prop, args.first) }
else
super(sym, *args)
end
end
def respond_to? sym, private=false
m_name.end_with?('_on_date') || super(sym, private)
end
def handle_blocking date
# see other answer
end
end
Then you can call "transaction_on_date", "events_on_date", "foo_on_date" and it would work.

Spree error when using decorator with the original code

Need a little help over here :-)
I'm trying to extend the Order class using a decorator, but I get an error back, even when I use the exactly same code from source. For example:
order_decorator.rb (the method is exactly like the source, I'm just using a decorator)
Spree::Order.class_eval do
def update_from_params(params, permitted_params, request_env = {})
success = false
#updating_params = params
run_callbacks :updating_from_params do
attributes = #updating_params[:order] ? #updating_params[:order].permit(permitted_params).delete_if { |k,v| v.nil? } : {}
# Set existing card after setting permitted parameters because
# rails would slice parameters containg ruby objects, apparently
existing_card_id = #updating_params[:order] ? #updating_params[:order][:existing_card] : nil
if existing_card_id.present?
credit_card = CreditCard.find existing_card_id
if credit_card.user_id != self.user_id || credit_card.user_id.blank?
raise Core::GatewayError.new Spree.t(:invalid_credit_card)
end
credit_card.verification_value = params[:cvc_confirm] if params[:cvc_confirm].present?
attributes[:payments_attributes].first[:source] = credit_card
attributes[:payments_attributes].first[:payment_method_id] = credit_card.payment_method_id
attributes[:payments_attributes].first.delete :source_attributes
end
if attributes[:payments_attributes]
attributes[:payments_attributes].first[:request_env] = request_env
end
success = self.update_attributes(attributes)
set_shipments_cost if self.shipments.any?
end
#updating_params = nil
success
end
end
When I run this code, spree never finds #updating_params[:order][:existing_card], even when I select an existing card. Because of that, I can never complete the transaction using a pre-existent card and bogus gateway(gives me empty blanks errors instead).
I tried to bind the method in order_decorator.rb using pry and noticed that the [:existing_card] is actuality at #updating_params' level and not at #updating_params[:order]'s level.
When I delete the decorator, the original code just works fine.
Could somebody explain to me what is wrong with my code?
Thanks,
The method you want to redefine is not really the method of the Order class. It is the method that are mixed by Checkout module within the Order class.
You can see it here: https://github.com/spree/spree/blob/master/core/app/models/spree/order/checkout.rb
Try to do what you want this way:
Create file app/models/spree/order/checkout.rb with code
Spree::Order::Checkout.class_eval do
def self.included(klass)
super
klass.class_eval do
def update_from_params(params, permitted_params, request_env = {})
...
...
...
end
end
end
end

Keep getting Stack level too deep when calling API recursively

I am continuously calling an API and keep getting Stack level too deep error within minutes of call. I guess I am missing something basic here:
Class SomeModel
def self.call_api(index)
ref = SomeModel.get_reference
query ||= Api.call(:parameter => ref.parameter, :offset => index)
# .. doing stuff
if index >= 949
sleep(20)
new_num = Integer(number) + 1000
ref.update_attribute(:parameter, new_num)
SomeModel.call_api(1)
else
sleep(10)
begin
# This is a rescue for the case when API call returns nothing.
SomeModel.call_api(index+50)
rescue
new_num = Integer(number) + 1000
ref.update_attribute(:parameter, new_num)
SomeModel.call_api(1)
end
end
end
end
Ruby does not handle recursion well and will always eventually give you a stack level too deep. You're better off putting this inside some sort of loop that does not call the method recursively. If you want it to be a little smarter than just calling the method in an infinite loop, you can use something like EventMachine to respond to different events.

How to test the number of database calls in Rails

I am creating a REST API in rails. I'm using RSpec. I'd like to minimize the number of database calls, so I would like to add an automatic test that verifies the number of database calls being executed as part of a certain action.
Is there a simple way to add that to my test?
What I'm looking for is some way to monitor/record the calls that are being made to the database as a result of a single API call.
If this can't be done with RSpec but can be done with some other testing tool, that's also great.
The easiest thing in Rails 3 is probably to hook into the notifications api.
This subscriber
class SqlCounter< ActiveSupport::LogSubscriber
def self.count= value
Thread.current['query_count'] = value
end
def self.count
Thread.current['query_count'] || 0
end
def self.reset_count
result, self.count = self.count, 0
result
end
def sql(event)
self.class.count += 1
puts "logged #{event.payload[:sql]}"
end
end
SqlCounter.attach_to :active_record
will print every executed sql statement to the console and count them. You could then write specs such as
expect do
# do stuff
end.to change(SqlCounter, :count).by(2)
You'll probably want to filter out some statements, such as ones starting/committing transactions or the ones active record emits to determine the structures of tables.
You may be interested in using explain. But that won't be automatic. You will need to analyse each action manually. But maybe that is a good thing, since the important thing is not the number of db calls, but their nature. For example: Are they using indexes?
Check this:
http://weblog.rubyonrails.org/2011/12/6/what-s-new-in-edge-rails-explain/
Use the db-query-matchers gem.
expect { subject.make_one_query }.to make_database_queries(count: 1)
Fredrick's answer worked great for me, but in my case, I also wanted to know the number of calls for each ActiveRecord class individually. I made some modifications and ended up with this in case it's useful for others.
class SqlCounter< ActiveSupport::LogSubscriber
# Returns the number of database "Loads" for a given ActiveRecord class.
def self.count(clazz)
name = clazz.name + ' Load'
Thread.current['log'] ||= {}
Thread.current['log'][name] || 0
end
# Returns a list of ActiveRecord classes that were counted.
def self.counted_classes
log = Thread.current['log']
loads = log.keys.select {|key| key =~ /Load$/ }
loads.map { |key| Object.const_get(key.split.first) }
end
def self.reset_count
Thread.current['log'] = {}
end
def sql(event)
name = event.payload[:name]
Thread.current['log'] ||= {}
Thread.current['log'][name] ||= 0
Thread.current['log'][name] += 1
end
end
SqlCounter.attach_to :active_record
expect do
# do stuff
end.to change(SqlCounter, :count).by(2)

Controller show action code is flawed, but error is silent

Here is my User controller show action
def show
#public_groups = Group.public
#groups_member = #user.groups_as_member
#groups_as_owner = #user.groups_as_owner
#random_items = []
#assignments = []
unless #groups_member.nil?
until #random_items.count == 5 do
random_groups = #groups_member.sort_by{rand}.slice(0,5)
random_groups.each do |group|
assignments = Assignment.where(:group_id => group.id).limit(5).all
#assignments = Assignment.find_by_group_id(group.id)
y = Post.find_by_id(assignments.rand.post_id)
#random_items << y
end
end
end
end
I think it might be the way I am declaring the instance variable arrays #random_items and #assignments. I have no idea what the problem is though because my development and production servers don't give any compilation errors or anything.
When I comment out the big block of logic starting with the array declarations the site works.
I'd suggest you to perform a refactoring before you can find an error. Some principles before:
Any dataflow is about model layer responsibility
instance variables are use to share objects between ActionPack layers (controller and view)
use object's attributes instead of instance variables to easy to test
use associations and minimize Arel method and just find in controllers
With according to your code, it can be rewritten with:
# User model
def random_items
return unless groups_as_member
random_groups = groups_member.sort_by{rand}.slice(0,5)
random_groups.each do |group|
return if randorm_groups.length > 5
assignments = group.assignments.limit(5)
if y = Post.rand_by_post(assignments)
random_groups << y
end
end
return random_groups
end
# Post model
def self.rand_by_post(assignments)
find_by_id(assignments.rand.post_id)
end
Once you have the logic clear, you can find the bug and cover it with tests.

Resources