Skip to content

Commit

Permalink
Work around the fact the JSON gem was overwriting to_json implementat…
Browse files Browse the repository at this point in the history
…ion for all Ruby core classes.

This is required because the JSON gem is incompatible with Rails behavior and was not allowing ActiveModel::Errors to be serialized.
So we need to ensure Rails implementation is the one triggered. [#4890 state:resolved]
  • Loading branch information
josevalim committed Jun 26, 2010
1 parent cfaaed3 commit 7bd85a8
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 22 deletions.
17 changes: 7 additions & 10 deletions activemodel/lib/active_model/errors.rb
Expand Up @@ -3,6 +3,7 @@
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/ordered_hash'

module ActiveModel
Expand Down Expand Up @@ -164,15 +165,12 @@ def empty?
# # <error>name must be specified</error>
# # </errors>
def to_xml(options={})
require 'builder' unless defined? ::Builder
options[:root] ||= "errors"
options[:indent] ||= 2
options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])

options[:builder].instruct! unless options.delete(:skip_instruct)
options[:builder].errors do |e|
to_a.each { |error| e.error(error) }
end
to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
end

# Returns an array as JSON representation for this object.
def as_json(options=nil)
to_a
end

# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
Expand Down Expand Up @@ -283,7 +281,6 @@ def full_messages
# <li><tt>errors.attributes.title.blank</tt></li>
# <li><tt>errors.messages.blank</tt></li>
# </ol>

def generate_message(attribute, type = :invalid, options = {})
type = options.delete(:message) if options[:message].is_a?(Symbol)

Expand Down
Expand Up @@ -32,7 +32,7 @@ def test_validate_presences
assert t.valid?
end

test 'accepts array arguments' do
def test_accepts_array_arguments
Topic.validates_presence_of %w(title content)
t = Topic.new
assert t.invalid?
Expand Down
21 changes: 15 additions & 6 deletions activemodel/test/cases/validations_test.rb
Expand Up @@ -6,6 +6,9 @@
require 'models/custom_reader'
require 'models/automobile'

require 'active_support/json'
require 'active_support/xml_mini'

class ValidationsTest < ActiveModel::TestCase

def setup
Expand Down Expand Up @@ -158,12 +161,18 @@ def test_invalid_validator
end
end

def test_errors_to_xml
r = Reply.new :title => "Wrong Create"
assert r.invalid?
xml = r.errors.to_xml(:skip_instruct => true)
assert_equal "<errors>", xml.first(8)
assert xml.include?("<error>Content is Empty</error>")
def test_errors_conversions
Topic.validates_presence_of %w(title content)
t = Topic.new
assert t.invalid?

xml = t.errors.to_xml
assert_match %r{<errors>}, xml
assert_match %r{<error>Title can't be blank</error>}, xml
assert_match %r{<error>Content can't be blank</error>}, xml

json = t.errors.to_json
assert_equal t.errors.to_a.to_json, json
end

def test_validation_order
Expand Down
19 changes: 14 additions & 5 deletions activesupport/lib/active_support/json/encoding.rb
Expand Up @@ -128,12 +128,21 @@ def escape(string)
end
end

class Object
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
# their default behavior. That said, we need to define the basic to_json method in all of them,
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
klass.class_eval <<-RUBY, __FILE__, __LINE__
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
RUBY
end

class Object
def as_json(options = nil) #:nodoc:
if respond_to?(:to_hash)
to_hash
Expand Down
7 changes: 7 additions & 0 deletions activesupport/test/ordered_hash_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
require 'active_support/json'

class OrderedHashTest < Test::Unit::TestCase
def setup
Expand Down Expand Up @@ -185,6 +186,12 @@ def test_inspect
assert @ordered_hash.inspect.include?(@hash.inspect)
end

def test_json
ordered_hash = ActiveSupport::OrderedHash[:foo, :bar]
hash = Hash[:foo, :bar]
assert_equal ordered_hash.to_json, hash.to_json
end

def test_alternate_initialization_with_splat
alternate = ActiveSupport::OrderedHash[1,2,3,4]
assert_kind_of ActiveSupport::OrderedHash, alternate
Expand Down

0 comments on commit 7bd85a8

Please sign in to comment.