paradocs 1.1.0 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|