I discovered this behavior of multi_json ruby gem:
2.1.0 :001 > require 'multi_json'
=> true
2.1.0 :002 > sym = :symbol
=> :symbol
2.1.0 :003 > sym.class
=> Symbol
2.1.0 :004 > res = MultiJson.load MultiJson.dump(sym)
=> "symbol"
2.1.0 :005 > res.class
=> String
Is this an appropriate way to store ruby symbols? Does JSON provide some way to distinguish :symbol from "string"?
Nope is the simple answer. Most of the time it only really matters for hashes and there's a cheat on hashes, symbolize_keys!. Bottom line is that JSON does not understand symbols, just strings.
Since you are using MultiJson, you can also ask MultiJson to do this for you...
MultiJson.load('{"abc":"def"}', :symbolize_keys => true)
While upgrading a large project from 1.8.7 to 1.9.2, I have uncovered a bug that I am at a loss to explain. It appears the fork operation breaks a memcached connection, even when the memcached connection isn't used in the forked code. Here is an IRB session, in 1.9.2 showing the error, and in 1.8.7 showing no error. Can anyone explain why there is no ServerIsMarkedDead error in 1.8.7 but there is in 1.9.2?
MBPR:$ rvm list
rvm rubies
ruby-1.8.7-p370 [ i686 ]
=* ruby-1.9.2-p320 [ x86_64 ]
# => - current
# =* - current && default
# * - default
MBPR:$ irb
irb(main):001:0> require 'rubygems'
=> false
irb(main):002:0> require 'memcached'
=> true
irb(main):003:0> mc = Memcached.new("localhost:11211", :timeout => 1.0)
=> #<Memcached:0x007fc91082abc8 #struct=#<Rlibmemcached::MemcachedSt:0x007fc91082a948>, #options={:hash=>:fnv1_32, :no_block=>false, :noreply=>false, :distribution=>:consistent_ketama, :ketama_weighted=>true, :buffer_requests=>false, :cache_lookups=>true, :support_cas=>false, :tcp_nodelay=>false, :show_backtraces=>false, :retry_timeout=>30, :timeout=>1.0, :rcv_timeout=>1.0, :poll_timeout=>1.0, :connect_timeout=>4, :prefix_key=>"", :prefix_delimiter=>"", :hash_with_prefix_key=>true, :default_ttl=>604800, :default_weight=>8, :sort_hosts=>false, :auto_eject_hosts=>true, :server_failure_limit=>2, :verify_key=>true, :use_udp=>false, :binary_protocol=>false, :credentials=>nil, :experimental_features=>false, :exception_retry_limit=>5, :exceptions_to_retry=>[Memcached::ServerIsMarkedDead, Memcached::ATimeoutOccurred, Memcached::ConnectionBindFailure, Memcached::ConnectionFailure, Memcached::ConnectionSocketCreateFailure, Memcached::Failure, Memcached::MemoryAllocationFailure, Memcached::ReadFailure, Memcached::ServerEnd, Memcached::ServerError, Memcached::SystemError, Memcached::UnknownReadFailure, Memcached::WriteFailure, Memcached::SomeErrorsWereReported]}, #default_ttl=604800, #not_found=#<Memcached::NotFound: Memcached::NotFound>, #not_stored=#<Memcached::NotStored: Memcached::NotStored>>
irb(main):004:0> mc.set "foo", 1, 100
=> nil
irb(main):005:0> mc.get "foo"
=> 1
irb(main):006:0> pid1 = fork do
irb(main):007:1* end
=> 9682
irb(main):008:0> sleep 1
=> 1
irb(main):009:0> mc.get "foo"
Memcached::ServerIsMarkedDead: Key {"foo"=>"localhost:11211:8"}
from /Users/me/.rvm/gems/ruby-1.9.2-p320#global/gems/memcached-1.5.0/lib/memcached/memcached.rb:630:in `reraise'
from /Users/me/.rvm/gems/ruby-1.9.2-p320#global/gems/memcached-1.5.0/lib/memcached/memcached.rb:608:in `check_return_code'
from /Users/me/.rvm/gems/ruby-1.9.2-p320#global/gems/memcached-1.5.0/lib/memcached/memcached.rb:517:in `get'
from (irb):9
from /Users/me/.rvm/rubies/ruby-1.9.2-p320/bin/irb:12:in `<main>'
irb(main):010:0>
And in 1.8.7
MBPR:$ rvm use 1.8.7
Using /Users/me/.rvm/gems/ruby-1.8.7-p370
MBPR:$ irb
1.8.7 :001 > require 'rubygems'
=> true
1.8.7 :002 > require 'memcached'
=> true
1.8.7 :003 > mc = Memcached.new("localhost:11211", :timeout => 1.0)
=> #<Memcached:0x10fc65dd0 #not_stored=#<Memcached::NotStored: Memcached::NotStored>, #servers=["localhost:11211:8"], #not_found=#<Memcached::NotFound: Memcached::NotFound>, #default_ttl=604800, #struct=#<Rlibmemcached::MemcachedSt:0x10fc65808>, #options={:cache_lookups=>true, :retry_timeout=>30, :rcv_timeout=>1.0, :exception_retry_limit=>5, :show_backtraces=>false, :binary_protocol=>false, :hash_with_prefix_key=>true, :server_failure_limit=>2, :exceptions_to_retry=>[Memcached::ServerIsMarkedDead, Memcached::ATimeoutOccurred, Memcached::ConnectionBindFailure, Memcached::ConnectionFailure, Memcached::ConnectionSocketCreateFailure, Memcached::Failure, Memcached::MemoryAllocationFailure, Memcached::ReadFailure, Memcached::ServerError, Memcached::SystemError, Memcached::UnknownReadFailure, Memcached::WriteFailure], :no_block=>false, :distribution=>:consistent_ketama, :timeout=>1.0, :ketama_weighted=>true, :prefix_delimiter=>"", :auto_eject_hosts=>true, :support_cas=>false, :hash=>:fnv1_32, :buffer_requests=>false, :poll_timeout=>1.0, :default_ttl=>604800, :sort_hosts=>false, :verify_key=>true, :tcp_nodelay=>false, :connect_timeout=>4, :default_weight=>8, :credentials=>nil, :use_udp=>false}>
1.8.7 :004 > mc.set "foo", 1, 100
=> nil
1.8.7 :005 > mc.get "foo"
=> 1
1.8.7 :006 > pid1 = fork do
1.8.7 :007 > end
=> 9799
1.8.7 :008 > sleep 1
=> 1
1.8.7 :009 > mc.get "foo"
=> 1
1.8.7 :010 >
This is fundamental to the UNIX process model with respect to fork. Both programs are absolutely identical clones, and the only way to differentiate one from the other is the result of the fork call.
The clone will terminate any connections as the master would when the process completes. That this doesn't happen in one version of Ruby is probably a bug.
This is why forking before opening connections is probably a good idea, and forking after opening connections requires special handling.
Okay, I want to draw charts within Ruby, so I've found gem called Googlecharts. Here's the problem:
-> % irb
1.9.2-p320 :001 > require 'gchart'
=> true
1.9.2-p320 :002 > Gchart.line(:size => '200x200', :title => 'title', :bg => 'ffffff', :data => [10,20,30,50])
=> "http://chart.apis.google.com/chart?"
As you can see, there's no link. What's wrong?
IRB and Rails console both have a nice way of outputting symbols that only quote-escapes them when necessary. Some examples:
1.9.3p194 :001 > "#test".to_sym
=> :#test
1.9.3p194 :002 > "#Test".to_sym
=> :#Test
1.9.3p194 :003 > "#123".to_sym
=> :"#123"
1.9.3p194 :004 > "##_test".to_sym
=> :##_test
1.9.3p194 :005 > "test?".to_sym
=> :test?
1.9.3p194 :006 > "test!".to_sym
=> :test!
1.9.3p194 :007 > "_test!".to_sym
=> :_test!
1.9.3p194 :008 > "_test?".to_sym
=> :_test?
1.9.3p194 :009 > "A!".to_sym
=> :"A!"
1.9.3p194 :010 > "#a!".to_sym
=> :"#a!"
How would you do this yourself, so that you could do:
puts "This is valid code: #{escape_symbol(some_symbol)}"
The easiest and best way to do this is via Symbol's inspect method:
1.9.3p194 :013 > puts "This is valid code: #{"#a!".to_sym.inspect}"
This is valid code: :"#a!"
=> nil
1.9.3p194 :014 > puts "This is valid code: #{"a!".to_sym.inspect}"
This is valid code: :a!
You could look at the sym_inspect(VALUE sym) method in string.c in Ruby 1.9.3 that does that, if you're curious.
So, even though you don't need another method to call inspect, this would be the simplest implementation:
def escape_symbol(sym)
sym.inspect
end
Here's my attempt at implementing with a few regexs, although I'd suggest using inspect instead if you can:
def escape_symbol(sym)
sym =~ /^[#a-zA-Z_]#?[a-zA-Z_0-9]*$/ || sym =~ /^[a-z_][a-zA-Z_0-9]*[?!]?$/ ? ":#{sym}" : ":\"#{sym.gsub(/"/, '\\"')}\""
end
ruby 1.9 now uses fastercsv, but how do i replicate the generate_line behaviour of ruby 1.8.7 ?
ruby-1.8.7-p334 :010 > require 'csv'
=> true
ruby-1.8.7-p334 :010 > CSV.generate_line(["ab","cd"], "\t")
=> "ab\tcd"
ruby-1.9.2-p180 :002 > require 'csv'
=> true
ruby-1.9.2-p180 :007 > CSV.generate_line(["ab","cd"], :row_sep => ?\t)
=> "ab,cd\t"
Notice how \t is between the two array items in ruby 1.8.7 and at last in 1.9.2
You have to use col_sep instead. row_sep is the row separator:
CSV.generate_line(["ab","cd"], :col_sep => ?\t)
=> "ab\tcd\n"
or
CSV.generate_line(["ab","cd"], :col_sep => ?\t, :row_sep => '')
=> "ab\tcd"
You can find more details and additional options in the documentation.
CSV.generate_line(['a','b','c'],:col_sep=>"\t")