1

I have a bit of code that checks if a user is in the "sakai_trained" array before proceeding. For some reason when I run this code:

CSV.foreach(activation_csv, {:headers => true}) do |row|
  if sakai_trained
    row << 'Untrained' unless sakai_trained.include?(row[1]) 
  end
  
  course_list << row
end

I get this error

C:/Ruby193/lib/ruby/1.9.1/csv.rb:478:in `==': undefined method `row' for "stuartademo":String (NoMethodError)
        from activate-courses.rb:42:in `include?'
        from activate-courses.rb:42:in `block in <main>'
        from C:/Ruby193/lib/ruby/1.9.1/csv.rb:1792:in `each'
        from C:/Ruby193/lib/ruby/1.9.1/csv.rb:1208:in `block in foreach'
        from C:/Ruby193/lib/ruby/1.9.1/csv.rb:1354:in `open'
        from C:/Ruby193/lib/ruby/1.9.1/csv.rb:1207:in `foreach'
        from activate-courses.rb:40:in `<main>'

I thought at first it was having a problem with the row[1] but it breaks the same way even with a string literal. I checked to make sure the sakai_trained array exists AND has data in it as well. I also tried rewriting it as an if statement in case the unless logic was flawed but that also returns the same error.

In case it's unclear, I want to check that the userid located in row[1] exists in the sakai_trained array before adding the row to the course_list array. If it doesn't, I want 'Untrained' added to the row first, then the row added to the array. When I removed the unless... part I was able to get a complete course_list array, but as expected, every row has "untrained". The problem appears to be with the

unless sakai_trained.include?(row[1])

part but I just can't see it.

Update:

sakai_trained = []
  CSV.foreach(training_csv, {:headers => true}) do |trained|
    sakai_trained << trained
  end

Should I #map! each item with .to_s to make them into strings then?

Update 2:

I changed

sakai_trained << trained

to

sakai_trained << trained.to_s

and it's removed the error, but the output still isn't quite right.

Update 3: ALMOST. WORKING. You guys are all incredibly awesome, and as frustrating as this is I have learned some new and interesting things.

Code:

course_list = []

if options[:verify]
  sakai_trained = []
  CSV.foreach(training_csv, {:headers => true}) do |trained|
    sakai_trained << trained.to_s
  end
end 
 
CSV.foreach(activation_csv, {:headers => true}) do |row|
  if sakai_trained && !sakai_trained.include?(row[1])  
    row << 'Untrained' 
  end
  
  course_list << row
end

Yields:

2124-5318,stuartademo,Untrained

2124-5320,bobsmith,Untrained

2124-4686,jimsmith,Untrained

2124-3560,jillsmith,Untrained

2124-3562,suesmith,Untrained

2124-5428,harrysmith,Untrained

When it should be

2124-5318,stuartademo,Untrained

2124-5320,bobsmith

2124-4686,jimsmith

2124-3560,jillsmith

2124-3562,suesmith

2124-5428,harrysmith

Community
  • 1
  • 1
lyonsinbeta
  • 919
  • 6
  • 25
  • The error does not make sense. Which is line No.478 ? – SwiftMango Apr 20 '12 at 19:19
  • The error is coming from csv.rb, part of the standard ruby library, not part of my code as indicated [below.](http://stackoverflow.com/a/10251760/1007711) And I agree, it doesn't make any sense. -_- – lyonsinbeta Apr 20 '12 at 19:22
  • 2
    show us the code which sets sakai_trained. I suspect the objects being populated into it aren't strings, but rather [CSV::Row](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/csv/rdoc/CSV/Row.html)s, which explains why the Array#include? is evaluating [CSV::Row#==](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/csv/rdoc/CSV/Row.html#method-i-3D-3D). – dbenhur Apr 20 '12 at 19:39
  • I didn't know csv rows weren't read into the array as strings. I've updated the post above with the relevant lines. – lyonsinbeta Apr 20 '12 at 19:43

2 Answers2

2

The problem is occurring inside the csv.rb file in the standard Ruby library on line 478. Here's the CSV code that is causing the problem for you:

#
# Returns +true+ if this row contains the same headers and fields in the
# same order as +other+.
#
def ==(other)
  @row == other.row
end

From the looks of your error message, the String "stuartademo" is being passed into this method and, of course, there is no String#row. It looks like other should be a row of a csv file. According to the comments on the above method, it should contain headers and fields.

I would suggest finding where this String "stuartademo" is coming from and figure out why only the String is getting passed in instead of the entire row.

EDIT:

If sakai_trained is populated from a CSV, then it is not an array but rather CSV:Row type. In this case, when you call CSV::Row#include? then the ==(other) is getting called. Hence, what you are passing into is, row[1] is a String. It should not be a String.

Instead of using include?, try using field?(data) or fields.include?.

CSV.foreach(activation_csv, {:headers => true}) do |row|
  if sakai_trained
    row << 'Untrained' unless sakai_trained.field?(row[1]) 
  end

  course_list << row
end
Charles Caldwell
  • 16,649
  • 4
  • 40
  • 47
  • It's because I'm passing row[1] to include? and row[1] is a string. Isn't that the point? To see if a string is in the array? If I pass the entire array the code runs, but the condition will never be true because the sakai_trained array does not include any entire rows from the activation_csv array. – lyonsinbeta Apr 20 '12 at 18:51
  • It doesn't make sense that the error is occurring in the `include?` method (even though that is what the error message is saying) since the `include?` method would never call something in the `csv.rb` file. Let me look a little closer at the `csv.rb` file. – Charles Caldwell Apr 20 '12 at 18:59
  • I'm looking and looking and I can't seem to find anywhere where `==(other)` is called but it is definitely what is causing the error. `Array#include?`, `CSV::Row#<<(arg)`, and `CSV::Row#field(header_or_index, minimum_index = 0)` are all getting called but none of them call `==(other)`. =\ – Charles Caldwell Apr 20 '12 at 19:45
  • 1
    Your update of `sakai_trained` being populated from a csv file makes a little more sense. That's where the `==(other)` is getting called. I've updated my answer to reflect this. – Charles Caldwell Apr 20 '12 at 20:02
  • That code runs, @PortableWorld, but the output is not as expected. I KNOW one of the users is not in sakai_training but they are all passing. – lyonsinbeta Apr 20 '12 at 20:08
  • 1
    @thekungfuman what does a row in your training_csv file look like? You're calling #to_s on it now, but if it has fields, and you're only testing for one field, you need to extract that field and push it's string representation into sakai_trained. – dbenhur Apr 20 '12 at 21:26
  • @dbenhur, you win! You made me see my mistake. I was turning the entire ROW into a string because it only had one field (rows of length 1) but by adding trained[0].to_s it works! – lyonsinbeta Apr 21 '12 at 23:31
1

I think this simplifies the logic a bit and might help you trace your problem in a different way.

CSV.foreach(activation_csv, {:headers => true}) do |row|
  if sakai_trained && !sakai_trained.include?(row[1])
    row << 'Untrained'
  end

  course_list << row
end

UPDATE:

Try breaking it down into parts and letting us know what all the outputs are of this:

CSV.foreach(activation_csv, {:headers => true}) do |row|
  puts row                   #=>  2124-5318,stuartademo
  puts row.class             #=>  CSV::Row 
  puts row[1]                #=>  stuartademo 
  puts row[1].class          #=>  String 
  puts sakai_trained         #=>  I'm assuming nil because it is of NilClass
  puts sakai_trained.class   #=>  NilClass
end

I think your sakai_trained is actually nil which explains why sakai_trained.to_s gets rid of the error. You can't call include? on anything nil but when you use to_s it probably is turning it into "" which would return false.

Josh
  • 5,631
  • 1
  • 28
  • 54
  • Tried it, and it throws the exact same error. In my experience and testing #include? takes a string, but for some reason in this csv#foreach loop it's expecting a row. I like the simplification in the logic though. – lyonsinbeta Apr 20 '12 at 19:37
  • 1
    You are asking ruby if sakai_trained(some object) includes a string. Is a particular attribute you can check against instead of the entire object? Or is sakai_trained already a string? – Josh Apr 20 '12 at 19:42
  • sakai_trained is an array that I thought was full of strings read from a different CSV (as now noted in the original updated post). – lyonsinbeta Apr 20 '12 at 19:47
  • Try running my updated coded, it would be very beneficial to see all the different parts. – Josh Apr 20 '12 at 19:55
  • 2124-5318,stuartademo CSV::Row stuartademo String NilClass 2124-5320,littletw CSV::Row bobsmith String NilClass 2124-4686,ossmerm CSV::Row jimsmith String NilClass 2124-3560,manzuetac CSV::Row tomsmith String NilClass 2124-3562,manzuetac CSV::Row jillsmith String NilClass 2124-5428,pleasantsg CSV::Row pleasantsg String NilClass Is the output from your code, but note that I updated the sakai_trained code to add trained.to_s instead of the CSV row. – lyonsinbeta Apr 20 '12 at 20:03