Ruby json parse error: unexpected token - ruby

I have a working method that opens and parses a json file. Now I'm trying to iterate through a directory of json files and display their contents.
Working method for a single file:
def aperson
File.open("people/Elvis Presley.json") do |f|
parse = JSON.parse(f.read)
end
end
Non-working method to iterate through a directory:
16. def list
17. Dir.glob('people/*').each do |f|
18. parse = JSON.parse(f)
19 end
20. end
My error is:
/Users/ad/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1/json/common.rb:148:in `parse': 743: unexpected token at 'people/Elvis Presley.json' (JSON::ParserError)
from /Users/ad/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1/json/common.rb:148:in `parse'
from app.rb:18:in `block in list'
from app.rb:17:in `each'
from app.rb:17:in `list'
from app.rb:24:in `<main>'
All of the files in the directory have the same content and are valis as per JSONlint.
Any help would be greatly appreciated.

You tried to parse the filename as JSON, which won't work.
Instead, you need to read the file first:
parse = JSON.parse(File.read(f))

not sure, but can you try to parse the content of file instead of file name:
parse = JSON.parse( File.read f )

In your non-working code, f is just string of the expanded file name. So you need to read the file after you've received the filename in the block.
While writing it, #nneonneo already gave you solution. So I'm not giving again.

Related

Ruby CSV - Retrieve original, raw line with each row parsed?

TL;DR: How to get the raw input line (not line number) while parsing a csv file?
I'm parsing a delimited file with Ruby's CSV class. I'd like to retrieve the raw line from the file for each row, in addition to the parsed fields from that row.
Here is what I have now:
CSV.foreach(input_file, csv_params) do |row|
add_uploaded_user(row)
end
That works perfectly. Every file is parsed correctly, and add_uploaded_user does what it is supposed to.
We are getting some unusual files from one client, with unexpected user names in the data. The file is valid csv and parses correctly. They claim we are messing up their records, so we want to capture each raw line from the file before it is parsed. We already save the whole CSV file, but it is inconvenient to manually pull the file and find the source record when we get a complaint. We'd like to give them a tool so they can verify exactly what they sent us. Also, we cannot reveal other records from that file the user in question, so we cannot share the entire file.
So, we'd like to capture the raw line of input with each parsed record we create from their file. Something like this:
CSV.foreach(input_file, csv_params) do |row|
add_uploaded_user(row, row.raw_line)
end
...where raw_line is some method/attribute/helper from CSV that reveals the line that was just parsed.
I've gone through the CSV docs, and found https://ruby-doc.org/stdlib-2.6.1/libdoc/csv/rdoc/CSV.html#method-i-line :
line() - The last row read from this file.
But I can't figure out how to call line(). I've tried several invocations, and they all turn out pretty much the same, with NoMethodError: undefined method 'line' for CSV:Class :
irb(main):022:0> CSV.line
NoMethodError: undefined method 'line' for CSV:Class
irb(main):049:0* csv = CSV.new("a,b,c\n1,2,3\n")
=> <#CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
irb(main):050:0> csv.each do |row|
irb(main):051:1* puts row
irb(main):052:1> puts csv.line
irb(main):053:1> end
a
b
c
NoMethodError: undefined method 'line' for #<CSV:0x00007feeb25de3c0>
from (irb):52:in 'block in irb_binding'
from (irb):50
irb(main):054:0>
And a simpler example, reading an actual file:
irb(main):055:0> csv = CSV.new(File.open('3_licenses.csv'))
=> <#CSV io_type:File io_path:"3_licenses.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\r\n" quote_char:"\"">
irb(main):062:0> csv.shift
=> ["first_name", "last_name", "license_number"]
irb(main):063:0> csv.shift
=> ["David ", "Hempy", "1001"]
irb(main):064:0> csv.line
NoMethodError: undefined method 'line' for #<CSV:0x00007feeb2591020>
from (irb):64
irb(main):065:0> csv.shift
=> ["Santa", "Claus", "np.1"]
UPDATE:
The docs I was reading was for 2.6. I'm running ruby 2.4.5, but it looks like it was there then, as well: https://ruby-doc.com/stdlib-2.4.5/libdoc/csv/rdoc/CSV.html#method-i-line . Interestingly, .line is not mentioned in https://docs.ruby-lang.org/en/2.4.0/CSV.html Hmm....
Also, I don't need the line number -- I need the raw line from the input file.
At this point, I'm about ready to just read the lines myself, then call CSV separately for each line. That will certainly work and put me in control...but I'm still confused why I can't call the .line() method described in the docs. If anyone can see why I'm getting "undefined method 'line'", I'd surely appreciate it.
When the documentation refers to CSV#line they mean you have to call it on an instance of CSV:
require 'csv'
csv = CSV.new(File.open('example.csv'))
csv.each do |row|
p csv.line
end

Ruby, writing to a YAML file, with arrays

I'm trying to save a few variables in a YAML config file.
Cool!!
However, when I try and save them, I get an error in RUBY:
undefined method `[]=' for false:FalseClass (NoMethodError)
My function should (In my head at least) be:
Does the config file exist, if not, just create a blank one.
Now that we know it exists, YAML.open it
set the new/overwriting key/value pairs
re Write the file
But, I'm getting the error above.
I'm new to Ruby (PHP bloke here), tell me where I'm being stupid please :)
def write_to_file( path_to_file, key, value, overwrite = true )
if !File.exist?(path_to_file)
File.open(path_to_file, 'a+')
end
config_file = YAML.load_file( path_to_file)
config_file[key] = value
File.open(path_to_file, 'w') { |f| YAML.dump(config_file, f) }
# I tried this commented code below too, same error..
# {|f| f.write config_file.to_yaml }
end
The problem is that you created an empty file. And the YAML parser returns false for an empty string:
YAML.load('') #=> false
Just set config_file to an empty hash when the YAML loader returned false:
config_file = YAML.load_file(path_to_file) || {}

Read compressed csv file on-the-fly

I have wrote some csv file and compress it, using this code:
arr = (0...2**16).to_a
File.open('file.bz2', 'wb') do |f|
writer = Bzip2::Writer.new f
CSV(writer) do |csv|
(2**16).times { csv << arr }
end
writer.close
end
I want to read this csv bzip2ed file (csv files compressed with bzip2). These files uncompressed look like:
1,2
4,12
5,2
8,7
1,3
...
So I tried this code:
Bzip2::Reader.open(filename) do |bzip2|
CSV.foreach(bzip2) do |row|
puts row.inspect
end
end
but when it is executed, it throws:
/Users/foo/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/csv.rb:1256:in `initialize': no implicit conversion of Bzip2::Reader into String (TypeError)
from /Users/foo/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/csv.rb:1256:in `open'
from /Users/foo/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/csv.rb:1256:in `open'
from /Users/foo/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/csv.rb:1121:in `foreach'
from worm_pathfinder_solver.rb:79:in `block in <main>'
from worm_pathfinder_solver.rb:77:in `open'
from worm_pathfinder_solver.rb:77:in `<main>'
Question:
What is wrong?
How should I do?
CSV.foreach assumes you're passing a file path to open. If you want to pass a stream to CSV you need to be more explicit and use CSV.new. This code will process a gzipped file:
Zlib::GzipReader.open(filename) do |gzip|
csv = CSV.new(gzip)
csv.each do |row|
puts row.inspect
end
end
Based on the brief docs you'll probably need send the read method on bzip2 object (not tested):
Bzip2::Reader.open(filename) do |bzip2|
CSV.foreach(bzip2.read) do |row|
# ^^^^
puts row.inspect
end
end
My guess would be that CSV tries to convert the Bzip2::Reader to a string but doesn't know how and simply throws the exception. You can manually read the data into a string and then pass THAT to CSV.
Though it's strange since it could handle Bzip2::Writer just fine.

How can I use string interpolation to pass a url into Nokogiri?

So I have a method and when I pass in a straight URL (see code below) the method returns just fine. When I pass in the URL using #{} for the possibility of using different location in Craigslist, it throws the error shown at the bottom. I suppose my question is twofold:
Why doesn't Nokogiri allow me to open this?
Can I change this to accept the URL?
Code:
def get_post_date(listing_url)
# This method takes in a page and returns a date hopefully in a date format
# but right now text
listing = Nokogiri::HTML(open(listing_url)).css("p")
setter = ""
for element in listing
if element.css('time').text!=""&&setter==""
post_time = "poop" # Time.parse(element.css('time').text)
return "poop"
end
end
end
location = "sfbay"
# THIS throws an error
p get_post_date("#{location}.craigslist.org/sfc/vac/4248712420.html")
# THIS works
p get_post_date("sfbay.craigslist.org/sfc/vac/4248712420.html")
Error:
c:\>ruby cljobs.rb C:/Ruby193/lib/ruby/1.9.1/open-uri.rb:35:in
`initialize': No such file or direct ory -
sfbay.craigslist.org/sfc/vac/4248712420.html (Errno::ENOENT)
from C:/Ruby193/lib/ruby/1.9.1/open-uri.rb:35:in `open'
from C:/Ruby193/lib/ruby/1.9.1/open-uri.rb:35:in `open'
from cljobs.rb:7:in `get_post_date'
from cljobs.rb:40:in `'
In order to open a URL you need to require OpenURI. Otherwise nokogiri will try to open a file.
require 'open-uri'
listing = Nokogiri::HTML(open(listing_url))

Why can't my program find the text file I want to open?

I am writing this code for a CS assignment in Ruby. I am just beginning in Ruby so I don't know much about it but I keep getting a no method error from this code like this:
V:\CS 300 RubyAssignment\lib\rubyAssignment.rb:13:in `categorize': undefined method `line' for #<File:ruby1.txt (closed)> (NoMethodError)
from V:\CS 300 RubyAssignment\lib\rubyAssignment.rb:11:in `open'
from V:\CS 300 RubyAssignment\lib\rubyAssignment.rb:11:in `categorize'
from V:\CS 300 RubyAssignment\lib\rubyAssignment.rb:30
The code is below but I get a feeling my text files are in the wrong place. I use a NetBeans Ruby plugin and I don't know if my text files should be in the projects source file folder, test file folder or libraries folder in netbeans? It might be as simple as that any ideas?
# This program reads a file line by line,
# separating lines by writing into certain text files.
# PPQ - Pangrams, Palindromes, and Quotes
class PPQ
def categorize
file_pangram = File.new('pangram.txt', 'w')
file_palindrome = File.new('palindrome.txt', 'w')
file_quotes = File.new('quotes.txt','w')
File.open('ruby1.txt','r') do |file|
while line = file.gets
if(file.line.reverse == file.line)
file_palindrome.write line
if(file.line.contains('a'&&'b'&&'c'&&'d'&&'e'&&'f'&&'g'&&'h'&&'i'&&'j'&&'k'&&'l'&&'m'&&'n'&&'o'&&'p'&&'q'&&'r'&&'s'&&'t'&&'u'&&'v'&&'w'&&'x'&&'y'&&'z'))
file_pangram.write "file.line"
else
file_quotes.write "file.line"
end
end
end
file.close
file_pangram.close
file_palindrome.close
file_quotes.close
end
end
end
my_ruby_assignment = PPQ.new
my_ruby_assignment.categorize
The clue's in the error message!
undefined method line
In your while loop you're reading a line from file via gets, then storing it in line. This code:
if(file.line.reverse == file.line)
is therefore incorrect -- you don't need the file. prefix! Ruby thinks you're trying to call the line method of the file object, which it doesn't have. That's why it's giving you the error it did…

Resources