NoMethodError: undefined method `<<' for nil:NilClass - ruby-on-rails

I have a task that I did not build but is no longer working. i am new to ruby on rails and have not found a solution for this problem yet.
when i run the task i Get:
this is the task:
desc "Archive Loads/Trucks that have expired."
task :expire_old_posts => :environment do
expired_loads = []
loads = Load.where("pickup < NOW()")
loads.each {|load| expired_loads << load.attributes }
ArchivedLoad.create( expired_loads )
loads.delete_all
end
little Debugging thru console
Confirms there are 120530 loads to transfer:
loads = Load.where("pickup < NOW()").count
2014-07-23 12:55:56 DEBUG -- (149.8ms) SELECT COUNT(*) FROM "loads" WHERE (pickup < NOW())
=> 120530
create the empty array:
expired_loads = []
=> []
run the where command
loads = Load.where("pickup < NOW()")
lots and lots of records... i limited it to two for testing
1.9.3-p547 :006 > loads = Load.where("pickup < NOW()").limit(2)
2014-07-23 13:02:07 DEBUG -- Load Load (74.2ms) SELECT "loads".* FROM "loads" WHERE (pickup < NOW()) LIMIT 2
=> [#<Load id: 18398947, user_id: 11074, origin: #<RGeo::Geographic::SphericalPointImpl:0x595f056 "POINT (-80.48237609863281 37.772544860839844)">, dest: #<RGeo::Geographic::SphericalPointImpl:0x595ec64 "POINT (-78.30302429199219 40.295894622802734)">, length: 48, comments: "~PostEverywhere_20140721140916~", ltl: false, rate: nil, delivery: "2014-07-22 17:00:00", pickup: "2014-07-21 17:00:00", weight: 48, equipment_id: 8, covered: false, created_at: "2014-07-21 19:16:16", updated_at: "2014-07-21 19:16:16", owner: nil, deleted: false, origin_city: "ronceverte", origin_state: "wv", dest_city: "martinsburg", dest_state: "pa">, #<Load id: 18398948, user_id: 11074, origin: #<RGeo::Geographic::SphericalPointImpl:0x553cd2a "POINT (-81.035400390625 37.384891510009766)">, dest: #<RGeo::Geographic::SphericalPointImpl:0x553c9d8 "POINT (-79.80570983886719 40.317527770996094)">, length: 48, comments: "~PostEverywhere_20140721140916~", ltl: false, rate: nil, delivery: "2014-07-22 17:00:00", pickup: "2014-07-21 17:00:00", weight: 48, equipment_id: 8, covered: false, created_at: "2014-07-21 19:16:16", updated_at: "2014-07-21 19:16:16", owner: nil, deleted: false, origin_city: "princeton", origin_state: "wv", dest_city: "greenock", dest_state: "pa">]
Really not sure what the << is but i assume this is just distributing the attributes so that they can be saved in the other table
loads.each {|load| expired_loads << load.attributes }
Errors here with
**NoMethodError: undefined method `<<' for nil:NilClass**
Next line is: Saves them to archived_loads table and deletes from loads table
ArchivedLoad.create( expired_loads )
loads.delete_all

Why not use #map instead? expired_loads = loads.map(&:attributes) or better yet just create a method in Load to archive them e.g.
class Load
scope :expired_loads, -> {where("pickup < NOW()")}
def self.archive_now
ArchiveLoad.create(expired_loads.map(&:attributes))
expired_loads.delete_all
end
end
Then you can call Loads.archive_now I am unsure of any of your structure this is just a suggestion as to a more concise implementation.
It also might be prudent to post the create method of your ArchivedLoad in case you have utilized the << method inside of there as well. If you have not implemented some kind of a custom create method I think you need a iteration instead such as
expired_loads.map(&:attributes).each{|load| ArchiveLoad.create(load)}
Also << is similar to push it adds an object to the end of an Array

Related

ActiveRecord query returns different results for two in-memory instances of the same database object

In a console session, repeatably, If I access a certain model instance by pulling out of an array, I get incorrect results when calling a method on that object. However, if I load the same object directly from the database with find(id), the same method returns the correct results.
How can this happen? The really crazy part is that for the first two elements of the array, everything is normal. As you can see at the end of the console output, results for the first two elements in the array (they are all the same type and all have two Link children) make sense. The latter five should not be empty.
# Instance from an array
irb(main):417:0> the_array = User.find(89).utterances.select{|u| u.links.count > 1}
=> [#<Utterance id: ...>, #<Utterance id: 6931, ...>, #<Utterance id: 6935, ...>, #<Utterance id: 6944, ...>, #<Utterance id: 6955, ...>, #<Utterance id: 6989, ...>, #<Utterance id: 7014, ...>]
irb(main):418:0> from_array = the_array.last
=> #<Utterance id: 7014, ...>
irb(main):419:0> from_array.class
=> Utterance(id: integer, index: integer, begins_at: float, text: text, created_at: datetime, updated_at: datetime, ends_at: float, recording_id: integer)
irb(main):420:0> from_array.links.class
=> Link::ActiveRecord_Associations_CollectionProxy
irb(main):421:0> from_array.links
=> #<ActiveRecord::Associations::CollectionProxy []>
# Instance from find
irb(main):422:0> from_find = Utterance.find(7014)
Utterance Load (1.8ms) SELECT "utterances".* FROM "utterances" WHERE "utterances"."id" = $1 LIMIT $2 [["id", 7014], ["LIMIT", 1]]
=> #<Utterance id: 7014, ...>
irb(main):423:0> from_find.class
=> Utterance(id: integer, index: integer, begins_at: float, text: text, created_at: datetime, updated_at: datetime, ends_at: float, recording_id: integer)
irb(main):424:0> from_find.links.class
=> Link::ActiveRecord_Associations_CollectionProxy
irb(main):424:0> ap from_find.links
[
[0] #<Link:0x000055fc7f154150> {
:id => 212,
#...
},
[1] #<Link:0x000055fc7f15feb0> {
:id => 213,
#...
}
]
irb(main):425:0>
# But for the first two elements of the array, no problem
irb(main):486:0> the_array.each{|u| puts ap u.links}
[
[0] #<Link:0x000055fc80cc34e0> {
:id => 208,
...
},
[1] #<Link:0x000055fc80cc3328> {
:id => 209,
...
}
]
[
[0] #<Link:0x000055fc80cc7180> {
:id => 211,
...
},
[1] #<Link:0x000055fc80cc6fc8> {
:id => 210,
...
}
]
[]
[]
[]
[]
[]
Associations are cached. from_array.links is getting the cached set from when it was first called, from_find.links is getting a fresh copy from the database. Presumably some links were added between the first call to from_array.links and from_find.links.
If you run from_array.links.reload it will refresh the cached association and you should get the same result as from_find.links.
You can avoid stale association caches by altering the association using methods on the collection. For example, if you want to create a new link...
# This will update the utterance.links cache
utterance.links.create!( some: "attribute" )
# This will not.
link = Links.create!(
some: "attribute",
utterance: utterance
)
# This will.
utterance.links << link
# This will not.
link.destroy
# This will.
utterance.links.destroy(link)
Note that: User.find(89).utterances.select{|u| u.links.count > 1} is inefficient. It must load each Utterance and count its links individually, known as an N+1 query (or 1+N).
It's more efficient to do this with a join and a group by.
User.find(89).utterances
.joins(:links)
.group('utterances.id')
.having('count(utterances.id) > 1')
The answer turns out to be that my console session was somehow corrupt. I took the following steps and then all of the calls to the links mtehod on the Utterance model, from the array, worked as expected.
Quit the console
rails c
Paste this code:
the_array = Recording.find(89).utterances.select{|u| u.links.count > 1}
from_array = the_array.last
from_array.class
from_array.links.class
from_array.links
from_find = Utterance.find(7014)
ap from_find.id
ap from_find.class
from_find.links.class
ap from_db.links
the_array.each{|u| puts ap u.links}
Step 3 is the same thing I was doing in the previous console session. Apparently I had somehow borked the previous session's stack.

and sometimes I get failed spices because of Time.now, can't understand why

so i have a method in model
class << self
def last_week
start = Time.zone.now.beginning_of_week - 7.days
finish = start + 7.days
where('appointment_at >= ? AND appointment_at < ?', start, finish).order(appointment_at: :desc)
end
end
And I write spec for this method.
RSpec.describe Appointment, type: :model, vcr: { record: :none } do
let!(:time) { Time.now }
let(:appointment_at) { time }
context '.last_week' do
let!(:scoped_appointment) { create(:appointment, appointment_at: time - 2.days) }
let!(:another_appointment) { create(:appointment, appointment_at: time - 16.days) }
it do
travel_to(time) do
expect(Appointment.last_week).to include(scoped_appointment)
expect(Appointment.last_week).not_to include(another_appointment)
end
end
end
end
And sometime i get failed this spec with error.
expected #<ActiveRecord::Relation []> to include #<Appointment id: 18, lead_id: 27, body: nil, appointment_at: "2019-02-25 00:59:47", google_id: nil, ... "pending", user_id: 22, notify: nil, cc_emails: nil, appointment_minutes: nil, status_message: nil>
Diff:
## -1,2 +1,2 ##
-[#<Appointment id: 18, lead_id: 27, body: nil, appointment_at: "2019-02-25 00:59:47", google_id: nil, created_at: "2019-02-27 00:59:47", updated_at: "2019-02-27 00:59:47", timezone: nil, subject: "Meeting with Lead", address: nil, notification: nil, status: "pending", user_id: 22, notify: nil, cc_emails: nil, appointment_minutes: nil, status_message: nil>]
+[]
I can't understand why?
And I have a suggestion that I should tightly set time
in spec_helper.rb
$now = DateTime.parse('2020-01-01 00:00:01 -0500')
will it be right? and why ?
Your test setup is brittle. It will break depending on the day of the week you run your spec.
The scope in your model returns appointments from the previous week, Monday through Sunday (you are calling beginning_of_week and adding 7 days to it)
So if your tests run on a Wednesday, like in the example you provided, the appointment’s appointment_at field will be set to Monday (since you are calculating it as Time.now - 2.days). That means your scope will not cover that appointment.
I suggest you use a specific time in your setup. Given your current setup, using let(:time) { DateTime.parse('2019-02-25 00:00:00') } should work

Rails: why can I send a single record attribute via controller to new record but not array?

Creating articles via the controller in Rails. A simple method, which more or less works; just call the method from some other place and it generates a new article via the back end and fills in the values:
def test_create_briefing
a = Article.new
a.type_id = 27
a.status = 'published'
a.headline = 'This is a headline'
a.lede = 'Our article is about some interesting topic.'
a.body = test_article_text
a.save!
end
If test_article_text is just a single record, this works fine and prints the existing article body into the new article body. Looks right in the view and looks right in "edit". All perfect.
def test_article_text
a = Article.find_by_id(181)
a.body
end
But if I try to do the same thing with the last ten articles, it doesn't work:
def test_article_text
Article.lastten.each do |a|
a.body
end
end
In the view you get:
[#, #, #, #, #, #, #, #, #, #]
And in "edit" you get:
[#<Article id: 357, headline: "This is a headline", lede: "Our article is about some interesting topic.", body: "[#<Article id: 356, headline: \"This is a headline\"...", created_at: "2017-12-31 20:40:16", updated_at: "2017-12-31 20:40:16", type_id: 27, urgency: nil, main: nil, status: "published", caption: nil, source: nil, video: nil, summary: nil, summary_slug: nil, topstory: false, email_to: nil, notification_slug: nil, notification_message: nil, short_lede: nil, short_headline: nil, is_free: nil, briefing_point: nil>, #<Article id: 356, headline: "This is a headline"…etc, etc, etc.
What do I not know? What am I missing?
It is returned as below because the Article.lastten is the returned variable from your controller.
[#<Article id: 357, headline: "This is a headline", lede: "Our article is about some interesting topic.", body: "[#<Article id: 356, headline: \"This is a headline\"...", created_at: "2017-12-31 20:40:16", updated_at: "2017-12-31 20:40:16", type_id: 27, urgency: nil, main: nil, status: "published", caption: nil, source: nil, video: nil, summary: nil, summary_slug: nil, topstory: false, email_to: nil, notification_slug: nil, notification_message: nil, short_lede: nil, short_headline: nil, is_free: nil, briefing_point: nil>, #<Article id: 356, headline: "This is a headline"…etc, etc, etc.
To return all Article body, do as below:
def test_article_text
arr = Array.new
Article.lastten.each do |a|
arr << a.body
end
arr # should be added so it will be the last value returned from your controller
end
So, #Shiko was nearly right, certainly on the right path. Had to manipulate the array a bit and do two things to get it to work:
.join the bits of the array to strip out all of the rubbish;
Concatenate the different bits for each article in a different way than you normally do in a view. So to_sfor each of the attributes, concatenating things "" + "" and rebuilding the url with information available in the array (no link_to, etc.).
The "**"is markdown, because I'm using that, but I suppose you could bung html tags in there if you needed to.
This works:
def test_article_text
arr = Array.new
Article.lastten.each do |a|
arr << "**" + a.headline.to_s + "**: " + a.text.to_s + "[Read now](/articles/#{a.id}-#{a.created_at.strftime("%y%m%d%H%M%S")}-#{a.headline.parameterize})"
end
arr.join("\n\n")
end

Access attributes in ActiveRecord::Associations::CollectionProxy

I am trying to write a custom function that will throw an error if the amount of associated objects are >=4
I am wondering how i can access the keys/values in the contained hash and run a validation on it
if i do this
animal = FactoryGirl.create(:animal, images_count: 4)
a = animal.animal_images
ap(a)
I get this returned
#<ActiveRecord::Associations::CollectionProxy [
#<AnimalImage id: 520, animal_id: 158, image: "yp2.jpg", created_at: "2014-10-15 13:45:11", updated_at: "2014-10-15 13:45:11">,
#<AnimalImage id: 521, animal_id: 158, image: "yp2.jpg", created_at: "2014-10-15 13:45:11", updated_at: "2014-10-15 13:45:11">,
#<AnimalImage id: 522, animal_id: 158, image: "yp2.jpg", created_at: "2014-10-15 13:45:11", updated_at: "2014-10-15 13:45:11">,
#<AnimalImage id: 523, animal_id: 158, image: "yp2.jpg", created_at: "2014-10-15 13:45:11", updated_at: "2014-10-15 13:45:11">
]
So i thought of using .map
animal = FactoryGirl.create(:animal, images_count: 4)
a = animal.animal_images
map = a.each.map { |i| i.image }
if map.length >= 4
ap('MORE THAN 4 IMAGES')
end
"MORE THAN 4 IMAGES"
So that iterates through the CollectionProxy. However how can i get this formatted into a correct rspec test and perform the logic in a custom validation function.
I thought my test would look like this
it 'should display an error message when too many images are uploaded' do
animal = FactoryGirl.create(:animal, images_count: 4)
animal.max_num_of_images
expect(animal.errors[:base]).to include("Max of 3 images allowed")
end
and just to get the pass for now (add error message) with no logic i have
class AnimalImage < ActiveRecord::Base
belongs_to :animal
validate :max_num_of_images, :if => "image?"
def max_num_of_images
errors.add(:base, "Max of 3 images allowed")
end
end
but it seems as if the test doesnt get past the first line
ActiveRecord::RecordInvalid:
Validation failed: Max of 3 images allowed
the above is thrown in the console
This is my factory
FactoryGirl.define do
factory :animal, class: Animal do
ignore do
images_count 0
end
after(:create) do |animal, evaluator|
create_list(:animal_image, evaluator.images_count, animal: animal)
end
end
end
FactoryGirl.define do
factory :animal_image do
image { File.open("#{Rails.root}/spec/fixtures/yp2.jpg") }
end
end
i'm probably going about this in the most backwards way possible, does anyone have any suggestions please
thanks
I am trying to write a custom function that will throw an error if the amount of associated objects are >=4
You are overcomplicating things. If you just want to count the number of records in a collection then you can simply do animal.animal_images.size. So your model will look like this:
class Animal < ActiveRecord::Base
has_many :animal_images
validate :max_num_of_images
def max_num_of_images
errors.add(:base, "Max of 3 images allowed") if self.animal_images.size >= 4
end
end

Group by part of attribute in hash

I have a model called coverage that looks like this
1.9.3p429 :005 > Coverage.new
=> #<Coverage id: nil, postcode: nil, name: nil, created_at: nil, updated_at: nil>
Here is an example record:
1.9.3p429 :006 > Coverage.find(10)
Coverage Load (7.3ms) SELECT "coverages".* FROM "coverages" WHERE "coverages"."id" = $1 LIMIT 1 [["id", 10]]
=> #<Coverage id: 10, postcode: "N10", name: "N10 - Muswell Hill", created_at: "2013-05-22 14:42:37", updated_at: "2013-05-22 14:42:37">
I've got over 300 postcodes and I want to group them by some values I have in this array
group = ['N','E','EC','LS','TS']
So I would like to do
#postcodes = Coverage.all
run it through something with the above array to get the following hash
#postcode_hash = { 'N' => [ '#Active Record for N1', '#Active Record for N2' ], 'E' => [ '#Active Record for E1', '#Active Record for E2' ] }
# note: not complete should contain all index from the above array
You can use the .group_by{} method:
#postcodes = Coverage.all
#postcodes_hash = #postcodes.group_by{ |c| c.postcode.gsub(/[0-9]/, '') }
Take a look at the group_by documentation:
http://apidock.com/rails/Enumerable/group_by
There is the explicit version of above:
#postcode_hash = {}
group = ['N','E','EC','LS','TS']
group.each{ |code| #postcode_hash[code] = [] }
#postcodes = Coverage.scoped # similar to .all but better
#postcodes.map do |coverage|
code = coverage.postcode.gsub(/[0-9]/, '') # takes off all the numbers of the postcode
#postcode_hash[code] << coverage if #postcode_hash[code]
end

Resources