From d125183ac4bb2a0f783a99c03abcd0e4f5f520d5 Mon Sep 17 00:00:00 2001 From: emmett Date: Thu, 2 Feb 2012 10:25:24 -0800 Subject: [PATCH] Backslashes are now quoted and unquoted correctly --- activerecord-postgres-hstore.gemspec | 4 +-- lib/activerecord-postgres-hstore/hash.rb | 38 ++++++++++++++--------- spec/activerecord-postgres-hstore_spec.rb | 18 ++++++++++- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/activerecord-postgres-hstore.gemspec b/activerecord-postgres-hstore.gemspec index d75447b..12e1e21 100644 --- a/activerecord-postgres-hstore.gemspec +++ b/activerecord-postgres-hstore.gemspec @@ -5,10 +5,10 @@ Gem::Specification.new do |s| s.name = %q{activerecord-postgres-hstore} - s.version = "0.3.0" + s.version = "0.3.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["Juan Maiz", "Diogo Biazus"] + s.authors = ["Juan Maiz", "Diogo Biazus", "Emmett Shear"] s.date = %q{2011-12-28} s.description = %q{This gem adds support for the postgres hstore type. It is the _just right_ alternative for storing hashes instead of using seralization or dynamic tables.} s.email = %q{juanmaiz@gmail.com} diff --git a/lib/activerecord-postgres-hstore/hash.rb b/lib/activerecord-postgres-hstore/hash.rb index a3df13b..a5573d2 100644 --- a/lib/activerecord-postgres-hstore/hash.rb +++ b/lib/activerecord-postgres-hstore/hash.rb @@ -1,24 +1,34 @@ class Hash + HSTORE_ESCAPED = /[,\s=>\\]/ + + # Escapes values such that they will work in an hstore string + def hstore_escape(str) + if str.nil? + return 'NULL' + end + + str = str.to_s.dup + # backslash is an escape character for strings, and an escape character for gsub, so you need 6 backslashes to get 2 in the output. + # see http://stackoverflow.com/questions/1542214/weird-backslash-substitution-in-ruby for the gory details + str.gsub!(/\\/, '\\\\\\') + # escape backslashes before injecting more backslashes + str.gsub!(/"/, '\"') + + if str =~ HSTORE_ESCAPED or str.empty? + str = '"%s"' % str + end + + return str + end # Generates an hstore string format. This is the format used # to insert or update stuff in the database. def to_hstore return "" if empty? - map { |idx, val| - iv = [idx,val].map { |_| - e = _.to_s.gsub(/"/, '\"') - if _.nil? - 'NULL' - elsif e =~ /[,\s=>]/ || e.blank? - '"%s"' % e - else - e - end - } - - "%s=>%s" % iv - } * "," + map do |idx, val| + "%s=>%s" % [hstore_escape(idx), hstore_escape(val)] + end * "," end # If the method from_hstore is called in a Hash, it just returns self. diff --git a/spec/activerecord-postgres-hstore_spec.rb b/spec/activerecord-postgres-hstore_spec.rb index cf13697..0353f01 100644 --- a/spec/activerecord-postgres-hstore_spec.rb +++ b/spec/activerecord-postgres-hstore_spec.rb @@ -63,13 +63,29 @@ end it "should quote keys and values correctly with combinations of single and double quotes" do - { %q("a') => %q(b "a' b) }.to_hstore.should eq(%q(\"a'=>"b \"a' b")) + { %q("a') => %q(b "a' b) }.to_hstore.should eq(%q("\"a'"=>"b \"a' b")) end it "should unquote keys and values correctly with combinations of single and double quotes" do %q("\"a'"=>"b \"a' b").from_hstore.should eq({%q("a') => %q(b "a' b)}) end + it "should quote keys and values correctly with backslashes" do + { %q(\\) => %q(\\) }.to_hstore.should eq(%q("\\\\"=>"\\\\")) + end + + it "should unquote keys and values correctly with backslashes" do + %q("\\\\"=>"\\\\").from_hstore.should eq({ %q(\\) => %q(\\) }) + end + + it "should quote keys and values correctly with combinations of backslashes and quotes" do + { %q(' \\ ") => %q(" \\ ') }.to_hstore.should eq(%q("' \\\\ \""=>"\" \\\\ '")) + end + + it "should unquote keys and values correctly with combinations of backslashes and quotes" do + %q("' \\\\ \""=>"\" \\\\ '").from_hstore.should eq({ %q(' \\ ") => %q(" \\ ') }) + end + it "should convert empty hash" do {}.to_hstore.should eq("") end