-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
400 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
struct JSON::Any | ||
def dig?(keys) | ||
raise ArgumentError.new("Keys must not be empty!") if keys.empty? | ||
|
||
if keys.size > 1 | ||
key = keys.shift | ||
value = self[key]? | ||
|
||
return unless value | ||
|
||
if value.is_a?(JSON::Any) | ||
value.dig?(keys) | ||
else | ||
raise ArgumentError.new("JSON is expected to have JSON::Any value at key #{key}") | ||
end | ||
else | ||
self[keys.first]? | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,128 @@ | ||
module Prism::Params | ||
# Define a single param. Must be called within the `#params` block. | ||
# Define a single param or nested params. Must be called within the `#params` block. | ||
# | ||
# **Arguments:** | ||
# | ||
# - *name* declares an access key for the `params` tuple | ||
# - *type* defines a type which the param must be casted to, otherwise validation will fail (i.e. "foo" won't cast to `Int32`) | ||
# - *:nilable* option declares if this param is nilable (the same effect is achieved with nilable *type*, i.e. `Int32?`) | ||
# - *validate* option accepts a `Proc` which must return truthy value for the param to pass validation | ||
# - *proc* allows to call `Proc` each time the param is casted (after validation). The param becomes the returned value, so this *proc* **must** return the same type. | ||
# | ||
# NOTE: If a param is nilable, but is present and of invalid type, an `InvalidParamTypeError` will be raised. | ||
# Example: | ||
# | ||
# ``` | ||
# params do | ||
# param :id, Int32, validate: ->(id : Int32) { id > 0 } | ||
# param :name, String? # => Nilable | ||
# param :age, Int32, nilable: true # => Nilable as well | ||
# param :uuid, String, proc: ->(uuid : String) do | ||
# UUID.new(uuid) | ||
# rescue ex : ArgumentError | ||
# error!(:uuid, ex.message) | ||
# param :user do | ||
# param :email, String, validate: {regex: /@/} | ||
# param :password, String, validate: {size: (0..32)} | ||
# param :age, Int32? | ||
# end | ||
# end | ||
# ``` | ||
macro param(name, type _type, **options) | ||
# | ||
# **Nested params** (e.g. `param :user do`) can have following options: | ||
# | ||
# - *nilable* (`false` by default) | ||
# | ||
# **Single param** has two mandatory arguments: | ||
# | ||
# - *name* declares an access key for the `params` tuple | ||
# - *type* defines a type which the param must be casted to, otherwise validation will fail (i.e. "foo" won't cast to `Int32`) | ||
# | ||
# **Single param** can also have some options: | ||
# | ||
# - *nilable* declares if this param is nilable (the same effect is achieved with nilable *type*, i.e. `Int32?`) | ||
# - *validate* defines validation options. See `Validation` | ||
# - *proc* will be called each time the param is casted (right after validation). The param becomes the returned value, so this *proc* **must** return the same type | ||
# | ||
# NOTE: If a param is nilable, but is present and of invalid type, an `InvalidParamTypeError` will be raised. | ||
macro param(name, type _type = nil, **options, &block) | ||
{% if block %} | ||
{% | ||
INTERNAL__PRISM_PARAMS_PARENTS[:current_value].push(name) | ||
|
||
if options[:nilable] | ||
INTERNAL__PRISM_PARAMS_PARENTS[:nilable][INTERNAL__PRISM_PARAMS_PARENTS[:current_value].select { |x| x }] = options[:nilable] | ||
end | ||
%} | ||
|
||
{{yield}} | ||
|
||
\{% | ||
current_size = INTERNAL__PRISM_PARAMS_PARENTS[:current_value].select{ |x| x }.size | ||
INTERNAL__PRISM_PARAMS_PARENTS[:current_value][current_size - 1] = nil | ||
INTERNAL__PRISM_PARAMS_PARENTS[:current_value] = INTERNAL__PRISM_PARAMS_PARENTS[:current_value].select{ |x| x } | ||
%} | ||
{% else %} | ||
{% | ||
raise "Expected param type" unless _type | ||
|
||
nilable = if options[:nilable] == nil | ||
"#{_type}".includes?("?") || "#{_type}".includes?("Nil") | ||
else | ||
options[:nilable] | ||
end | ||
|
||
INTERNAL__PRISM_PARAMS.push({ | ||
parents: INTERNAL__PRISM_PARAMS_PARENTS[:current_value].size > 0 ? INTERNAL__PRISM_PARAMS_PARENTS[:current_value].map { |x| x } : nil, | ||
name: name, | ||
type: _type, | ||
nilable: nilable, | ||
validate: options[:validate], | ||
proc: options[:proc], | ||
}) | ||
%} | ||
{% end %} | ||
end | ||
|
||
# TODO: Introduce macro recursion | ||
private macro define_params_tuple | ||
{% | ||
nilable = if options[:nilable] == nil | ||
"#{_type}".includes?("?") || "#{_type}".includes?("Nil") | ||
else | ||
options[:nilable] | ||
end | ||
|
||
INTERNAL__PRISM_PARAMS.push({ | ||
name: name, | ||
type: _type, | ||
nilable: nilable, | ||
validations: options[:validate], | ||
proc: options[:proc], | ||
}) | ||
tuple_hash = INTERNAL__PRISM_PARAMS.reduce({} of Object => Object) do |hash, param| | ||
if !param[:parents] | ||
hash[param[:name].id.stringify] = param[:type] | ||
else | ||
if param[:parents].size == 0 | ||
hash[param[:name].id.stringify] = param[:type] | ||
elsif param[:parents].size == 1 | ||
key = param[:parents][0].id.stringify | ||
hash[key] = {} of Object => Object unless hash[key] | ||
hash[key]["__nilable"] = true if INTERNAL__PRISM_PARAMS_PARENTS[:nilable][param[:parents]] | ||
|
||
hash[key][param[:name].id.stringify] = param[:type] | ||
elsif param[:parents].size == 2 | ||
key = param[:parents][0].id.stringify | ||
hash[key] = {} of Object => Object unless hash[key] | ||
|
||
key1 = param[:parents][1].id.stringify | ||
hash[key][key1] = {} of Object => Object unless hash[key][key1] | ||
hash[key][key1]["__nilable"] = true if INTERNAL__PRISM_PARAMS_PARENTS[:nilable][param[:parents]] | ||
|
||
hash[key][key1][param[:name].id.stringify] = param[:type] | ||
elsif param[:parents].size == 3 | ||
key = param[:parents][0].id.stringify | ||
hash[key] = {} of Object => Object unless hash[key] | ||
|
||
key1 = param[:parents][1].id.stringify | ||
hash[key][key1] = {} of Object => Object unless hash[key][key1] | ||
|
||
key2 = param[:parents][2].id.stringify | ||
hash[key][key1][key2] = {} of Object => Object unless hash[key][key1][key2] | ||
hash[key][key1][key2]["__nilable"] = true if INTERNAL__PRISM_PARAMS_PARENTS[:nilable][param[:parents]] | ||
|
||
hash[key][key1][key2][param[:name].id.stringify] = param[:type] | ||
else | ||
raise "Too deep params nesting" | ||
end | ||
end | ||
|
||
hash | ||
end | ||
%} | ||
|
||
# Damn hacks | ||
alias ParamsTuple = NamedTuple({{"#{hash}".gsub(/\"/, "\"").gsub(%r[=> {(.*), "__nilable" => true(.*)}], "=> {\\1\\2 | Nil ").gsub(/ \=>/, ":")[1..-2].id}}) | ||
end | ||
|
||
private macro define_param_type | ||
struct Param < AbstractParam | ||
property value : {{INTERNAL__PRISM_PARAMS.map(&.[:type]).join(" | ").id}} | String | Hash(String, Param) | Nil | ||
|
||
def initialize(@value) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.