Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

YAML | JSON.mapping presence: true option. #4843

Merged
merged 1 commit into from
Aug 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions spec/std/json/mapping_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ private class JSONWithNilableUnion2
})
end

private class JSONWithPresence
JSON.mapping({
first_name: {type: String?, presence: true, nilable: true},
last_name: {type: String?, presence: true, nilable: true},
})
end

describe "JSON mapping" do
it "parses person" do
person = JSONPerson.from_json(%({"name": "John", "age": 30}))
Expand Down Expand Up @@ -427,4 +434,14 @@ describe "JSON mapping" do
obj.value.should be_nil
obj.to_json.should eq(%({}))
end

describe "parses JSON with presence markers" do
it "parses person with absent attributes" do
json = JSONWithPresence.from_json(%({"first_name": null}))
json.first_name.should be_nil
json.first_name_present?.should be_true
json.last_name.should be_nil
json.last_name_present?.should be_false
end
end
end
17 changes: 17 additions & 0 deletions spec/std/yaml/mapping_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ private class YAMLWithTimeEpochMillis
})
end

private class YAMLWithPresence
YAML.mapping({
first_name: {type: String?, presence: true, nilable: true},
last_name: {type: String?, presence: true, nilable: true},
})
end

describe "YAML mapping" do
it "parses person" do
person = YAMLPerson.from_yaml("---\nname: John\nage: 30\n")
Expand Down Expand Up @@ -288,4 +295,14 @@ describe "YAML mapping" do
yaml.value.should eq(Time.epoch_ms(1459860483856))
yaml.to_yaml.should eq("---\nvalue: 1459860483856\n")
end

describe "parses YAML with presence markers" do
it "parses person with absent attributes" do
yaml = YAMLWithPresence.from_yaml("---\nfirst_name:\n")
yaml.first_name.should be_nil
yaml.first_name_present?.should be_true
yaml.last_name.should be_nil
yaml.last_name_present?.should be_false
end
end
end
15 changes: 15 additions & 0 deletions src/json/mapping.cr
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ module JSON
# * **root**: assume the value is inside a JSON object with a given key (see `Object.from_json(string_or_io, root)`)
# * **setter**: if `true`, will generate a setter for the variable, `true` by default
# * **getter**: if `true`, will generate a getter for the variable, `true` by default
# * **presence**: if `true`, a `{{key}}_present?` method will be generated when the key was present (even if it has a `null` value), `false` by default
#
# This macro by default defines getters and setters for each variable (this can be overrided with *setter* and *getter*).
# The mapping doesn't define a constructor accepting these variables as arguments, but you can provide an overload.
Expand Down Expand Up @@ -78,6 +79,14 @@ module JSON
@{{key.id}}
end
{% end %}

{% if value[:presence] %}
@{{key.id}}_present : Bool = false

def {{key.id}}_present?
@{{key.id}}_present
end
{% end %}
{% end %}

def initialize(%pull : ::JSON::PullParser)
Expand Down Expand Up @@ -144,6 +153,12 @@ module JSON
@{{key.id}} = (%var{key.id}).as({{value[:type]}})
{% end %}
{% end %}

{% for key, value in properties %}
{% if value[:presence] %}
@{{key.id}}_present = %found{key.id}
{% end %}
{% end %}
end

def to_json(json : ::JSON::Builder)
Expand Down
16 changes: 16 additions & 0 deletions src/yaml/mapping.cr
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ module YAML
# * *converter* takes an alternate type for parsing. It requires a `#from_yaml` method in that class, and returns an instance of the given type. Examples of converters are `Time::Format` and `Time::EpochConverter` for `Time`.
# * **setter**: if `true`, will generate a setter for the variable, `true` by default
# * **getter**: if `true`, will generate a getter for the variable, `true` by default
# * **presence**: if `true`, a `{{key}}_present?` method will be generated when the key was present (even if it has a `null` value), `false` by default
#
# This macro by default defines getters and setters for each variable (this can be overrided with *setter* and *getter*).
# The mapping doesn't define a constructor accepting these variables as arguments, but you can provide an overload.
Expand Down Expand Up @@ -85,6 +86,14 @@ module YAML
@{{key.id}}
end
{% end %}

{% if value[:presence] %}
@{{key.id}}_present : Bool = false

def {{key.id}}_present?
@{{key.id}}_present
end
{% end %}
{% end %}

def initialize(%pull : ::YAML::PullParser)
Expand All @@ -100,6 +109,7 @@ module YAML
{% for key, value in properties %}
when {{value[:key] || key.id.stringify}}
%found{key.id} = true

%var{key.id} =
{% if value[:nilable] || value[:default] != nil %} %pull.read_null_or { {% end %}

Expand Down Expand Up @@ -144,6 +154,12 @@ module YAML
@{{key.id}} = %var{key.id}.as({{value[:type]}})
{% end %}
{% end %}

{% for key, value in properties %}
{% if value[:presence] %}
@{{key.id}}_present = %found{key.id}
{% end %}
{% end %}
end

def to_yaml(%yaml : ::YAML::Builder)
Expand Down