Skip to content

Commit

Permalink
Preliminary fix for ohler55#244
Browse files Browse the repository at this point in the history
  • Loading branch information
Alastair Surin committed Aug 9, 2015
1 parent ba63bab commit b022679
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 7 deletions.
2 changes: 1 addition & 1 deletion ext/oj/design.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The parser in sparse.c is represented by the ParseInfo struct in parser.h. Key e

The options are simply flags and parameters that modify the parse output or behavior. They are constructed from the default options and the arguments to the Ruby calls.

The Reader is defined in reader.h and is struct that represented an object that is able to read bytes from some source and provide those bytes to the parser. The single method on the Reader is the 'read_func' which is a pointer to a function. The function pointed to by this member is selected based on the input type. The psuedo subclasses of the Reader are readers for files, general IO, IO that supports partial reads, and strings. A mentioned earlier the string version does not performa as well as direct string access so the original parser is used instead.
The Reader is defined in reader.h and is struct that represented an object that is able to read bytes from some source and provide those bytes to the parser. The single method on the Reader is the 'read_func' which is a pointer to a function. The function pointed to by this member is selected based on the input type. The psuedo subclasses of the Reader are readers for files, general IO, IO that supports partial reads, and strings. A mentioned earlier the string version does not perform as well as direct string access so the original parser is used instead.

On the output side the callback functions such as 'start_hash' represent the method of the parser. By assigning different functions to those members the parser is effectively subclassed to be either the object, compat, strict, or SCP parsers. In a future release those callbacks and the specifics of each parser may be pulled out to become the handlers.

Expand Down
26 changes: 21 additions & 5 deletions ext/oj/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ typedef unsigned long ulong;

static void raise_strict(VALUE obj);
static void dump_val(VALUE obj, int depth, Out out);
static void dump_val_using_params(VALUE obj, int depth, Out out, int argc, VALUE *argv);
static void dump_nil(Out out);
static void dump_true(Out out);
static void dump_false(Out out);
Expand Down Expand Up @@ -88,6 +89,7 @@ static void dump_data_null(VALUE obj, Out out);
static void dump_data_comp(VALUE obj, int depth, Out out);
static void dump_data_obj(VALUE obj, int depth, Out out);
static void dump_obj_comp(VALUE obj, int depth, Out out);
static void dump_obj_comp_using_params(VALUE obj, int depth, Out out, int argc, VALUE *argv);
static void dump_obj_obj(VALUE obj, int depth, Out out);
static void dump_struct_comp(VALUE obj, int depth, Out out);
static void dump_struct_obj(VALUE obj, int depth, Out out);
Expand Down Expand Up @@ -1350,6 +1352,11 @@ dump_data_obj(VALUE obj, int depth, Out out) {

static void
dump_obj_comp(VALUE obj, int depth, Out out) {
dump_obj_comp_using_params(obj, depth, out, 0, 0);
}

static void
dump_obj_comp_using_params(VALUE obj, int depth, Out out, int argc, VALUE *argv) {
if (rb_respond_to(obj, oj_to_hash_id)) {
volatile VALUE h = rb_funcall(obj, oj_to_hash_id, 0);

Expand All @@ -1358,7 +1365,7 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
}
dump_hash(h, Qundef, depth, out->opts->mode, out);
} else if (rb_respond_to(obj, oj_as_json_id)) {
volatile VALUE aj = rb_funcall(obj, oj_as_json_id, 0);
volatile VALUE aj = rb_funcall2(obj, oj_as_json_id, argc, argv);

// Catch the obvious brain damaged recursive dumping.
if (aj == obj) {
Expand Down Expand Up @@ -1894,8 +1901,12 @@ raise_strict(VALUE obj) {

static void
dump_val(VALUE obj, int depth, Out out) {
int type = rb_type(obj);
dump_val_using_params(obj, depth, out, 0, 0);
}

static void
dump_val_using_params(VALUE obj, int depth, Out out, int argc, VALUE *argv) {
int type = rb_type(obj);
if (MAX_DEPTH < depth) {
rb_raise(rb_eNoMemError, "Too deeply nested.\n");
}
Expand Down Expand Up @@ -1965,7 +1976,7 @@ dump_val(VALUE obj, int depth, Out out) {
switch (out->opts->mode) {
case StrictMode: dump_data_strict(obj, out); break;
case NullMode: dump_data_null(obj, out); break;
case CompatMode: dump_obj_comp(obj, depth, out); break;
case CompatMode: dump_obj_comp_using_params(obj, depth, out, argc, argv); break;
case ObjectMode:
default: dump_obj_obj(obj, depth, out); break;
}
Expand All @@ -1988,7 +1999,7 @@ dump_val(VALUE obj, int depth, Out out) {
case NullMode: dump_nil(out); break;
case CompatMode:
case ObjectMode:
default: dump_obj_comp(obj, depth, out); break;
default: dump_obj_comp_using_params(obj, depth, out, argc, argv); break;
}
break;
default:
Expand All @@ -2002,6 +2013,11 @@ dump_val(VALUE obj, int depth, Out out) {

void
oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
oj_dump_obj_to_json_using_params(obj, copts, out, 0, 0);
}

void
oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv) {
if (0 == out->buf) {
out->buf = ALLOC_N(char, 4096);
out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors
Expand All @@ -2015,7 +2031,7 @@ oj_dump_obj_to_json(VALUE obj, Options copts, Out out) {
oj_cache8_new(&out->circ_cache);
}
out->indent = copts->indent;
dump_val(obj, 0, out);
dump_val_using_params(obj, 0, out, argc, argv);
if (0 < out->indent) {
switch (*(out->cur - 1)) {
case ']':
Expand Down
2 changes: 1 addition & 1 deletion ext/oj/oj.c
Original file line number Diff line number Diff line change
Expand Up @@ -1821,7 +1821,7 @@ mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
// To be strict the mimic_object_to_json_options should be used but people
// seem to prefer the option of changing that.
//oj_dump_obj_to_json(self, &mimic_object_to_json_options, &out);
oj_dump_obj_to_json(self, &copts, &out);
oj_dump_obj_to_json_using_params(self, &copts, &out, argc, argv);
if (0 == out.buf) {
rb_raise(rb_eNoMemError, "Not enough memory.");
}
Expand Down
1 change: 1 addition & 0 deletions ext/oj/oj.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ extern VALUE oj_object_parse_cstr(int argc, VALUE *argv, char *json, size_t len)
extern void oj_parse_options(VALUE ropts, Options copts);

extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
Expand Down
50 changes: 50 additions & 0 deletions test/test_as_json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env ruby
# encoding: UTF-8

$: << File.dirname(__FILE__)

require 'helper'
require 'active_record'
require 'sqlite3'
require 'oj'
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => ":memory:"
)

ActiveRecord::Schema.define do
create_table :raccoons do |table|
table.column :name, :string
table.column :occupation, :string
end
end

class ObjectFolder < Minitest::Test
class Raccoon < ActiveRecord::Base

def serializable_hash(opts = {})
super(opts)
end

end

def setup
@default_options = Oj.default_options
end

def teardown
Oj.default_options = @default_options
end

def test_as_json_options
Oj.mimic_JSON()
raccoon = Raccoon.create(name: 'Rocket', occupation: 'Bounty Hunter')
obj = Oj.dump(raccoon.to_json)
assert_equal(obj, '"{\"id\":1,\"name\":\"Rocket\",\"occupation\":\"Bounty Hunter\"}"')
obj = Oj.dump(raccoon.as_json(include:{}, only:[], methods:[]))
assert_equal(obj, '{}')
obj = Oj.dump(raccoon.to_json(include:{}, only:[], methods:[]))
assert_equal(obj, '"{}"')
end

end

0 comments on commit b022679

Please sign in to comment.