Skip to content

Commit

Permalink
Implement JSON::Any and YAML::Any without recursive aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite committed Jan 15, 2018
1 parent b4fed51 commit 597ccac
Show file tree
Hide file tree
Showing 16 changed files with 201 additions and 204 deletions.
26 changes: 0 additions & 26 deletions spec/std/json/any_spec.cr
Expand Up @@ -86,24 +86,6 @@ describe JSON::Any do
end
end

describe "each" do
it "of array" do
elems = [] of Int32
JSON.parse("[1, 2, 3]").each do |any|
elems << any.as_i
end
elems.should eq([1, 2, 3])
end

it "of hash" do
elems = [] of String
JSON.parse(%({"foo": "bar"})).each do |key, value|
elems << key.to_s << value.to_s
end
elems.should eq(%w(foo bar))
end
end

it "traverses big structure" do
obj = JSON.parse(%({"foo": [1, {"bar": [2, 3]}]}))
obj["foo"][1]["bar"][1].as_i.should eq(3)
Expand All @@ -123,12 +105,4 @@ describe JSON::Any do
(/o+/ === JSON.parse(%("foo"))).should be_truthy
$~[0].should eq("oo")
end

it "is enumerable" do
nums = JSON.parse("[1, 2, 3]")
nums.each_with_index do |x, i|
x.should be_a(JSON::Any)
x.raw.should eq(i + 1)
end
end
end
4 changes: 2 additions & 2 deletions spec/std/json/parser_spec.cr
Expand Up @@ -34,7 +34,7 @@ describe JSON::Parser do
it_parses "[0]", [0]
it_parses " [ 0 ] ", [0]

it_parses "{}", {} of String => JSON::Type
it_parses "{}", {} of String => JSON::Any
it_parses %({"foo": 1}), {"foo" => 1}
it_parses %({"foo": 1, "bar": 1.5}), {"foo" => 1, "bar" => 1.5}
it_parses %({"fo\\no": 1}), {"fo\no" => 1}
Expand Down Expand Up @@ -75,7 +75,7 @@ describe JSON::Parser do
end

it "returns raw" do
value = JSON.parse_raw("1")
value = JSON.parse("1").raw
value.should eq(1)
value.should be_a(Int64)
end
Expand Down
6 changes: 3 additions & 3 deletions spec/std/json/pull_parser_spec.cr
Expand Up @@ -46,16 +46,16 @@ class JSON::PullParser
def assert(array : Array)
assert_array do
array.each do |x|
assert x
assert x.raw
end
end
end

def assert(hash : Hash)
assert_object do
hash.each do |key, value|
assert(key.as(String)) do
assert value
assert(key) do
assert value.raw
end
end
end
Expand Down
20 changes: 1 addition & 19 deletions spec/std/yaml/any_spec.cr
Expand Up @@ -128,24 +128,6 @@ describe YAML::Any do
end
end

describe "each" do
it "of array" do
elems = [] of String
YAML.parse("- foo\n- bar\n").each do |any|
elems << any.as_s
end
elems.should eq(%w(foo bar))
end

it "of hash" do
elems = [] of String
YAML.parse("foo: bar").each do |key, value|
elems << key.to_s << value.to_s
end
elems.should eq(%w(foo bar))
end
end

it "traverses big structure" do
obj = YAML.parse("--- \nfoo: \n bar: \n baz: \n - qux\n - fox")
obj["foo"]["bar"]["baz"][1].as_s.should eq("fox")
Expand Down Expand Up @@ -174,7 +156,7 @@ describe YAML::Any do

it "is enumerable" do
nums = YAML.parse("[1, 2, 3]")
nums.each_with_index do |x, i|
nums.as_a.each_with_index do |x, i|
x.should be_a(YAML::Any)
x.raw.should eq(i + 1)
end
Expand Down
4 changes: 2 additions & 2 deletions spec/std/yaml/schema/core_spec.cr
Expand Up @@ -31,15 +31,15 @@ private def it_parses_scalar_from_pull(string, expected, file = __FILE__, line =
end
end

private def it_parses_scalar_from_pull(string, file = __FILE__, line = __LINE__, &block : YAML::Type ->)
private def it_parses_scalar_from_pull(string, file = __FILE__, line = __LINE__, &block : YAML::Any::Type ->)
it "parses #{string.inspect}", file, line do
pull = YAML::PullParser.new(%(value: #{string}))
pull.read_stream_start
pull.read_document_start
pull.read_mapping_start
pull.read_scalar # key

block.call(YAML::Schema::Core.parse_scalar(pull).as(YAML::Type))
block.call(YAML::Schema::Core.parse_scalar(pull).as(YAML::Any::Type))
end
end

Expand Down
8 changes: 4 additions & 4 deletions spec/std/yaml/yaml_spec.cr
Expand Up @@ -7,26 +7,26 @@ describe "YAML" do
it { YAML.parse("- foo\n- bar").should eq(["foo", "bar"]) }
it { YAML.parse_all("---\nfoo\n---\nbar\n").should eq(["foo", "bar"]) }
it { YAML.parse("foo: bar").should eq({"foo" => "bar"}) }
it { YAML.parse("--- []\n").should eq([] of YAML::Type) }
it { YAML.parse("--- []\n").should eq([] of YAML::Any) }
it { YAML.parse("---\n...").should eq nil }

it "parses recursive sequence" do
doc = YAML.parse("--- &foo\n- *foo\n")
doc[0].raw.as(Array).should be(doc.raw.as(Array))
doc[0].as_a.should be(doc.raw.as(Array))
end

it "parses recursive mapping" do
doc = YAML.parse(%(--- &1
friends:
- *1
))
doc["friends"][0].raw.as(Hash).should be(doc.raw.as(Hash))
doc["friends"][0].as_h.should be(doc.as_h)
end

it "parses alias to scalar" do
doc = YAML.parse("---\n- &x foo\n- *x\n")
doc.should eq(["foo", "foo"])
doc[0].raw.as(String).should be(doc[1].raw.as(String))
doc[0].as_s.should be(doc[1].as_s)
end

describe "merging with << key" do
Expand Down
8 changes: 0 additions & 8 deletions src/json.cr
Expand Up @@ -75,16 +75,8 @@ module JSON
end
end

# All valid JSON types.
alias Type = Nil | Bool | Int64 | Float64 | String | Array(Type) | Hash(String, Type)

# Parses a JSON document as a `JSON::Any`.
def self.parse(input : String | IO) : Any
Any.new parse_raw(input)
end

# Parses a JSON document as a `JSON::Type`.
def self.parse_raw(input : String | IO) : Type
Parser.new(input).parse
end
end
Expand Down
85 changes: 45 additions & 40 deletions src/json/any.cr
@@ -1,4 +1,4 @@
# `JSON::Any` is a convenient wrapper around all possible JSON types (`JSON::Type`)
# `JSON::Any` is a convenient wrapper around all possible JSON types (`JSON::Any::Type`)
# and can be used for traversing dynamic or unknown JSON structures.
#
# ```
Expand All @@ -14,7 +14,8 @@
# when the underlying value is not a String will raise: the value won't automatically
# be converted (parsed) to a `String`.
struct JSON::Any
include Enumerable(self)
# All possible JSON types.
alias Type = Nil | Bool | Int64 | Float64 | String | Array(Any) | Hash(String, Any)

# Reads a `JSON::Any` value from the given pull parser.
def self.new(pull : JSON::PullParser)
Expand All @@ -30,27 +31,27 @@ struct JSON::Any
when :string
new pull.read_string
when :begin_array
ary = [] of JSON::Type
ary = [] of JSON::Any
pull.read_array do
ary << new(pull).raw
ary << new(pull)
end
new ary
when :begin_object
hash = {} of String => JSON::Type
hash = {} of String => JSON::Any
pull.read_object do |key|
hash[key] = new(pull).raw
hash[key] = new(pull)
end
new hash
else
raise "Unknown pull kind: #{pull.kind}"
end
end

# Returns the raw underlying value, a `JSON::Type`.
getter raw : JSON::Type
# Returns the raw underlying value.
getter raw : Type

# Creates a `JSON::Any` that wraps the given `JSON::Type`.
def initialize(@raw : JSON::Type)
# Creates a `JSON::Any` that wraps the given value.
def initialize(@raw : Type)
end

# Assumes the underlying value is an `Array` or `Hash` and returns its size.
Expand All @@ -72,7 +73,7 @@ struct JSON::Any
def [](index : Int) : JSON::Any
case object = @raw
when Array
Any.new object[index]
object[index]
else
raise "Expected Array for #[](index : Int), not #{object.class}"
end
Expand All @@ -84,8 +85,7 @@ struct JSON::Any
def []?(index : Int) : JSON::Any?
case object = @raw
when Array
value = object[index]?
value.nil? ? nil : Any.new(value)
object[index]?
else
raise "Expected Array for #[]?(index : Int), not #{object.class}"
end
Expand All @@ -97,7 +97,7 @@ struct JSON::Any
def [](key : String) : JSON::Any
case object = @raw
when Hash
Any.new object[key]
object[key]
else
raise "Expected Hash for #[](key : String), not #{object.class}"
end
Expand All @@ -109,31 +109,12 @@ struct JSON::Any
def []?(key : String) : JSON::Any?
case object = @raw
when Hash
value = object[key]?
value.nil? ? nil : Any.new(value)
object[key]?
else
raise "Expected Hash for #[]?(key : String), not #{object.class}"
end
end

# Assumes the underlying value is an `Array` or `Hash` and yields each
# of the elements or key/values, always as `JSON::Any`.
# Raises if the underlying value is not an `Array` or `Hash`.
def each
case object = @raw
when Array
object.each do |elem|
yield Any.new(elem), Any.new(nil)
end
when Hash
object.each do |key, value|
yield Any.new(key), Any.new(value)
end
else
raise "Expected Array or Hash for #each, not #{object.class}"
end
end

# Checks that the underlying value is `Nil`, and returns `nil`.
# Raises otherwise.
def as_nil : Nil
Expand Down Expand Up @@ -214,26 +195,26 @@ struct JSON::Any

# Checks that the underlying value is `Array`, and returns its value.
# Raises otherwise.
def as_a : Array(Type)
def as_a : Array(Any)
@raw.as(Array)
end

# Checks that the underlying value is `Array`, and returns its value.
# Returns `nil` otherwise.
def as_a? : Array(Type)?
as_a if @raw.is_a?(Array(Type))
def as_a? : Array(Any)?
as_a if @raw.is_a?(Array)
end

# Checks that the underlying value is `Hash`, and returns its value.
# Raises otherwise.
def as_h : Hash(String, Type)
def as_h : Hash(String, Any)
@raw.as(Hash)
end

# Checks that the underlying value is `Hash`, and returns its value.
# Returns `nil` otherwise.
def as_h? : Hash(String, Type)?
as_h if @raw.is_a?(Hash(String, Type))
def as_h? : Hash(String, Any)?
as_h if @raw.is_a?(Hash)
end

# :nodoc:
Expand Down Expand Up @@ -276,6 +257,30 @@ class Object
end
end

struct Value
def ==(other : JSON::Any)
self == other.raw
end
end

class Reference
def ==(other : JSON::Any)
self == other.raw
end
end

class Array
def ==(other : JSON::Any)
self == other.raw
end
end

class Hash
def ==(other : JSON::Any)
self == other.raw
end
end

class Regex
def ===(other : JSON::Any)
value = self === other.raw
Expand Down

0 comments on commit 597ccac

Please sign in to comment.