From 29d4a4f19ff2e64a606e23d18cc8a02ecacd4e4a Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Thu, 20 Sep 2012 10:03:17 +0200 Subject: Increase hash likeness of State objects --- .travis.yml | 7 +++--- CHANGES | 1 + Gemfile | 9 +------ ext/json/ext/generator/generator.c | 37 ++++++++++++++++++++++++++++- ext/json/ext/generator/generator.h | 8 +++++++ java/src/json/ext/GeneratorState.java | 20 ++++++++++++++-- lib/json/pure/generator.rb | 44 +++++++++++++++++++++++++---------- tests/test_json.rb | 2 +- tests/test_json_encoding.rb | 2 +- tests/test_json_fixtures.rb | 2 +- tests/test_json_generate.rb | 44 +++++++++++++++++++++-------------- tests/test_json_generic_object.rb | 2 +- tests/test_json_string_matching.rb | 2 +- tests/test_json_unicode.rb | 2 +- 14 files changed, 131 insertions(+), 51 deletions(-) diff --git a/.travis.yml b/.travis.yml index 647a707..185b3e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,18 +3,17 @@ bundler_args: --binstubs # Specify which ruby versions you wish to run your tests on, each version will be used rvm: - - 1.8.6 - 1.8.7 - 1.9.2 - 1.9.3 + - ree - rbx-18mode - rbx-19mode - - ree - jruby-18mode - jruby-19mode - ruby-head matrix: allow_failures: - - rvm: 1.8.6 - + - rvm: rbx-18mode + - rvm: rbx-19mode script: "bundle exec rake" diff --git a/CHANGES b/CHANGES index 00f042d..5d2cc61 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,7 @@ 2012-11-29 (1.7.6) * Add GeneratorState#merge alias for JRuby, fix state accessor methods. Thx to jvshahid@github. + * Increase hash likeness of state objects. 2012-08-17 (1.7.5) * Fix compilation of extension on older rubies. 2012-07-26 (1.7.4) diff --git a/Gemfile b/Gemfile index 5a97c8c..de6df47 100644 --- a/Gemfile +++ b/Gemfile @@ -6,11 +6,4 @@ gemspec :name => 'json' gemspec :name => 'json_pure' gemspec :name => 'json-java' -group :development, :test do - gem 'simplecov', :platform => :mri_19 - gem 'utils' -end - -group :test do - gem 'test-unit', '~> 2.5', :platform => :mri_19 -end +gem 'utils' diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index e64ea50..ae4593c 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -598,6 +598,18 @@ static VALUE cState_configure(VALUE self, VALUE opts) return self; } +static void set_state_ivars(VALUE hash, VALUE state) +{ + VALUE ivars = rb_obj_instance_variables(state); + int i = 0; + for (i = 0; i < RARRAY_LEN(ivars); i++) { + VALUE key = rb_funcall(rb_ary_entry(ivars, i), i_to_s, 0); + long key_len = RSTRING_LEN(key); + VALUE value = rb_iv_get(state, StringValueCStr(key)); + rb_hash_aset(hash, rb_str_intern(rb_str_substr(key, 1, key_len - 1)), value); + } +} + /* * call-seq: to_h * @@ -608,6 +620,7 @@ static VALUE cState_to_h(VALUE self) { VALUE result = rb_hash_new(); GET_STATE(self); + set_state_ivars(result, self); rb_hash_aset(result, ID2SYM(i_indent), rb_str_new(state->indent, state->indent_len)); rb_hash_aset(result, ID2SYM(i_space), rb_str_new(state->space, state->space_len)); rb_hash_aset(result, ID2SYM(i_space_before), rb_str_new(state->space_before, state->space_before_len)); @@ -629,11 +642,31 @@ static VALUE cState_to_h(VALUE self) */ static VALUE cState_aref(VALUE self, VALUE name) { + name = rb_funcall(name, i_to_s, 0); if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) { return rb_funcall(self, i_send, 1, name); } else { - return Qnil; + return rb_ivar_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name))); + } +} + +/* +* call-seq: []=(name, value) +* +* Set the attribute name to value. +*/ +static VALUE cState_aset(VALUE self, VALUE name, VALUE value) +{ + VALUE name_writer; + + name = rb_funcall(name, i_to_s, 0); + name_writer = rb_str_cat2(rb_str_dup(name), "="); + if (RTEST(rb_funcall(self, i_respond_to_p, 1, name_writer))) { + return rb_funcall(self, i_send, 2, name_writer, value); + } else { + rb_ivar_set(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)), value); } + return Qnil; } static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) @@ -1326,7 +1359,9 @@ void Init_generator() rb_define_method(cState, "configure", cState_configure, 1); rb_define_alias(cState, "merge", "configure"); rb_define_method(cState, "to_h", cState_to_h, 0); + rb_define_alias(cState, "to_hash", "to_h"); rb_define_method(cState, "[]", cState_aref, 1); + rb_define_method(cState, "[]=", cState_aset, 2); rb_define_method(cState, "generate", cState_generate, 1); mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods"); diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index 7d429d5..1b7074c 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -14,6 +14,14 @@ #include "re.h" #endif +#ifndef rb_intern_str +#define rb_intern_str(string) SYM2ID(rb_str_intern(string)) +#endif + +#ifndef rb_obj_instance_variables +#define rb_obj_instance_variables(object) rb_funcall(object, rb_intern("instance_variables"), 0) +#endif + #define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key)) /* unicode defintions */ diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index 58904ee..658d6d0 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -262,6 +262,19 @@ public class GeneratorState extends RubyObject { String name = vName.asJavaString(); if (getMetaClass().isMethodBound(name, true)) { return send(context, vName, Block.NULL_BLOCK); + } else { + return getInstanceVariables().getInstanceVariable("@" + name); + } + } + + @JRubyMethod(name="[]=", required=2) + public IRubyObject op_aset(ThreadContext context, IRubyObject vName, IRubyObject value) { + String name = vName.asJavaString(); + String nameWriter = name + "="; + if (getMetaClass().isMethodBound(nameWriter, true)) { + return send(context, context.getRuntime().newString(nameWriter), value, Block.NULL_BLOCK); + } else { + getInstanceVariables().setInstanceVariable("@" + name, value); } return context.getRuntime().getNil(); } @@ -480,9 +493,9 @@ public class GeneratorState extends RubyObject { * *

Returns the configuration instance variables as a hash, that can be * passed to the configure method. - * @return + * @return the hash */ - @JRubyMethod + @JRubyMethod(alias = "to_hash") public RubyHash to_h(ThreadContext context) { Ruby runtime = context.getRuntime(); RubyHash result = RubyHash.newHash(runtime); @@ -498,6 +511,9 @@ public class GeneratorState extends RubyObject { result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context)); result.op_aset(context, runtime.newSymbol("depth"), depth_get(context)); result.op_aset(context, runtime.newSymbol("buffer_initial_length"), buffer_initial_length_get(context)); + for (String name: getInstanceVariableNameList()) { + result.op_aset(context, runtime.newSymbol(name.substring(1)), getInstanceVariables().getInstanceVariable(name)); + } return result; } diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 6916678..fcbc604 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -220,15 +220,20 @@ module JSON # Configure this State instance with the Hash _opts_, and return # itself. def configure(opts) - @indent = opts[:indent] if opts.key?(:indent) - @space = opts[:space] if opts.key?(:space) - @space_before = opts[:space_before] if opts.key?(:space_before) - @object_nl = opts[:object_nl] if opts.key?(:object_nl) - @array_nl = opts[:array_nl] if opts.key?(:array_nl) - @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan) - @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only) - @depth = opts[:depth] || 0 - @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode) + for key, value in opts + instance_variable_set "@#{key}", value + end + @indent = opts[:indent] if opts.key?(:indent) + @space = opts[:space] if opts.key?(:space) + @space_before = opts[:space_before] if opts.key?(:space_before) + @object_nl = opts[:object_nl] if opts.key?(:object_nl) + @array_nl = opts[:array_nl] if opts.key?(:array_nl) + @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan) + @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only) + @depth = opts[:depth] || 0 + @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode) + @buffer_initial_length ||= opts[:buffer_initial_length] + if !opts.key?(:max_nesting) # defaults to 100 @max_nesting = 100 elsif opts[:max_nesting] @@ -244,12 +249,15 @@ module JSON # passed to the configure method. def to_h result = {} - for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only quirks_mode buffer_initial_length depth] - result[iv.intern] = instance_variable_get("@#{iv}") + for iv in instance_variables + iv = iv.to_s[1..-1] + result[iv.to_sym] = self[iv] end result end + alias to_hash to_h + # Generates a valid JSON document from object +obj+ and returns the # result. If no valid JSON document can be created this method raises a # GeneratorError exception. @@ -267,7 +275,19 @@ module JSON # Return the value returned by method +name+. def [](name) - __send__ name + if respond_to?(name) + __send__(name) + else + instance_variable_get("@#{name}") + end + end + + def []=(name, value) + if respond_to?(name_writer = "#{name}=") + __send__ name_writer, value + else + instance_variable_set "@#{name}", value + end end end diff --git a/tests/test_json.rb b/tests/test_json.rb index 5ebe5ec..be974cd 100755 --- a/tests/test_json.rb +++ b/tests/test_json.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# -*- coding: utf-8 -*- +# encoding: utf-8 require 'test/unit' require File.join(File.dirname(__FILE__), 'setup_variant') diff --git a/tests/test_json_encoding.rb b/tests/test_json_encoding.rb index caa0c6c..fa7d878 100644 --- a/tests/test_json_encoding.rb +++ b/tests/test_json_encoding.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# -*- coding: utf-8 -*- +# encoding: utf-8 require 'test/unit' require File.join(File.dirname(__FILE__), 'setup_variant') diff --git a/tests/test_json_fixtures.rb b/tests/test_json_fixtures.rb index 37e5145..584dffd 100755 --- a/tests/test_json_fixtures.rb +++ b/tests/test_json_fixtures.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# -*- coding: utf-8 -*- +# encoding: utf-8 require 'test/unit' require File.join(File.dirname(__FILE__), 'setup_variant') diff --git a/tests/test_json_generate.rb b/tests/test_json_generate.rb index 5a96b05..5947bf6 100755 --- a/tests/test_json_generate.rb +++ b/tests/test_json_generate.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# -*- coding: utf-8 -*- +# encoding: utf-8 require 'test/unit' require File.join(File.dirname(__FILE__), 'setup_variant') @@ -227,7 +227,7 @@ EOT GC.stress = stress end if GC.respond_to?(:stress=) - def test_configure_using_merge + def test_configure_using_configure_and_merge numbered_state = { :indent => "1", :space => '2', @@ -252,22 +252,6 @@ EOT end if defined?(JSON::Ext::Generator) - [:merge, :configure].each do |method| - define_method "test_configure_using_#{method}" do - state = JSON::Ext::Generator::State.new - state.send method, :indent => "1", - :space => '2', - :space_before => '3', - :object_nl => '4', - :array_nl => '5' - assert_equal '1', state.indent - assert_equal '2', state.space - assert_equal '3', state.space_before - assert_equal '4', state.object_nl - assert_equal '5', state.array_nl - end - end - def test_broken_bignum # [ruby-core:38867] pid = fork do Bignum.class_eval do @@ -288,4 +272,28 @@ EOT # introducing race conditions of tests are run in parallel end end + + def test_hash_likeness_set_symbol + state = JSON.state.new + assert_equal nil, state[:foo] + assert_equal nil, state['foo'] + state[:foo] = :bar + assert_equal :bar, state[:foo] + assert_equal :bar, state['foo'] + state_hash = state.to_hash + assert_kind_of Hash, state_hash + assert_equal :bar, state_hash[:foo] + end + + def test_hash_likeness_set_string + state = JSON.state.new + assert_equal nil, state[:foo] + assert_equal nil, state['foo'] + state['foo'] = :bar + assert_equal :bar, state[:foo] + assert_equal :bar, state['foo'] + state_hash = state.to_hash + assert_kind_of Hash, state_hash + assert_equal :bar, state_hash[:foo] + end end diff --git a/tests/test_json_generic_object.rb b/tests/test_json_generic_object.rb index 1cec795..83093b8 100644 --- a/tests/test_json_generic_object.rb +++ b/tests/test_json_generic_object.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# -*- coding: utf-8 -*- +# encoding: utf-8 require 'test/unit' require File.join(File.dirname(__FILE__), 'setup_variant') diff --git a/tests/test_json_string_matching.rb b/tests/test_json_string_matching.rb index 97e8c07..2ddedfa 100644 --- a/tests/test_json_string_matching.rb +++ b/tests/test_json_string_matching.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# -*- coding: utf-8 -*- +# encoding: utf-8 require 'test/unit' require File.join(File.dirname(__FILE__), 'setup_variant') diff --git a/tests/test_json_unicode.rb b/tests/test_json_unicode.rb index c328811..8352d5c 100755 --- a/tests/test_json_unicode.rb +++ b/tests/test_json_unicode.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# -*- coding: utf-8 -*- +# encoding: utf-8 require 'test/unit' require File.join(File.dirname(__FILE__), 'setup_variant') -- cgit v1.2.1