Why am I getting the error "undefined local variable or method `assigns'"? - ruby-on-rails

I might be missing something basic here, but I'm stumped on this error:
model code:
class CachedStat < ActiveRecord::Base
def self.create_stats_days_ago(days_ago, human_id)
d = Date.today - days_ago.day
#prs = PageRequest.find(:all, :conditions => [ "owner_type = 'Human' and owner_id = ? and created_at = ?", human_id, d] )
end
end
spec code:
it "should create stats for the specified number of days in the past" do
CachedStat.create_stats_days_ago(1, Human.first.id)
assigns[:prs].should eql("foo")
end
The error is:
undefined local variable or method `assigns' for #<Spec::Rails::Example::ModelExampleGroup::Subclass_1:0x2fbac28>
I feel like I'm overlooking something obvious but it's invisible to me. Any suggestions?
Thanks very much!
-Jason

as neutrino said, assigns are only available in controllers/views specs, they're meaningless in a Model specs.
in your case example can look like
it "should create stats for the specified number of days in the past" do
CachedStat.create_stats_days_ago(1, Human.first.id).should eql("foo")
end

I could be wrong here, but assigns might be available only in controller specs.
Also, check your rspec version.

Related

why is ruby expecting wrong number of arguments

I have a rails app where I am setting up a method before_save activeRecord callback, like this:
class GroupEvent < ApplicationRecord
enum status: [ :published, :draft ]
before_save :calculate_and_set_dates
def calculate_and_set_dates
missing_properties = []
#check for available or set attributes
puts "sss" + self.startDate.to_s
if !self.startDate
missing_properties<<"startDate"
end
if !self.duration
missing_properties<<"duration"
end
if !self.endDate
missing_properties<<"endDate"
end
binding.pry
if missing_properties.length<=1
set_missing_property(missing_properties[0])
else
set_errors_for(missing_properties)
end
end
private
def set_missing_property(missing_property)
case missing_property
when "startDate"
self.startDate = self.endDate - self.duration
when "duration"
self.duration = self.endDate - self.startDate
when "endDate"
self.endDate= self.startDate +self.duration
end
end
end
Note: this note the complete class so don't worry about set_errors_for methods implimentation.
Now when, I create a groupEvent using GroupEvent.create(name:"hackaton",description:"hecking my life away",startDate: DateTime.now, duration:10). At point where set_missing_property(missing_properties[0]) is called ,I get an error for wrong number of arguments:
ArgumentError: wrong number of arguments (given 1, expected 0) from /Users/haroonAzhar/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/activerecord-6.0.3.4/lib/active_record/attribute_methods/read.rb:15:in startDate'
As you can see that the method set_missing_property has 1 argument in definition, why is it expecting 0? What's even more confusing is that: when i pass no argument to the 'set_missing_property' method I get this error:
ArgumentError: wrong number of arguments (given 0, expected 1) from /Users/haroonAzhar/Desktop/develop/whitespectre/app/models/group_event.rb:29:in set_missing_property'
I don't know why it's looking at
/Users/haroonAzhar/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/activerecord-6.0.3.4/lib/active_record/attribute_methods/read.rb:15:in startDate'
when I give one parameter/argument but i checked out the file it was suggesting and the part it is referring to looks like this:
module ClassMethods # :nodoc:
private
def define_method_attribute(name)
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
generated_attribute_methods, name
) do |temp_method_name, attr_name_expr|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{temp_method_name}
name = #{attr_name_expr}
_read_attribute(name) { |n| missing_attribute(n, caller) }
end
RUBY
end
end
end
what is even look for at that place when the defined method is right in the class ? but the real question to which I need answer is why is it expecting wrong number of arguments and how do I fix it?
Thankyou in advance for your help, really appreciate it :D
Alright guys, I solved it(Maybe)
So at endDate case inside my set_missing_property, I changed it to:
when "endDate"
self.endDate = self.startDate + self.duration
I believe I was calling the self.startDate with a argument which doesnt expect any,so it was throwing that error.
Thanks for giving it a look to all the peeps that gave their time to this. really appreciate it.

How is something like 30.seconds.ago implemented?

I found this question here.
And really curious to know the technical explanation of how something like 30.seconds.ago is implemented in Rails.
Method chaining? Numeric usage as per:
http://api.rubyonrails.org/classes/Numeric.html#method-i-seconds .
What else?
Here is the implementation of the seconds:
def seconds
ActiveSupport::Duration.new(self, [[:seconds, self]])
end
And, here is the implementation of the ago:
# Calculates a new Time or Date that is as far in the past
# as this Duration represents.
def ago(time = ::Time.current)
sum(-1, time)
end
And, here is the implementation of the sum method that's used inside the ago:
def sum(sign, time = ::Time.current) #:nodoc:
parts.inject(time) do |t,(type,number)|
if t.acts_like?(:time) || t.acts_like?(:date)
if type == :seconds
t.since(sign * number)
else
t.advance(type => sign * number)
end
else
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
end
end
end
To understand it fully, you should follow the method calls and look for their implementations in the Rails source code like I showed you just now.
One easy way to find a method definition inside Rails code base is to use source_location in your Rails console:
> 30.method(:seconds).source_location
# => ["/Users/rislam/.rvm/gems/ruby-2.2.2/gems/activesupport-4.2.3/lib/active_support/core_ext/numeric/time.rb", 19]
> 30.seconds.method(:ago).source_location
# => ["/Users/rislam/.rvm/gems/ruby-2.2.2/gems/activesupport-4.2.3/lib/active_support/duration.rb", 108]

Count Records within 30 days of today

I Have a model an Opportunity model that has an attribute called date_of_opportunity. I am trying to write a method that counts how many opportunities are within 30 days of today. However when I try to call my method in the console, I get the error 'undefined local variable or method'
Here is my model:
class Opportunity < ActiveRecord::Base
def calculate_num_days
num_days = 0
#opportunity = Opportunity.all
#opportunity.each do |opportunity|
if (opportunity.date_of_opportunity - Date.today < 30)
num_days = num_days + 1
return num_days
end
end
end
end
Can someone help me figure out whats wrong? Thanks!!
If you will get counts how many opportunities are within 30 days of today, you can try this :
class Opportunity < ActiveRecord::Base
def self.calculate_num_days(from = (Date.today-1.month).beginning_of_day,to = Date.today.end_of_day)
where(date_of_opportunity: from..to).count
end
end
And on your console you can type like this
Opportunity.calculate_num_days
Output looks like :
irb(main):001:0> Opportunity.calculate_num_days
←[0m←[1m←[35m (51.0ms)←[0m SELECT COUNT(*) FROM "opportunities" WHERE ("opportunities"."date_of_opportunity" BETWEEN '2014-05-04 00:00:00.000000' AND '2014-06-04 23:59:59.999999')
=> 2
You seem to want a class method, but are defining an instance method. Do this:
def self.calculate_num_days
...
end
Maybe, #opportunity = Opportunity.all should be #opportunities = Opportunity.all
Unless I am missing what you are trying to do I would let ActiveRecord do the heavy lifting. Opportunity.where("date_of_opportunity - :today < 30", today: Date.today).size
Disclaimer, this is completely untested

Rails NoMethodError in loop when method exists

Good day all.
I'm running into a bit of a problem getting a script running on my production environment, even though it works just fine on my dev box. I've verified that all the requisite gems and such are the same version.
I should mention that the script is intended to be run with the script/runner command.
Here is a super-condensed version of what I'm trying to do, centered around the part that's broken:
def currentDeal
marketTime = self.convertToTimeZone(Time.new)
deal = Deal.find(:first, :conditions => ["start_time ? AND market_id = ? AND published = ?", marketTime, marketTime, self.id, 1])
return deal
end
markets = Market.find(all)
markets.each do |market|
deal = market.currentDeal
puts deal.subject
end
Now convertToTimeZone is a method attached to the model. So, this code works just fine on my dev machine, as stated. However, attempting to run it on my production machine results in:
undefined method `subject' for nil:NilClass (NoMethodError)
If, however, I go into the console on the production box and do this:
def currentDeal
marketTime = self.convertToTimeZone(Time.new)
deal = Deal.find(:first, :conditions => ["start_time ? AND market_id = ? AND published = ?", marketTime, marketTime, self.id, 1])
return deal
end
market = Market.find(1)
deal = market.currentDeal
puts deal.subject
It returns the correct value, no problem. So what is going on?
This is on rails v 2.3.5, on both machines.
Thanks for any help
You are looping though all Markets on your production code, but your test snippet is only looking for one. The problem is that one of your Markets in your database has a currentDeal of nil (it has no object associated with it).
Run this on your production console instead.
markets = Market.find(all)
markets.each do |market|
deal = market.currentDeal
if deal
puts deal.subject
else
puts "NO currentDeal for Market with id: #{market.id}"
end
end
This will tell you exactly which Market record is exploding without a currentDeal.
So the question is how to fix it? Either all Markets are expected to have a currentDeal, or sometimes they don't and that's ok. If Market's should always have a currentDeal, then you need to adjust your validations to now allow a Market to be saved without a currentDeal. But given that the currentDeal is a time based thing, I would be that there is times when no deal is scheduled and therefore currentDeal will return nil.
So, more likely, you need to allow for the current deal to be nil. Your test code doesn't do this. It asks the market for the deal, and then the deal for it's subject. If the market return a nil deal, then you immediately ask nil for it's subject and you get the exception because nil does not have a method named subject. A few simple ways to nil protect you code:
deal = market.currentDeal
# simple if
if deal
puts deal.subject
end
# rails try method returns nil if the receiver is nil
# or executes the method if the object supports it
puts deal.try(:subject)
# ternary
puts deal ? deal.subject : "NO DEAL!"
# conditional execution
puts deal && deal.subject
Lastly, a ruby tip. This method is more complicated than it needs to be.
def currentDeal
marketTime = self.convertToTimeZone(Time.new)
deal = Deal.find(:first, :conditions => ["start_time ? AND market_id = ? AND published = ?", marketTime, marketTime, self.id, 1])
return deal
end
Ruby will always return the last expression's result in a method, and a has based conditions finder will clean up that query quite a bit.
def currentDeal
marketTime = self.convertToTimeZone(Time.new)
Deal.find(:first, :conditions => ["start_time > ? AND market_id = ? AND published = ?", marketTime, marketTime, id, true])
end
But this looks more like an association anyway. So you may want to use the association methods to clean this up further.
Clearly you are calling nil.subject, so Deal.find is returning nil in the production code. Your test case is only looking at one specific Market object, but the general case loops through Market objects. Your code needs to handle not finding a currentDeal for a Market object

How do you mock params when testing a Rails model's setter?

Given the code from the Complex Form part III how would you go about testing the virtual attribute?
def new_task_attributes=(task_attributes)
task_attributes.each do |attributes|
tasks.build(attributes)
end
end
I am currently trying to test it like this:
def test_adding_task_to_project
p = Project.new
params = {"new_tasks_attributes" => [{ "name" => "paint fence"}]}
p.new_tasks_attributes=(params)
p.save
assert p.tasks.length == 1
end
But I am getting the following error:
NoMethodError: undefined method `stringify_keys!' for "new_tasks_attributes":String
Any suggestions on improving this test would be greatly appreciated.
It looks as if new_task_attributes= is expecting an array of hashes, but you're passing it a hash. Try this:
def test_adding_task_to_project
p = Project.new
new_tasks_attributes = [{ "name" => "paint fence"}]
p.new_tasks_attributes = (new_tasks_attributes)
p.save
assert p.tasks.length == 1
end
Can we see the whole stack trace? Where does it think String#stringify_keys! is being called?
Also, params looks odd to me. Is tasks.build() expecting input like this: ["new_tasks_attribute", {"name" => "paint fence"}] ?
If not, maybe you actually want Hash#each_key() instead of Hash#each()?
Need more data. Also, you might consider a Ruby tag to accompany your Rails tag.

Resources