I would like to decode a json string with a \u0022 character in it.
I am successful with:
>> ActiveSupport::JSON.decode("{\"json\":{\"difficulty\":1}}")
=> {"json"=>{"difficulty"=>1}}
But fail with:
>> ActiveSupport::JSON.decode("{\"json\":{\"difficulty\":\"test\\u0022test\"}}")
StandardError: Invalid JSON string
from /home/.../.rbenv/versions/1.8.7-p358/lib/ruby/gems/1.8/gems/activesupport-2.3.15/lib/active_support/json/backends/yaml.rb:14:in `decode'
from /home/.../.rbenv/versions/1.8.7-p358/lib/ruby/gems/1.8/gems/activesupport-2.3.15/lib/active_support/json/decoding.rb:14:in `__send__'
from /home/.../.rbenv/versions/1.8.7-p358/lib/ruby/gems/1.8/gems/activesupport-2.3.15/lib/active_support/json/decoding.rb:14:in `decode'
from (irb):11
I would love to replace the \u0022 character with another one, but I can't do it because it's inside the parameters parsing process of rails when the app receives a request; except if I override the json decode chore method, which I would prefer to avoid.
FYI : I'm on Ruby 1.8.7-p358 & Rails 2.3.15 & I can't change that.
I think this has to do with Syck, the old YAML library used in (very) old versions of Ruby. What does this have to do with YAML? In ActiveSupport 2.3.15, JSON decoding uses YAML.load to parse JSON, because JSON happens to be a subset of YAML.
A quick aside: In ActiveSupport 2.3.18, ActiveSupport::JSON::Backends::Yaml no longer uses the YAML backend decode YAML, and ActiveSupport::JSON::Backends::Yaml#decode looks like this:
def decode(json)
raise "The Yaml backend has been deprecated due to security risks, you should set ActiveSupport::JSON.backend = 'OkJson'"
end
There are very serious consequences to using outdated versions of Ruby and Rails. Upgrade to versions that are still supported by security releases, or you will be sorry. Seriously.
Anyway, let's take a closer look at your data. As I suspect you know, the UTF-8 character U+0022 is a double-quote. All of the escaping makes things confusing, so let's see what string we actually have:
str = "{\"json\":{\"difficulty\":\"test\\u0022test\"}}"
puts str
# => {"json":{"difficulty":"test\u0022test"}}
FWIW it's more readable to use one of Ruby's alternate string syntaxes here, e.g.:
str = %Q[{"json":{"difficulty":"test\\u0022test"}}]
puts str
# => {"json":{"difficulty":"test\u0022test"}}
Either way, that's a literal \ followed by u0022. So far so good. Now let's try to parse it with Syck (I'm using Ruby 2.2, but it doesn't make a difference in this case):
require "syck"
str = %Q[{"json":{"difficulty":"test\\u0022test"}}]
Syck.load(str)
# => .../syck.rb:145:in `load': syntax error on line 0, col 38: `# Transforms the subclass name found i}}' (ArgumentError)
I don't know why Syck's error messages are so weird, but anyway we can see that Syck falls over trying to parse this JSON. Now let's try it with a modern YAML parser:
require "psych"
str = %Q[{"json":{"difficulty":"test\\u0022test"}}]
Psych.load(str)
# => {"json":{"difficulty":"test\u0022test"}}
Works great!
So you have a few options.
You could upgrade to a version of Ruby that's still maintained, doesn't have security holes, and has a YAML parser that isn't broken.
You could upgrade to a version of Rails that's still maintained, doesn't have security holes, and doesn't use a broken YAML parser to parse JSON.
You could install Psych, which is available as a gem for older versions of Ruby.
Related
In the RubyMine debugger, just type this into the watches:
';'
or
";"
and I am getting the error:
"unterminated string meets end of file"
Why is this? It doesn't happen in the Rails console, and it doesn't have anything to do with RubyMine, as far as an I can tell.
This is the result of the Ruby debugger having different parsing rules from the Ruby interpreter. In fact, the regular Ruby debugger, invoked from irb or the ruby command exhibits this same behaviour. The workaround, however, is straightforward: to create a string literal consisting of a single semicolon, just escape it with a backslash:
$ irb
> require 'debugger'
=> true
> debugger
(rdb:1) ';'
*** SyntaxError Exception: /usr/local/rvm/rubies/ruby-1.9.3-p286/lib/ruby/1.9.1/irb/context.rb:166: unterminated string meets end of file
(rdb:1) '\;'
";"
It's important to note that the Ruby debugger command-line parser is not the same as the parser used by the irb or ruby interpreter: it is designed around parsing debugger commands such as backtrace, break etc. and not for parsing the Ruby language (with shell-like extensions in the case of irb). It has limited support for evaluating Ruby (or Ruby-style) expressions. This is, of course, crucial to effective debugging of Ruby programs. However, you should not expect it to be able to parse everything that irb or the ruby command itself would be able to parse or to parse things in exactly the same way. In some cases, like this, it can handle certain expressions but they need to be escaped subject to the parsing rules of the debugger as opposed to the Ruby language itself.
The Rails console is built on top of irb and is, thus, a Ruby shell and respects the parsing rules of the Ruby language just like irb and ruby.
I had the same problem with the Ruby CSV library parsing a CSV with a Semicolon as delimiter. I added the col_sep ';' but it always resulted in the following error: unterminated string meets end of file
Correct way of doing it:
CSV.open(file.path, col_sep: '\;', &:readline)
Added this as documentation for anybody else.
When i am trying to upgrade Rails to 2.3.16 from 2.3.15, the ActiveSupport::JSON.decode(response.body) fails to handle NaN.
Am getting an error like: invalid character at "NaN,...
Does anyone know how to fix it?
ActiveSupport::JSON.decode uses MultiJson under the hood which tends to ignore any passed in options (At least in Rails 3, not sure about 2.3). This means you can't pass in the usually accepted option of allow_nan.
However, if you are using Ruby 1.9+ you can use the built-in JSON parser:
require 'json'
json_result = JSON.parse(response.body, allow_nan: true)
I'm trying to create a Rails app template I have this block of code in there
file 'config/sass.rb', <<-RUBY
Sass::Engine::DEFAULT_OPTIONS[:load_paths].tap do |load_paths|
load_paths << "#{Rails.root}/app/assets/stylesheets"
load_paths << "#{Gem.loaded_specs['compass'].full_gem_path}/frameworks/compass/stylesheets"
end
RUBY
When I run 'rails new' with this template I get the following error:
undefined method `root' for Rails:Module (NoMethodError)
I'm new to app templates as well as this code block syntax. (What do you even call that <<-RUBY block? It's really hard to search for on google). It was my impression that it wouldn't be running any of the code inside the block so it shouldn't be causing errors. What gives?
UPDATE: Let me add some more context:
I'm trying to modify the app template here: https://github.com/leshill/rails3-app/blob/master/app.rb I want add the code from this blog post: http://metaskills.net/2011/05/18/use-compass-sass-framework-files-with-the-rails-3.1-asset-pipeline/ so that I can have compass support in rails3.1
To elaborate on mu's point.
The <<-SOMESTIRING syntax defines the beginning of a string. The string is terminated with SOMESTRING (at the start of the line)
For example you see this a lot
string = <<-EOF
Hey this is a really long string
with lots of new lines
EOF
string # => " Hey this is a really long string\n\n with lots of new lines\n"
In this case the RUBY is to signify that this is ruby code (that will be evaluated). You have to remember that when inside a string the #{ruby_code} escape syntax will evaluate the ruby_code given and insert the result into the string.
So to get around this you can do something like,
irb >> s = <<-RUBY
"#{'#{Rails.root}'}/app/assets/stylesheets"
RUBY
#=> ""\#{Rails.root}/app/assets/stylesheets"\n"
Here we break out of the string using #{} and then use the single quotes to tell ruby that we don't want the #{Rails.root} evaluated.
EDIT: I was thinking more about this, and realized this is equivalent and a little cleaner
irb >> s= <<-RUBY
Rails.root.to_s + "/app/assets/stylesheets"
RUBY #=> "Rails.root.to_s + "/app/assets/stylesheets"\n"
This way we don't have to worry about escaping at all : )
You are asking the "rails new" command to create a file and passing a block of content using a "heredoc" (signaled by the <<-SOMESTRING syntax). More about heredoc:
http://en.wikipedia.org/wiki/Here_document#Ruby
The parser will treat the content just like a Ruby string surrounded by doublequotes and attempt to substitute any string enclosed by #{}. It fails because it can't find a variable named Rails.root.
You can avoid the substitution behavior (have the content treated like a Ruby string surrounded by singlequotes) by using single-quote-style-heredoc. Surround the heredoc signal with singlequotes:
file 'config/sass.rb', <<-'RUBY'
Sass::Engine::DEFAULT_OPTIONS[:load_paths].tap do |load_paths|
load_paths << "#{Rails.root}/app/assets/stylesheets"
load_paths << "#{Gem.loaded_specs['compass'].full_gem_path}/frameworks/compass/stylesheets"
end
RUBY
Since you're creating Rails app template for a starter app, it might be helpful to look at the
Rails 3.1 Application Templates
from the Rails Apps project on GitHub.
The project provides good examples of app templates plus documentation (be sure to take a look at Thor::Actions and Rails::Generators::Actions).
I'm trying to roundtrip encode/decode plain strings in json, but I'm getting an error.
In rails 2.3. w/ ruby 1.8.6, it used to work.
>> puts ActiveSupport::JSON.decode("abc".to_json)
abc
=> nil
In rails 3.1beta1 w/ ruby 1.9.2, it raises an error.
ruby-1.9.2-p180 :001 > puts ActiveSupport::JSON.decode("abc".to_json)
MultiJson::DecodeError: 706: unexpected token at '"abc"'
from /home/stevenh/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/json/common.rb:147:in `parse'
from /home/stevenh/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/json/common.rb:147:in `parse'
from /home/stevenh/.rvm/gems/ruby-1.9.2-p180/gems/multi_json-1.0.1/lib/multi_json/engines/json_gem.rb:13:in `decode'
[...]
This is pretty much the same question discussed at nil.to_json cannot be parsed back to nil?
But nil used to work in 2.3/1.8.7 as well.
puts ActiveSupport::JSON.decode(nil.to_json)
nil
Is this the new normal?
This change occurred with the switch from ActiveSupport's JSON backend to MultiJson that was included in Rails 3.1.0.rc1. Per the MultiJson project team, the current behavior is correct and the previous implementation was faulty due to RFC4627's specification of the JSON grammar:
2. JSON Grammar
A JSON text is a sequence of tokens. The set of tokens includes six
structural characters, strings, numbers, and three literal names.
A JSON text is a serialized object or array.
JSON-text = object / array
As neither "abc" nor "/"abc/"" are serialized objects or arrays, an error when attempting to decode them is appropriate.
The diagrams from the JSON website reinforce this specification.
That being said, this would seem to imply a bug in the to_json implementation that results in:
ruby-1.9.2-p180 :001 > "abc".to_json
=> "\"abc\""
Yes, what is happening in Rails3 is the new normal. The changes you illustrate seem like a reflection of a maturing framework.
Methods named "encode" & "decode" should be expected to be perfectly compliant with the JSON spec, and inverses of one another.
String#to_json, on the other hand is a behavior-ish type of method that functions as a convenience for building more complex JSON objects presumably used internally (within ActiveSupport) when Array#to_json or Hash#to_json encounter a String value as one of their consituent elements.
If you need to restore that behavior follow these steps, i.e.
# in your Gemfile
gem 'yajl-ruby'
# in your application.rb
require 'yajl/json_gem'
After those steps:
Loading development environment (Rails 3.2.8)
[1] pry(main)> puts ActiveSupport::JSON.decode("abc".to_json)
abc
=> nil
[2] pry(main)> puts ActiveSupport::JSON.decode(nil.to_json)
=> nil
I'm trying to port over some of my old rails apps to Ruby 1.9 and I keep getting warnings about how "Ruby 1.9 doesn't support Unicode normalization yet." I've tracked it down to this function, but I'm getting about 20 warning messages per request:
rails-2.3.5/activesupport/lib/active_support/inflector.rb
def transliterate(string)
warn "Ruby 1.9 doesn't support Unicode normalization yet"
string.dup
end
Any ideas how I should start tracking these down and resolving it?
If you are aware of the consequences, i.e. accented characters will not be transliterated in Ruby 1.9.1 + Rails 2.3.x, place this in config/initializers to silence the warning:
# http://stackoverflow.com/questions/2135247/ruby-1-9-doesnt-support-unicode-normalization-yet
module ActiveSupport
module Inflector
# Calling String#parameterize prints a warning under Ruby 1.9,
# even if the data in the string doesn't need transliterating.
if Rails.version =~ /^2\.3/
undef_method :transliterate
def transliterate(string)
string.dup
end
end
end
end
Rails 3 does indeed solve this issue, so a more future-proof solution would be to migrate towards that.
The StringEx Gem seems to work pretty well. It has no dependency on Iconv either.
It adds some methods to the String class, like "to_ascii" which does beautiful transliteration out of the box:
require 'stringex'
"äöüÄÖÜßë".to_ascii #=> "aouAOUsse"
Also, the Babosa Gem does a great job transliterating UTF-8 strings, even with language support:
"Jürgen Müller".to_slug.transliterate.to_s #=> "Jurgen Muller"
"Jürgen Müller".to_slug.transliterate(:german).to_s #=> "Juergen Mueller"
Enjoy.
That method definition is wrapped in an if-statement for Ruby 1.9. Right above it, you will find the regular definition, which shows a bit more of what this is doing. It's a method used to convert accented characters into their regular variants. E.g.: á => a, or ë => e
But this method is only used in parameterize, which is in turn defined right above transliterate. This is all still in ActiveSupport. I can't find anything that is directly calling parameterize.
So perhaps you're using parameterize or transliterate yourself, somewhere in your Rails application?
Common usage (according to the parameterize documentation) is for creating friendly permalinks from arbitrary strings, much like SO does, for example:
http://stackoverflow.com/questions/2135247/ruby-1-9-doesnt-support-unicode-normalization-yet
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Replace the body of the method with
raise "transliterate called"
and observe a backtrace which will show you where the stuff is coming from at the first call. Your app will of course collapse as well but that will likely give you the culprit from the first try.
I appreciate that this is a dirty way to solve the problem, but having read the error message I'm aware of the issue. So I want to get rid of the warnings. I dropped this code in environment.rb:
module ActiveSupport
module Inflector
# Calling String#parameterize prints a warning under Ruby 1.9,
# even if the data in the string doesn't need transliterating.
# Maybe Rails 3 will have fixed it...?
if RAILS_GEM_VERSION =~ /^2\.3/
undef_method :transliterate
def transliterate(string)
string.dup
end
end
end
end
If you'd rather not monkey patch the Inflector module, you can also do this...
Both of the following worked for me to silence this annoying "Ruby 1.9 doesn't support Unicode normalization yet" warning:
silence_stream(STDERR) {
whatever_code_caused_transliterate_to_be_called
}
or
silence_warnings {
whatever_code_caused_transliterate_to_be_called
}
This does have the disadvantage that it requires cluttering up your calling code, but it is a technique you can use generally whenever you don't want to see warnings or other output.
activesupport provides silence_stream and silence_warnings in activesupport-2.3.11/lib/active_support/core_ext/kernel/reporting.rb
String#unicode_normalize, String#unicode_normalize!, String#unicode_normalized? will be introduced in Ruby 2.2. Sample code and implementation can be seen in test case, lib/unicode_normalize.rb and lib/unicode_normalize/normalize.rb.
// U+00E1: LATIN SMALL LETTER A WITH ACUTE
// U+U+0301: COMBINING ACUTE ACCENT
puts "\u00E1" == "a\u0301".unicode_normalize(:nfc)
puts true == "a".unicode_normalized?(:nfc)