Writing unicode json in Ruby - ruby

In Ruby 2.0.0, I want to write an array to json:
require 'json'
File.open('test.json', 'w') do |f2|
f2.puts ["£2M worth of wine"].to_json
end
This gives writes a file looking like this:
["£2M worth of wine"]
Obviously, not what I am looking for. Is this a bug in to_json? How can I make it work?

You might want to force each element in the array to be encoded UTF-8 before calling to_json
e.g:
["£2M worth of wine"].map { |str| str.encode("utf-8") }.to_json

Related

Ruby delete keys from hash only while printing without modifying the hash

I am a newbie to ruby. I am trying to delete keys which are passwords from the hash while printing the hash, with/without modifying the original hash
request = {"name"=>"test-2",
"dns_zone_status"=>"valid",
"certificate_status"=>"valid",
"db_password"=>"GS*8qLiU",
"af_pass"=>"rk*pp2",
"master_password"=>"sfvrere",
"db_user"=>"testuser2"}
puts "request : #{request}"
puts "request : #{request.except("db_password","af_pass","master_password")}"
The first puts print the whole hash and the 2nd one errors undefined method 'except' for #<Hash:0x00000001dbcd00> (NoMethodError)
Use Hash#reject, which is non-destructive and returns a hash.
puts "request : #{
request.reject do |k,_|
["db_password","af_pass","master_password"].include?(k)
end
}"
#request : {"name"=>"test-2", "dns_zone_status"=>"valid", "certificate_status"=>"valid", "db_user"=>"testuser2"}
As is commonly done, I've used an underscore for the block variable holding the value associated with the key k to tell the reader that it is not used in the block calculation.
Instead of having to bring all of ActiveSupport in as a dependency (which is the gem implementing Hash#except, if you don't need it in other ways you can just inline this tiny method as:
puts "request : #{request.slice(*request.keys - ["db_password","af_pass","master_password"])}"
The Hash#except method actually comes from ActiveSupport, which is loaded by default in Rails but not in standalone Ruby.
However, that's easily remedied, all you have to is add the activesupport gem and one of the following requires:
# require everything in active support, the easy/lazy option
# this is what rails does and it's probably fine to do
# unless you really, really care about how much memory you use
require 'active_support/all'
# require only the hash extensions
require 'active_support/core_ext/hash'
# require only this one specific method
require 'active_support/core_ext/hash/except'
Note that the name of the gem (activesupport) and the library you require (active_support) are indeed named differently, which can be a little tricky to remember.

Parse Ruby file for comments

I want something that can parse a ruby file to give me the file positions of comments. Ranked by desirability:
Ideally, there would be some command-line arg I could pass to "ruby" since of course "ruby" would know. But there doesn't seem to be one for this?
Does anyone know if/where in "ruby" I could hook in and use its methods to know where the comments are?
Some well-known regular expression?
Thanks!
Found: https://github.com/ruby/ruby/tree/trunk/ext/ripper
Example:
require 'ripper'
require 'pp'
class CommentRipper < Ripper::SexpBuilder
def on_comment(token)
super.tap { |result| pp result }
end
end
contents = File.read("file.rb")
pp CommentRipper.new(contents).parse
Helped me understand Ripper better: http://svenfuchs.com/2009/7/5/using-ruby-1-9-ripper

Is there a simple way to iterate through pairs in a Ruby hash in order of addition (i.e. oldest first)?

I create an empty hash chickens, and key-value pairs are then added over time.
chickens = {}
chickens.merge!("Davison"=>"plucky")
chickens.merge!("Mortimer"=>"sullen")
chickens.merge!("Chauncey"=>"forlorn")
for name,mood in chickens do puts "#{name}: #{mood}" end
produces
Mortimer: sullen
Chauncey: forlorn
Davison: plucky
but I don't desire this. How do I cycle through the chickens in the order they were added?
The short answer is: update your Ruby. Ruby 1.9 is years old and works the way you want (as does current Ruby, 2.0). If you insist on ancient, unsupported Ruby, there's an OrderedHash class available via a gem:
gem install orderedhash
Your code would then become:
require 'rubygems'
require 'orderedhash'
chickens = OrderedHash.new
# The rest stays the same
Again, I recommend instead just upgrading your Ruby installation.
P.S. Your code would be much more Ruby-like if you iterate over the Hash like this:
chickens.each do |name, mood|
puts "#{name}: #{mood}"
end
#DarshanComputing began talking about making the code more Ruby-like, but here's how I would do the sample code in its entirety:
chickens = {}
chickens["Davison"] = "plucky"
chickens["Mortimer"] = "sullen"
chickens["Chauncey"] = "forlorn"
chickens.each do |name, mood|
puts "#{name}: #{mood}"
end
Using merge is fine, but it's also verbose. You'll see a lot of Ruby programmers take the more simple path and add the key/value pair directly, rather than rely on a mutating method.
Using for to loop over something is definitely not Ruby-like. Though the language supports it, its use is eschewed by Ruby developers consistently. The reason is, for name, mood ... adds name and mood to the local variables, unlike each which confines them inside the do block. Littering the variable space with temporary variables is bad-form. For instance, after running the original code I can do:
[7] (pry) main: 0> name
"Chauncey"
[8] (pry) main: 0> mood
"forlorn"

Is there something like a null-stream in Ruby?

I could use:
File.open('/dev/null', 'w')
on Unix systems, but if there is a Ruby way to achieve this, I'd like to use it. I am just looking for an I/O stream, that immediately "trashes" all writes, kind of like a null-object.
If you want the full behavior of streams, the best is probably to use:
File.open(File::NULL, "w")
Note that File::NULL is new to Ruby 1.9.3; you can use my backports gem:
require 'backports/1.9.3/file/null' # => Won't do anything in 1.9.3+
File.open(File::NULL, "w") # => works even in Ruby 1.8.6
You could also copy the relevant code if you prefer.
There's stringIO, which I find useful when I want to introduce a dummy filestream:
require "stringio"
f = StringIO.new
f.gets # => nil
And here's some code from heckle that finds the bit bucket for both Unix and Windows, slightly modified:
# Is this platform MS Windows-like?
# Actually, I suspect the following line is not very reliable.
WINDOWS = RUBY_PLATFORM =~ /mswin/
# Path to the bit bucket.
NULL_PATH = WINDOWS ? 'NUL:' : '/dev/null'
No, I don't believe there is anything like a null stream in Ruby, at least in earlier versions. In that case, you must make one yourself. Depending on the methods that it will call, you will need to write
stub methods on the null stream class, like this:
class NullStream
def <<(o); self; end
end
The above example is by no means complete. For example, some streams may require calling the write, puts or other methods. Moreover, some methods should be implemented by returning self in their methods, like <<, others not.
Logger.new("/dev/null") does the trick
There's a gem called devnull
Ruby implementation of null file (like /dev/null on Un*x, NUL on
Windows)
It doesn't interact with the null file, but instead has dummy methods for all the methods that IO objects implement.

A little help getting into the Ruby world?

I'm learning Ruby in my spare time and I have installed a Linux Mint 10 Virtual Machine on my laptop.
I've gotten most of the basic Ruby structures down and now I'd like to actually build something with it to test it out.
For my first project, I want to parse a CSV file and save the information into an array in my code.
Simple enough right?
After some Googling, I found this library that seems to be what I need to use.
https://github.com/fauna/ccsv
My question is, how do I use this? I'm coming from the C#/.Net world where I would download a .dll (more recently just use NuGet) and it would be referenced into the project.
What do I need to do in Ruby? Can anyone walk me through it? I'm brand new to this language so try not to assume anything, I just may not know it yet. Thanks for your time.
I'm afraid that, in ruby, that isn't much of a project.
Suppose you have a file 'test.csv' with this content
letters,numbers
a,3
b,2
d,4
You can parse it like this:
require 'csv'
data = CSV.read('test.csv')
p data
#=> [["letters", "numbers"], ["a", "3"], ["b", "2"], ["d", "4"]]
Somewhat more complicated:
data = CSV.read('test.csv',{:headers=>true})
puts data['numbers'][0] #=> 3
This {:headers=>true} looks somewhat like a block, but it is a hash. CSV takes all kinds of parameters in a hash, a common pattern.
Ruby has a default csv library, and I use a function I found at http://snippets.dzone.com/posts/show/3899 to parse the csv.
require 'csv'
def parse(file)
csv_data = CSV.read file
headers = csv_data.shift.map {|i| i.to_s }
string_data = csv_data.map {|row| row.map {|cell| cell.to_s } }
string_data.map {|row| Hash[*headers.zip(row).flatten] }
end
myHash = parse('myfile')
If you are doing ruby without OOP, the function definitions must go before the code that calls it.
To answer your initial question, you would do in the terminal:
gem install ccsv
Then in your code:
require 'ccsv'

Resources