paradocs 1.1.0 → 1.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -11
- data/docs/changelog.md +17 -0
- data/docs/documentation_generation.md +38 -25
- data/docs/payload_builder.md +9 -8
- data/lib/paradocs/default_types.rb +0 -10
- data/lib/paradocs/extensions/payload_builder.rb +5 -3
- data/lib/paradocs/extensions/structure.rb +17 -16
- data/lib/paradocs/field_dsl.rb +12 -0
- data/lib/paradocs/policies.rb +12 -1
- data/lib/paradocs/schema.rb +2 -1
- data/lib/paradocs/struct.rb +1 -0
- data/lib/paradocs/version.rb +1 -1
- data/mkdocs.yml +1 -0
- data/paradocs.gemspec +1 -1
- data/spec/extensions/payload_builder_spec.rb +3 -3
- data/spec/extensions/structures_spec.rb +42 -29
- data/spec/field_spec.rb +1 -1
- data/spec/schema_spec.rb +6 -6
- data/spec/struct_spec.rb +8 -8
- data/spec/subschema_spec.rb +0 -3
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba77710f31f4b6d12c2c764807c1274a7409e18f
|
4
|
+
data.tar.gz: 79e70eed1f40fbc5b2c7fab32b935ecee813b7b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2365143b9765af228b14ed88105837d34fdba6b3da914e7cf71750e6bea120d6418767b7cd94678480b97ab7735af049129c3c180a6f46d7d590630d09f0d490
|
7
|
+
data.tar.gz: eae4b0b0593f473ac51fc5214bef19c8571490cde4a63b6274a755fc81c1099c226030eaeec56eb15eb91e4263062c57d0c31bf7c50bd01038ef3371c7382031
|
data/README.md
CHANGED
@@ -37,14 +37,4 @@ form.errors # => {}
|
|
37
37
|
```
|
38
38
|
|
39
39
|
## Learn more
|
40
|
-
|
41
|
-
- [Built In Policies](https://github.com/mtkachenk0/paradocs/wiki/Policies#built-in-policies)
|
42
|
-
- [Type Policies](https://github.com/mtkachenk0/paradocs/wiki/Policies#type-coercions)
|
43
|
-
- [Presence Policies](https://github.com/mtkachenk0/paradocs/wiki/Policies#presence-policies)
|
44
|
-
- [Custom Policies](https://github.com/mtkachenk0/paradocs/wiki/Policies#custom-policies)
|
45
|
-
- [Schema](https://github.com/mtkachenk0/paradocs/wiki/schema)
|
46
|
-
- [Expanding fields dynamically](https://github.com/mtkachenk0/paradocs/wiki/schema#expanding-fields-dynamically)
|
47
|
-
- [Multiple schema definitions](https://github.com/mtkachenk0/paradocs/wiki/schema#multiple-schema-definitions)
|
48
|
-
- [Documentation Generation](https://github.com/mtkachenk0/paradocs/wiki/Documentation-Generation)
|
49
|
-
- [What if my fields are conditional?!](https://github.com/mtkachenk0/paradocs/wiki/subschema)
|
50
|
-
- [For those who need more: RTFM](https://github.com/mtkachenk0/paradocs/wiki)
|
40
|
+
Please read the [documentation](https://paradocs.readthedocs.io/en/latest)
|
data/docs/changelog.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 1.1.2
|
4
|
+
- Regenerate structures each time without saving them into instance attributes.
|
5
|
+
|
6
|
+
## 1.1.1
|
7
|
+
- Fixed bug with missing `errors` meta key in `Structure#all_nested` and `Structure#all_flatten` methods.
|
8
|
+
- Fixed bug with absent `nested_name` meta key in `#Structure#all_nested` method.
|
9
|
+
- Added opportunity to sort generated by `PayloadBuilder` payload in the way it is described in schema.
|
10
|
+
|
11
|
+
## 1.1.0
|
12
|
+
> `Schema#structure` is not comptatible with previous versions
|
13
|
+
|
14
|
+
- Added `Paradocs::Extensions::StructureBuilder`. See [more](payload_builder)
|
15
|
+
- `Parardocs::Extensions::Structure` has replaced `Paradocs::Extensions::Insides` and changed `Schema#structure` behavior
|
16
|
+
- `Paradocs::Extensions::Structure` got more structure generation methods. See [Documentation Generation](documentation_generation)
|
17
|
+
|
@@ -188,12 +188,16 @@ all_nested[:subschema] # =>
|
|
188
188
|
required: true,
|
189
189
|
present: true,
|
190
190
|
json_path: "$.data",
|
191
|
+
nested_name: "data",
|
191
192
|
structure: {
|
192
|
-
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
193
|
-
"
|
194
|
-
"
|
195
|
-
"
|
196
|
-
"
|
193
|
+
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true, nested_name: "data.role"},
|
194
|
+
"test_field" => {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"},
|
195
|
+
"id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id", nested_name: "data.id"},
|
196
|
+
"name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name"},
|
197
|
+
"extra" => {
|
198
|
+
type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
|
199
|
+
structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}
|
200
|
+
}
|
197
201
|
}
|
198
202
|
}
|
199
203
|
}
|
@@ -205,12 +209,16 @@ all_nested[:test_subschema] # =>
|
|
205
209
|
required: true,
|
206
210
|
present: true,
|
207
211
|
json_path: "$.data",
|
212
|
+
nested_name: "data",
|
208
213
|
structure: {
|
209
|
-
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
210
|
-
"
|
211
|
-
"
|
212
|
-
"
|
213
|
-
"
|
214
|
+
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true, nested_name: "data.role"},
|
215
|
+
"test1" => {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"},
|
216
|
+
"id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id", nested_name: "data.id"},
|
217
|
+
"name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name"},
|
218
|
+
"extra" => {
|
219
|
+
type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
|
220
|
+
structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}
|
221
|
+
}
|
214
222
|
}
|
215
223
|
}
|
216
224
|
}
|
@@ -219,33 +227,38 @@ all_nested[:test_subschema] # =>
|
|
219
227
|
## Structure#all_flatten
|
220
228
|
> This method returns all available combinations of schema (built on subschema) without nesting (the same way as Structure#flatten method does)
|
221
229
|
|
222
|
-
Schema is the same as described in Structure#all_nested
|
230
|
+
Schema is the same as described in `Structure#all_nested`
|
223
231
|
```rb
|
224
232
|
schema.structure.all_flatten # =>
|
225
233
|
{
|
226
234
|
subschema: {
|
227
235
|
_errors: [],
|
228
|
-
"data" => {type: :object, required: true, present: true, json_path: "$.data"},
|
229
|
-
"data.id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id"},
|
230
|
-
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true},
|
231
|
-
"data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
232
|
-
"data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
|
233
|
-
"data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
|
234
|
-
"data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
|
236
|
+
"data" => {type: :object, required: true, present: true, json_path: "$.data", nested_name: "data"},
|
237
|
+
"data.id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id", nested_name: "data.id"},
|
238
|
+
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name"},
|
239
|
+
"data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true, nested_name: "data.role"},
|
240
|
+
"data.extra" => {type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra"},
|
241
|
+
"data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"},
|
242
|
+
"data.test_field" => {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"}
|
235
243
|
},
|
236
244
|
test_subschema: {
|
237
245
|
_errors: [],
|
238
|
-
"data" => {type: :object, required: true, present: true, json_path: "$.data"},
|
239
|
-
"data.id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id"},
|
240
|
-
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true},
|
241
|
-
"data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
242
|
-
"data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
|
243
|
-
"data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
|
244
|
-
"data.test1" => {required: true, present: true, json_path: "$.data.test1"}
|
246
|
+
"data" => {type: :object, required: true, present: true, json_path: "$.data", nested_name: "data"},
|
247
|
+
"data.id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id", nested_name: "data.id"},
|
248
|
+
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name"},
|
249
|
+
"data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true, nested_name: "data.role"},
|
250
|
+
"data.extra" => {type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra"},
|
251
|
+
"data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"},
|
252
|
+
"data.test1" => {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"}
|
245
253
|
}
|
246
254
|
}
|
247
255
|
```
|
248
256
|
|
257
|
+
## Passing a block
|
258
|
+
Given block to the methods above (`#flatten`, `#nested`, `#all_flatten`, `#all_nested`) will be executed for
|
259
|
+
each field, passing you as arguments `field.key` and `field.meta`. Mutating the second argument `field.meta`
|
260
|
+
will reflect onto returned `meta`.
|
261
|
+
|
249
262
|
## Schema#walk
|
250
263
|
|
251
264
|
The `#walk` method can recursively walk a schema definition and extract meta data or field attributes.
|
data/docs/payload_builder.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# Generate examples from the Schema
|
2
2
|
|
3
|
-
> Schema instance provides
|
3
|
+
> `Schema` instance provides `#example_payloads` method that returns example of all possible structures.
|
4
4
|
|
5
|
-
NOTE: PayloadBuilder sets nil values by default. If options are given - builder will take on of them, if default is set - builder will use it.
|
6
|
-
> PayloadBuilder#build! method takes a block as argument that may help you adding your custom rules.
|
5
|
+
NOTE: `PayloadBuilder` sets nil values by default. If options are given - builder will take on of them, if default is set - builder will use it.
|
7
6
|
|
8
7
|
#### Example schema
|
9
8
|
```ruby
|
@@ -59,12 +58,14 @@ schema.example_payloads.to_json # =>
|
|
59
58
|
```
|
60
59
|
|
61
60
|
## Customize payload generation logic
|
62
|
-
PayloadBuilder#build
|
61
|
+
`PayloadBuilder#build!` arguments:
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
-
|
67
|
-
-
|
63
|
+
1. `sort_by_schema: true` will try to return payload in the same way as declared in the schema.
|
64
|
+
2. `&block` will be executed for each key receiving the following arguments:
|
65
|
+
- `key`: Field name
|
66
|
+
- `meta`: Field meta data (that includes (if provided) field types, presence data, policies and other meta data
|
67
|
+
- `example_value`: Provided by generator example value.
|
68
|
+
- `skip_word`: Return this argument back if you want this item to be ommitted.
|
68
69
|
|
69
70
|
```rb
|
70
71
|
block = Proc.new do |key, meta, example, skip_word|
|
@@ -73,16 +73,6 @@ module Paradocs
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
Paradocs.policy :split do
|
77
|
-
coerce do |v, k, c|
|
78
|
-
v.kind_of?(Array) ? v : v.to_s.split(/\s*,\s*/)
|
79
|
-
end
|
80
|
-
|
81
|
-
meta_data do
|
82
|
-
{type: :array}
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
76
|
Paradocs.policy :datetime do
|
87
77
|
coerce do |v, k, c|
|
88
78
|
DateTime.parse(v.to_s)
|
@@ -8,8 +8,9 @@ module Paradocs
|
|
8
8
|
@skip_word = skip_word
|
9
9
|
end
|
10
10
|
|
11
|
-
def build!(&block)
|
12
|
-
structure.all_nested.map { |name, struct| [name, build_simple_structure(struct, &block)] }.to_h
|
11
|
+
def build!(sort_by_schema: false, &block)
|
12
|
+
result = structure.all_nested.map { |name, struct| [name, build_simple_structure(struct, &block)] }.to_h
|
13
|
+
sort_by_schema ? schema.resolve(result).output : result
|
13
14
|
end
|
14
15
|
|
15
16
|
private
|
@@ -20,7 +21,8 @@ module Paradocs
|
|
20
21
|
next if key.start_with?(Paradocs.config.meta_prefix) # skip all the meta fields
|
21
22
|
ex_value = restore_one(key, value, &block)
|
22
23
|
next if ex_value == @skip_word
|
23
|
-
|
24
|
+
key = value[:alias] || key
|
25
|
+
[key.to_s, ex_value]
|
24
26
|
end.compact.to_h
|
25
27
|
end
|
26
28
|
|
@@ -14,12 +14,8 @@ module Paradocs
|
|
14
14
|
@root = root
|
15
15
|
end
|
16
16
|
|
17
|
-
def flush!
|
18
|
-
@nested, @all_nested, @flatten, @all_flatten = [nil] * 4
|
19
|
-
end
|
20
|
-
|
21
17
|
def nested(&block)
|
22
|
-
|
18
|
+
schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), result|
|
23
19
|
meta, sc = collect_meta(field, root)
|
24
20
|
if sc
|
25
21
|
meta[:structure] = self.class.new(sc, ignore_transparent, meta[:json_path]).nested(&block)
|
@@ -27,8 +23,10 @@ module Paradocs
|
|
27
23
|
else
|
28
24
|
result[errors] += field.possible_errors
|
29
25
|
end
|
30
|
-
|
31
|
-
|
26
|
+
|
27
|
+
field_key = field.meta_data[:alias] || field.key
|
28
|
+
result[field_key] = meta unless ignore_transparent && field.transparent?
|
29
|
+
yield(field_key, meta) if block_given?
|
32
30
|
|
33
31
|
next unless field.mutates_schema?
|
34
32
|
schema.subschemes.each do |name, subschema|
|
@@ -39,12 +37,13 @@ module Paradocs
|
|
39
37
|
end
|
40
38
|
|
41
39
|
def all_nested(&block)
|
42
|
-
|
40
|
+
all_flatten(&block).each_with_object({}) do |(name, struct), obj|
|
43
41
|
obj[name] = {}
|
44
42
|
# sort the flatten struct to have iterated 1lvl keys before 2lvl and so on...
|
45
43
|
struct.sort_by { |k, v| k.to_s.count(".") }.each do |key, value|
|
46
44
|
target = obj[name]
|
47
45
|
key, value = key.to_s, value.clone # clone the values, because we do mutation below
|
46
|
+
value.merge!(nested_name: key) if value.respond_to?(:merge) # it can be array (_errors)
|
48
47
|
next target[key.to_sym] = value if key.start_with?(Paradocs.config.meta_prefix) # copy meta fields
|
49
48
|
|
50
49
|
parts = key.split(".")
|
@@ -60,17 +59,18 @@ module Paradocs
|
|
60
59
|
end
|
61
60
|
|
62
61
|
def all_flatten(schema_structure=nil, &block)
|
63
|
-
return @all_flatten if @all_flatten
|
64
62
|
schema_structure ||= flatten(&block)
|
65
63
|
if schema_structure[subschemes].empty?
|
66
64
|
schema_structure.delete(subschemes) # don't include redundant key
|
67
|
-
return
|
65
|
+
return {DEFAULT => schema_structure}
|
68
66
|
end
|
69
|
-
|
67
|
+
schema_structure[subschemes].each_with_object({}) do |(name, subschema), result|
|
70
68
|
if subschema[subschemes].empty?
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
result[name] = schema_structure.merge(subschema)
|
70
|
+
result[name][errors] += schema_structure[errors]
|
71
|
+
result[name][errors].uniq!
|
72
|
+
result[name].delete(subschemes)
|
73
|
+
next result[name]
|
74
74
|
end
|
75
75
|
|
76
76
|
all_flatten(subschema).each do |sub_name, schema|
|
@@ -80,7 +80,7 @@ module Paradocs
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def flatten(&block)
|
83
|
-
|
83
|
+
schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), obj|
|
84
84
|
meta, sc = collect_meta(field, root)
|
85
85
|
humanized_name = meta.delete(:nested_name)
|
86
86
|
obj[humanized_name] = meta unless ignore_transparent && field.transparent?
|
@@ -105,7 +105,8 @@ module Paradocs
|
|
105
105
|
private
|
106
106
|
|
107
107
|
def collect_meta(field, root)
|
108
|
-
|
108
|
+
field_key = field.meta_data[:alias] || field.key
|
109
|
+
json_path = root.empty? ? "$.#{field_key}" : "#{root}.#{field_key}"
|
109
110
|
meta = field.meta_data.merge(json_path: json_path)
|
110
111
|
sc = meta.delete(:schema)
|
111
112
|
meta[:mutates_schema] = true if meta.delete(:mutates_schema)
|
data/lib/paradocs/field_dsl.rb
CHANGED
@@ -32,5 +32,17 @@ module Paradocs
|
|
32
32
|
def length(opts)
|
33
33
|
policy :length, opts
|
34
34
|
end
|
35
|
+
|
36
|
+
def description(text)
|
37
|
+
meta description: text
|
38
|
+
end
|
39
|
+
|
40
|
+
def as(identifier)
|
41
|
+
meta alias: identifier
|
42
|
+
end
|
43
|
+
|
44
|
+
def example(value)
|
45
|
+
meta example: value
|
46
|
+
end
|
35
47
|
end
|
36
48
|
end
|
data/lib/paradocs/policies.rb
CHANGED
@@ -5,7 +5,7 @@ module Paradocs
|
|
5
5
|
class Format < Paradocs::BasePolicy
|
6
6
|
attr_reader :message
|
7
7
|
|
8
|
-
def initialize(fmt, msg
|
8
|
+
def initialize(fmt, msg="invalid format")
|
9
9
|
@message = msg
|
10
10
|
@fmt = fmt
|
11
11
|
end
|
@@ -18,12 +18,23 @@ module Paradocs
|
|
18
18
|
!payload.key?(key) || !!(value.to_s =~ @fmt)
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
class Split < Paradocs::BasePolicy
|
23
|
+
def initialize(delimiter=/\s*,\s*/)
|
24
|
+
@delimiter = delimiter
|
25
|
+
end
|
26
|
+
|
27
|
+
def coerce(v, *)
|
28
|
+
v.kind_of?(Array) ? v : v.to_s.split(@delimiter)
|
29
|
+
end
|
30
|
+
end
|
21
31
|
end
|
22
32
|
|
23
33
|
# Default validators
|
24
34
|
EMAIL_REGEXP = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i.freeze
|
25
35
|
|
26
36
|
Paradocs.policy :format, Policies::Format
|
37
|
+
Paradocs.policy :split, Policies::Split
|
27
38
|
Paradocs.policy :email, Policies::Format.new(EMAIL_REGEXP, 'invalid email')
|
28
39
|
|
29
40
|
Paradocs.policy :noop do
|
data/lib/paradocs/schema.rb
CHANGED
@@ -181,7 +181,8 @@ module Paradocs
|
|
181
181
|
invoke_subschemes!(val, context, flds: flds)
|
182
182
|
flds.each_with_object({}) do |(_, field), m|
|
183
183
|
r = field.resolve(val, context.sub(field.key))
|
184
|
-
|
184
|
+
key = field.meta_data[:alias] || field.key
|
185
|
+
m[key] = r.value if r.eligible?
|
185
186
|
end
|
186
187
|
end
|
187
188
|
|
data/lib/paradocs/struct.rb
CHANGED
data/lib/paradocs/version.rb
CHANGED
data/mkdocs.yml
CHANGED
data/paradocs.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Paradocs::VERSION
|
9
9
|
spec.authors = ["Maxim Tkachenko", "Ismael Celis"]
|
10
10
|
spec.email = ["tkachenko.maxim.w@gmail.com", "ismaelct@gmail.com"]
|
11
|
-
spec.description = %q{Flexible
|
11
|
+
spec.description = %q{Flexible DRY validations with API docs generation done right TLDR; parametrics on steroids.}
|
12
12
|
spec.summary = %q{A huge add-on for original gem mostly focused on retrieving the more metadata from declared schemas as possible.}
|
13
13
|
spec.homepage = "https://paradocs.readthedocs.io/en/latest"
|
14
14
|
spec.license = "MIT"
|
@@ -22,7 +22,7 @@ describe Paradocs::Extensions::PayloadBuilder do
|
|
22
22
|
end
|
23
23
|
subschema(:fooschema) { }
|
24
24
|
subschema(:barschema) do
|
25
|
-
field(:barfield).present.type(:boolean)
|
25
|
+
field(:barfield).present.type(:boolean).as(:bar_field)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -32,7 +32,7 @@ describe Paradocs::Extensions::PayloadBuilder do
|
|
32
32
|
allow_any_instance_of(Array).to receive(:sample) { "bar" }
|
33
33
|
payloads = described_class.new(schema).build!
|
34
34
|
expect(payloads.keys.sort).to eq([:barschema, :fooschema, :subschema1, :subschema2_deep_schema, :subschema2_empty])
|
35
|
-
expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => "bar", "
|
35
|
+
expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => "bar", "bar_field" => nil}})
|
36
36
|
expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => "bar"}})
|
37
37
|
expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => "bar"}, "subtest1" => nil})
|
38
38
|
expect(payloads[:subschema2_deep_schema]).to eq({
|
@@ -57,7 +57,7 @@ describe Paradocs::Extensions::PayloadBuilder do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
expect(payloads.keys.sort).to eq([:barschema, :fooschema, :subschema1, :subschema2_deep_schema, :subschema2_empty])
|
60
|
-
expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => nil, "
|
60
|
+
expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => nil, "bar_field" => true}}) # barfield is change to true and bar is nil
|
61
61
|
expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # bar is nil
|
62
62
|
expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # subtest is missing, bar is nil
|
63
63
|
expect(payloads[:subschema2_deep_schema]).to eq({
|
@@ -18,8 +18,8 @@ describe Paradocs::Extensions::Structure do
|
|
18
18
|
subschema(:highest_level) { field(:test).present } # no mutations on this level -> subschema ignored
|
19
19
|
|
20
20
|
field(:data).type(:object).present.schema do
|
21
|
-
field(:id).type(:integer).present.policy(:policy_with_error)
|
22
|
-
field(:name).type(:string).meta(label: "very important staff")
|
21
|
+
field(:id).type(:integer).present.policy(:policy_with_error).as(:user_id)
|
22
|
+
field(:name).type(:string).meta(label: "very important staff").description("Example description").example("John")
|
23
23
|
field(:role).type(:string).declared.options(["admin", "user"]).default("user").mutates_schema! do |*|
|
24
24
|
:test_subschema
|
25
25
|
end
|
@@ -64,17 +64,20 @@ describe Paradocs::Extensions::Structure do
|
|
64
64
|
test_field: {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"}
|
65
65
|
}
|
66
66
|
})
|
67
|
-
expect(data_structure[:
|
67
|
+
expect(data_structure[:user_id]).to eq({
|
68
68
|
type: :integer,
|
69
69
|
required: true,
|
70
70
|
present: true,
|
71
71
|
policy_with_error: {errors: [ArgumentError]},
|
72
|
-
|
73
|
-
|
72
|
+
alias: :user_id,
|
73
|
+
json_path: "$.data.user_id",
|
74
|
+
nested_name: "data.user_id"
|
74
75
|
})
|
75
76
|
expect(data_structure[:name]).to eq({
|
76
77
|
type: :string,
|
77
78
|
label: "very important staff",
|
79
|
+
description: "Example description",
|
80
|
+
example: "John",
|
78
81
|
mutates_schema: true,
|
79
82
|
block_works: true,
|
80
83
|
json_path: "$.data.name",
|
@@ -129,17 +132,20 @@ describe Paradocs::Extensions::Structure do
|
|
129
132
|
json_path: "$.data.extra[].extra",
|
130
133
|
policy_with_silent_error: {errors: []}
|
131
134
|
},
|
132
|
-
"data.
|
135
|
+
"data.user_id" => {
|
133
136
|
type: :integer,
|
134
137
|
required: true,
|
135
138
|
present: true,
|
136
|
-
|
139
|
+
alias: :user_id,
|
140
|
+
json_path: "$.data.user_id",
|
137
141
|
policy_with_error: {errors: [ArgumentError]}
|
138
142
|
},
|
139
143
|
"data.name" => {
|
140
144
|
type: :string,
|
141
145
|
json_path: "$.data.name",
|
142
146
|
label: "very important staff",
|
147
|
+
description: "Example description",
|
148
|
+
example: "John",
|
143
149
|
mutates_schema: true
|
144
150
|
},
|
145
151
|
"data.role" => {
|
@@ -174,20 +180,20 @@ describe Paradocs::Extensions::Structure do
|
|
174
180
|
it "generates N structures, where N = number of unique combinations of applied subschemas" do
|
175
181
|
expect(schema.structure.all_flatten).to eq({
|
176
182
|
subschema: {
|
177
|
-
_errors: [],
|
183
|
+
_errors: [ArgumentError],
|
178
184
|
"data" => {type: :object, required: true, present: true, json_path: "$.data"},
|
179
|
-
"data.
|
180
|
-
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true},
|
185
|
+
"data.user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id"},
|
186
|
+
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"},
|
181
187
|
"data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
182
188
|
"data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
|
183
189
|
"data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
|
184
190
|
"data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
|
185
191
|
},
|
186
192
|
test_subschema: {
|
187
|
-
_errors: [],
|
193
|
+
_errors: [ArgumentError],
|
188
194
|
"data" => {type: :object, required: true, present: true, json_path: "$.data"},
|
189
|
-
"data.
|
190
|
-
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true},
|
195
|
+
"data.user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id"},
|
196
|
+
"data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"},
|
191
197
|
"data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
192
198
|
"data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
|
193
199
|
"data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
|
@@ -201,34 +207,41 @@ describe Paradocs::Extensions::Structure do
|
|
201
207
|
it "generates N structures, where N = number of unique combinations of applied subschemas" do
|
202
208
|
result = schema.structure.all_nested
|
203
209
|
expect(result[:subschema]).to eq({
|
204
|
-
_errors: [],
|
210
|
+
_errors: [ArgumentError],
|
205
211
|
"data" => {
|
206
|
-
type:
|
207
|
-
required:
|
208
|
-
present:
|
209
|
-
json_path:
|
212
|
+
type: :object,
|
213
|
+
required: true,
|
214
|
+
present: true,
|
215
|
+
json_path: "$.data",
|
216
|
+
nested_name: "data",
|
210
217
|
structure: {
|
211
|
-
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
|
212
|
-
"
|
213
|
-
"
|
214
|
-
"
|
215
|
-
"
|
218
|
+
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true},
|
219
|
+
"test_field" => {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"},
|
220
|
+
"user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id", nested_name: "data.user_id"},
|
221
|
+
"name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"},
|
222
|
+
"extra" => {
|
223
|
+
type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
|
224
|
+
structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}
|
225
|
+
}
|
216
226
|
}
|
217
227
|
}
|
218
228
|
})
|
219
229
|
expect(result[:test_subschema]).to eq({
|
220
|
-
_errors: [],
|
230
|
+
_errors: [ArgumentError],
|
221
231
|
"data" => {
|
222
232
|
type: :object,
|
223
233
|
required: true,
|
224
234
|
present: true,
|
225
235
|
json_path: "$.data",
|
236
|
+
nested_name: "data",
|
226
237
|
structure: {
|
227
|
-
"role"
|
228
|
-
"
|
229
|
-
"
|
230
|
-
"
|
231
|
-
"
|
238
|
+
"role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true},
|
239
|
+
"test1" => {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"},
|
240
|
+
"user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id", nested_name: "data.user_id"},
|
241
|
+
"name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"},
|
242
|
+
"extra" => {
|
243
|
+
type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
|
244
|
+
structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}}
|
232
245
|
}
|
233
246
|
}
|
234
247
|
})
|
data/spec/field_spec.rb
CHANGED
data/spec/schema_spec.rb
CHANGED
@@ -16,7 +16,7 @@ describe Paradocs::Schema do
|
|
16
16
|
|
17
17
|
subject do
|
18
18
|
described_class.new do
|
19
|
-
field(:title).policy(:string).present
|
19
|
+
field(:title).policy(:string).present.as(:article_title)
|
20
20
|
field(:price).policy(:integer).meta(label: "A price")
|
21
21
|
field(:status).policy(:string).options(['visible', 'hidden'])
|
22
22
|
field(:tags).policy(:split).policy(:array)
|
@@ -33,8 +33,8 @@ describe Paradocs::Schema do
|
|
33
33
|
describe "#structure" do
|
34
34
|
it "represents data structure and meta data" do
|
35
35
|
sc = subject.structure.nested
|
36
|
-
expect(sc[:
|
37
|
-
expect(sc[:
|
36
|
+
expect(sc[:article_title][:present]).to be true
|
37
|
+
expect(sc[:article_title][:type]).to eq :string
|
38
38
|
expect(sc[:price][:type]).to eq :integer
|
39
39
|
expect(sc[:price][:label]).to eq "A price"
|
40
40
|
expect(sc[:variants][:type]).to eq :array
|
@@ -76,7 +76,7 @@ describe Paradocs::Schema do
|
|
76
76
|
|
77
77
|
output = subject.resolve(payload).output
|
78
78
|
expect(output).to eq({
|
79
|
-
|
79
|
+
article_title: "title",
|
80
80
|
price: 100,
|
81
81
|
status: "visible",
|
82
82
|
tags: ["tag"],
|
@@ -101,7 +101,7 @@ describe Paradocs::Schema do
|
|
101
101
|
variants: [{name: 'v1', sku: 'ABC', stock: '10', available_if_no_stock: true}]
|
102
102
|
},
|
103
103
|
{
|
104
|
-
|
104
|
+
article_title: 'iPhone 6 Plus',
|
105
105
|
price: 100,
|
106
106
|
status: 'visible',
|
107
107
|
tags: ['tag1', 'tag2'],
|
@@ -114,7 +114,7 @@ describe Paradocs::Schema do
|
|
114
114
|
variants: [{name: 'v1', available_if_no_stock: '1'}]
|
115
115
|
},
|
116
116
|
{
|
117
|
-
|
117
|
+
article_title: 'iPhone 6 Plus',
|
118
118
|
variants: [{name: 'v1', stock: 1, available_if_no_stock: true}]
|
119
119
|
})
|
120
120
|
|
data/spec/struct_spec.rb
CHANGED
@@ -16,13 +16,13 @@ describe Paradocs::Struct do
|
|
16
16
|
include Paradocs::Struct
|
17
17
|
|
18
18
|
schema do
|
19
|
-
field(:title).type(:string).present
|
19
|
+
field(:title).type(:string).present.as(:example_title)
|
20
20
|
field(:friends).type(:array).default([]).schema friend_class
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
new_instance = klass.new
|
25
|
-
expect(new_instance.
|
25
|
+
expect(new_instance.example_title).to eq ''
|
26
26
|
expect(new_instance.friends).to eq []
|
27
27
|
expect(new_instance.valid?).to be false
|
28
28
|
expect(new_instance.errors['$.title']).not_to be_nil
|
@@ -35,7 +35,7 @@ describe Paradocs::Struct do
|
|
35
35
|
]
|
36
36
|
})
|
37
37
|
|
38
|
-
expect(instance.
|
38
|
+
expect(instance.example_title).to eq 'foo'
|
39
39
|
expect(instance.friends.size).to eq 2
|
40
40
|
expect(instance.friends.first.name).to eq 'Ismael'
|
41
41
|
expect(instance.friends.first).to be_a friend_class
|
@@ -154,7 +154,7 @@ describe Paradocs::Struct do
|
|
154
154
|
schema do
|
155
155
|
field(:title).type(:string).present
|
156
156
|
field(:friends).type(:array).schema do
|
157
|
-
field(:name).type(:string)
|
157
|
+
field(:name).type(:string).as(:person_name)
|
158
158
|
field(:age).type(:integer).default(20)
|
159
159
|
end
|
160
160
|
end
|
@@ -171,8 +171,8 @@ describe Paradocs::Struct do
|
|
171
171
|
expect(instance.to_h).to eq({
|
172
172
|
title: 'foo',
|
173
173
|
friends: [
|
174
|
-
{
|
175
|
-
{
|
174
|
+
{person_name: 'Jane', age: 20},
|
175
|
+
{person_name: 'Joe', age: 39},
|
176
176
|
]
|
177
177
|
})
|
178
178
|
|
@@ -190,7 +190,7 @@ describe Paradocs::Struct do
|
|
190
190
|
include Paradocs::Struct
|
191
191
|
|
192
192
|
schema do
|
193
|
-
field(:title).type(:string).present
|
193
|
+
field(:title).type(:string).present.as(:example_title)
|
194
194
|
field(:friends).type(:array).schema do
|
195
195
|
field(:name).type(:string)
|
196
196
|
field(:age).type(:integer).default(20)
|
@@ -213,7 +213,7 @@ describe Paradocs::Struct do
|
|
213
213
|
]
|
214
214
|
)
|
215
215
|
|
216
|
-
expect(instance.
|
216
|
+
expect(instance.example_title).to eq 'foo'
|
217
217
|
expect(instance.email).to eq 'email@me.com'
|
218
218
|
expect(instance.friends.size).to eq 2
|
219
219
|
end
|
data/spec/subschema_spec.rb
CHANGED
@@ -72,7 +72,6 @@ describe "schemes with subschemes" do
|
|
72
72
|
expect(result.errors).to eq({"$.fail_field"=>["is required"]})
|
73
73
|
expect(result.output).to eq({error: :here, fail_field: nil})
|
74
74
|
expect(schema.structure.nested).to eq(structure)
|
75
|
-
schema.structure.flush!
|
76
75
|
expect(schema.structure(ignore_transparent: false).nested).to eq(structure.merge(
|
77
76
|
error: {transparent: true, mutates_schema: true, json_path: "$.error", nested_name: "error"}
|
78
77
|
))
|
@@ -80,9 +79,7 @@ describe "schemes with subschemes" do
|
|
80
79
|
result = schema.resolve({})
|
81
80
|
expect(result.errors).to eq({"$.success_field"=>["is required"]})
|
82
81
|
expect(result.output).to eq({success_field: nil})
|
83
|
-
schema.structure.flush!
|
84
82
|
expect(schema.structure.nested).to eq(structure)
|
85
|
-
schema.structure.flush!
|
86
83
|
expect(schema.structure(ignore_transparent: false).nested).to eq(structure.merge(
|
87
84
|
error: {transparent: true, mutates_schema: true, json_path: "$.error", nested_name: "error"}
|
88
85
|
))
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paradocs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maxim Tkachenko
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-
|
12
|
+
date: 2020-11-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -67,8 +67,8 @@ dependencies:
|
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
|
-
description: Flexible
|
71
|
-
|
70
|
+
description: Flexible DRY validations with API docs generation done right TLDR; parametrics
|
71
|
+
on steroids.
|
72
72
|
email:
|
73
73
|
- tkachenko.maxim.w@gmail.com
|
74
74
|
- ismaelct@gmail.com
|
@@ -88,6 +88,7 @@ files:
|
|
88
88
|
- README.md
|
89
89
|
- Rakefile
|
90
90
|
- bin/console
|
91
|
+
- docs/changelog.md
|
91
92
|
- docs/custom_configuration.md
|
92
93
|
- docs/documentation_generation.md
|
93
94
|
- docs/faq.md
|