paradocs 1.0.24 → 1.1.4

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.
@@ -0,0 +1,45 @@
1
+ module Paradocs
2
+ module Extensions
3
+ class PayloadBuilder
4
+ attr_reader :structure, :result
5
+ attr_accessor :skip_word
6
+ def initialize(schema, skip_word: :skip)
7
+ @structure = schema.structure
8
+ @skip_word = skip_word
9
+ end
10
+
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
14
+ end
15
+
16
+ private
17
+
18
+ def build_simple_structure(struct, &block)
19
+ struct.map do |key, value|
20
+ key = key.to_s
21
+ next if key.start_with?(Paradocs.config.meta_prefix) # skip all the meta fields
22
+ ex_value = restore_one(key, value, &block)
23
+ next if ex_value == @skip_word
24
+ key = value[:alias] || key
25
+ [key.to_s, ex_value]
26
+ end.compact.to_h
27
+ end
28
+
29
+ def restore_one(key, value, &block)
30
+ default = value[:default]
31
+ ex_value = if value[:structure]
32
+ data = build_simple_structure(value[:structure], &block)
33
+ value[:type] == :array ? [data] : data
34
+ elsif default
35
+ default.is_a?(Proc) ? default.call : default
36
+ elsif value[:options] && !value[:options].empty?
37
+ options = value[:options]
38
+ value[:type] == :array ? options : options.sample
39
+ end
40
+ return ex_value unless block_given?
41
+ yield(key, value, ex_value, @skip_word)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,119 @@
1
+ module Paradocs
2
+ module Extensions
3
+ class Structure
4
+ DEFAULT = :generic
5
+ %w(errors subschemes).each do |key|
6
+ define_method(key) { "#{Paradocs.config.meta_prefix}#{key}".to_sym }
7
+ end
8
+
9
+ attr_reader :schema, :ignore_transparent, :root
10
+ attr_accessor :ignore_transparent
11
+ def initialize(schema, ignore_transparent=true, root="")
12
+ @schema = schema
13
+ @ignore_transparent = ignore_transparent
14
+ @root = root
15
+ end
16
+
17
+ def nested(&block)
18
+ schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), result|
19
+ meta, sc = collect_meta(field, root)
20
+ if sc
21
+ meta[:structure] = self.class.new(sc, ignore_transparent, meta[:json_path]).nested(&block)
22
+ result[errors] += meta[:structure].delete(errors)
23
+ else
24
+ result[errors] += field.possible_errors
25
+ end
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?
30
+
31
+ next unless field.mutates_schema?
32
+ schema.subschemes.each do |name, subschema|
33
+ result[subschemes][name] = self.class.new(subschema, ignore_transparent, root).nested(&block)
34
+ result[errors] += result[subschemes][name][errors]
35
+ end
36
+ end
37
+ end
38
+
39
+ def all_nested(&block)
40
+ all_flatten(&block).each_with_object({}) do |(name, struct), obj|
41
+ obj[name] = {}
42
+ # sort the flatten struct to have iterated 1lvl keys before 2lvl and so on...
43
+ struct.sort_by { |k, v| k.to_s.count(".") }.each do |key, value|
44
+ target = obj[name]
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)
47
+ next target[key.to_sym] = value if key.start_with?(Paradocs.config.meta_prefix) # copy meta fields
48
+
49
+ parts = key.split(".")
50
+ next target[key] ||= value if parts.size == 1 # copy 1lvl key
51
+ parts.each.with_index do |subkey, index|
52
+ target[subkey] ||= value
53
+ next if parts.size == index + 1
54
+ target[subkey][:structure] ||= {}
55
+ target = target[subkey][:structure] # target goes deeper for each part
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def all_flatten(schema_structure=nil, &block)
62
+ schema_structure ||= flatten(&block)
63
+ if schema_structure[subschemes].empty?
64
+ schema_structure.delete(subschemes) # don't include redundant key
65
+ return {DEFAULT => schema_structure}
66
+ end
67
+ schema_structure[subschemes].each_with_object({}) do |(name, subschema), result|
68
+ if subschema[subschemes].empty?
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
+ end
75
+
76
+ all_flatten(subschema).each do |sub_name, schema|
77
+ result["#{name}_#{sub_name}".to_sym] = schema_structure.merge(schema)
78
+ end
79
+ end
80
+ end
81
+
82
+ def flatten(&block)
83
+ schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), obj|
84
+ meta, sc = collect_meta(field, root)
85
+ humanized_name = meta.delete(:nested_name)
86
+ obj[humanized_name] = meta unless ignore_transparent && field.transparent?
87
+
88
+ if sc
89
+ deep_result = self.class.new(sc, ignore_transparent, meta[:json_path]).flatten(&block)
90
+ obj[errors] += deep_result.delete(errors)
91
+ obj[subschemes].merge!(deep_result.delete(subschemes))
92
+ obj.merge!(deep_result)
93
+ else
94
+ obj[errors] += field.possible_errors
95
+ end
96
+ yield(humanized_name, meta) if block_given?
97
+ next unless field.mutates_schema?
98
+ schema.subschemes.each do |name, subschema|
99
+ obj[subschemes][name] ||= self.class.new(subschema, ignore_transparent, root).flatten(&block)
100
+ obj[errors] += obj[subschemes][name][errors]
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def collect_meta(field, root)
108
+ field_key = field.meta_data[:alias] || field.key
109
+ json_path = root.empty? ? "$.#{field_key}" : "#{root}.#{field_key}"
110
+ meta = field.meta_data.merge(json_path: json_path)
111
+ sc = meta.delete(:schema)
112
+ meta[:mutates_schema] = true if meta.delete(:mutates_schema)
113
+ json_path << "[]" if meta[:type] == :array
114
+ meta[:nested_name] = json_path.gsub("[]", "")[2..-1]
115
+ [meta, sc]
116
+ end
117
+ end
118
+ end
119
+ end
@@ -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
@@ -1,14 +1,13 @@
1
1
  require "paradocs/context"
2
2
  require "paradocs/results"
3
3
  require "paradocs/field"
4
- require "paradocs/extensions/insides"
4
+ require "paradocs/extensions/structure"
5
+ require "paradocs/extensions/payload_builder"
5
6
 
6
7
  module Paradocs
7
8
  class Schema
8
- include Extensions::Insides
9
-
10
9
  attr_accessor :environment
11
- attr_reader :subschemes
10
+ attr_reader :subschemes, :structure_builder
12
11
  def initialize(options={}, &block)
13
12
  @options = options
14
13
  @fields = {}
@@ -18,6 +17,7 @@ module Paradocs
18
17
  @default_field_policies = []
19
18
  @ignored_field_keys = []
20
19
  @expansions = {}
20
+ @structure_builder = Paradocs::Extensions::Structure.new(self)
21
21
  end
22
22
 
23
23
  def schema
@@ -29,6 +29,27 @@ module Paradocs
29
29
  f.mutates_schema!(&block)
30
30
  end
31
31
 
32
+ def structure(ignore_transparent: true)
33
+ flush!
34
+ structure_builder.ignore_transparent = ignore_transparent
35
+ structure_builder
36
+ end
37
+
38
+ def example_payloads(&block)
39
+ @example_payloads ||= Paradocs::Extensions::PayloadBuilder.new(self).build!(&block)
40
+ end
41
+
42
+ def walk(meta_key = nil, &visitor)
43
+ r = visit(meta_key, &visitor)
44
+ Results.new(r, {}, {})
45
+ end
46
+
47
+ def visit(meta_key = nil, &visitor)
48
+ fields.each_with_object({}) do |(_, field), m|
49
+ m[field.key] = field.visit(meta_key, &visitor)
50
+ end
51
+ end
52
+
32
53
  def subschema(*args, &block)
33
54
  options = args.last.is_a?(Hash) ? args.last : {}
34
55
  name = args.first.is_a?(Symbol) ? args.shift : Paradocs.config.default_schema_name
@@ -143,6 +164,11 @@ module Paradocs
143
164
  end
144
165
  end
145
166
 
167
+ def flush!
168
+ @fields = {}
169
+ @applied = false
170
+ end
171
+
146
172
  protected
147
173
 
148
174
  attr_reader :definitions, :options
@@ -155,7 +181,8 @@ module Paradocs
155
181
  invoke_subschemes!(val, context, flds: flds)
156
182
  flds.each_with_object({}) do |(_, field), m|
157
183
  r = field.resolve(val, context.sub(field.key))
158
- m[field.key] = r.value if r.eligible?
184
+ key = field.meta_data[:alias] || field.key
185
+ m[key] = r.value if r.eligible?
159
186
  end
160
187
  end
161
188
 
@@ -205,10 +232,5 @@ module Paradocs
205
232
  end
206
233
  @applied = true
207
234
  end
208
-
209
- def flush!
210
- @fields = {}
211
- @applied = false
212
- end
213
235
  end
214
236
  end
@@ -57,6 +57,7 @@ module Paradocs
57
57
  # this hook is called after schema definition in DSL module
58
58
  def paradocs_after_define_schema(schema)
59
59
  schema.fields.keys.each do |key|
60
+ key = schema.fields[key].meta_data[:alias] || key
60
61
  define_method key do
61
62
  _graph[key]
62
63
  end
@@ -1,3 +1,3 @@
1
1
  module Paradocs
2
- VERSION = "1.0.24"
2
+ VERSION = "1.1.4"
3
3
  end
@@ -0,0 +1,17 @@
1
+ site_name: Paradocs
2
+ theme:
3
+ name: readthedocs
4
+ nav_style: dark
5
+ nav:
6
+ - 'index.md'
7
+ - 'policies.md'
8
+ - 'schema.md'
9
+ - 'struct.md'
10
+ - 'form_objects_dsl.md'
11
+ - 'subschema.md'
12
+ - 'documentation_generation.md'
13
+ - 'payload_builder.md'
14
+ - 'custom_configuration.md'
15
+ - 'faq.md'
16
+ - 'changelog.md'
17
+
@@ -8,9 +8,9 @@ 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 DSL for declaring allowed parameters focused on DRY validation that gives you opportunity to generate API documentation on-the-fly.}
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
- spec.homepage = "https://github.com/mtkachenk0/paradocs"
13
+ spec.homepage = "https://paradocs.readthedocs.io/en/latest"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 2.1"
22
- spec.add_development_dependency "rake", ">= 12.3.3"
22
+ spec.add_development_dependency "rake", '~> 12.3'
23
23
  spec.add_development_dependency "rspec", '3.4.0'
24
24
  spec.add_development_dependency "pry", "~> 0"
25
25
  end
@@ -0,0 +1 @@
1
+ mkdocs==1.1.2
@@ -0,0 +1,70 @@
1
+ require "spec_helper"
2
+
3
+ describe Paradocs::Extensions::PayloadBuilder do
4
+ let(:schema) do
5
+ Paradocs::Schema.new do
6
+ field(:test).present.type(:string).mutates_schema! { :subschema1 }
7
+ subschema(:subschema1) do
8
+ field(:subtest1).declared.type(:number)
9
+ end
10
+ subschema(:subschema2) do
11
+ field(:subtest2).required.type(:array).schema do
12
+ field(:hello).type(:string).mutates_schema! { |*| :deep_schema }
13
+ subschema(:deep_schema) { field(:deep_field).type(:boolean) }
14
+ subschema(:empty) { }
15
+ end
16
+ end
17
+ # 2 mutation fields and more than 1 subschema pack work good in validation but docs
18
+ # will contain only 1 subschema at once: foo subschemes will never be mixed with test subschemes
19
+ field(:foo).required.type(:object).schema do
20
+ field(:bar).present.type(:string).options(["foo", "bar"]).mutates_schema! do |value, *|
21
+ value == "foo" ? :fooschema : :barschema
22
+ end
23
+ subschema(:fooschema) { }
24
+ subschema(:barschema) do
25
+ field(:barfield).present.type(:boolean).as(:bar_field)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ it "gives an example payload and takes into account the subschemes" do
32
+ allow_any_instance_of(Array).to receive(:sample) { "bar" }
33
+ payloads = described_class.new(schema).build!
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", "bar_field" => nil}})
36
+ expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => "bar"}})
37
+ expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => "bar"}, "subtest1" => nil})
38
+ expect(payloads[:subschema2_deep_schema]).to eq({
39
+ "subtest2" => [{"deep_field" => nil, "hello" => nil}], "test" => nil, "foo" => {"bar" => "bar"}
40
+ })
41
+ expect(payloads[:subschema2_empty]).to eq({
42
+ "test" => nil, "foo" => {"bar" => "bar"}, "subtest2" => [{"hello" => nil}]
43
+ })
44
+ end
45
+
46
+ it "yields a usefull block that changes the result" do
47
+ payloads = described_class.new(schema).build! do |key, meta, example, skip_word|
48
+ if key == "bar"
49
+ nil
50
+ elsif meta[:type] == :boolean
51
+ true
52
+ elsif key == "subtest1"
53
+ skip_word # this key value pair will be ommited
54
+ else
55
+ example # return suggested value
56
+ end
57
+ end
58
+
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, "bar_field" => true}}) # barfield is change to true and bar is nil
61
+ expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # bar is nil
62
+ expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # subtest is missing, bar is nil
63
+ expect(payloads[:subschema2_deep_schema]).to eq({
64
+ "subtest2" => [{"deep_field" => true, "hello" => nil}], "test" => nil, "foo" => {"bar" => nil}
65
+ })
66
+ expect(payloads[:subschema2_empty]).to eq({
67
+ "test" => nil, "foo" => {"bar" => nil}, "subtest2" => [{"hello" => nil}]
68
+ })
69
+ end
70
+ end
@@ -0,0 +1,250 @@
1
+ require 'spec_helper'
2
+
3
+ describe Paradocs::Extensions::Structure do
4
+ Paradocs.policy :policy_with_error do
5
+ register_error ArgumentError
6
+
7
+ validate do |*|
8
+ raise ArgumentError
9
+ end
10
+ end
11
+
12
+ Paradocs.policy :policy_with_silent_error do
13
+ register_silent_error RuntimeError
14
+ end
15
+
16
+ let(:schema) do
17
+ Paradocs::Schema.new do
18
+ subschema(:highest_level) { field(:test).present } # no mutations on this level -> subschema ignored
19
+
20
+ field(:data).type(:object).present.schema do
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
+ field(:role).type(:string).declared.options(["admin", "user"]).default("user").mutates_schema! do |*|
24
+ :test_subschema
25
+ end
26
+ field(:extra).type(:array).required.schema do
27
+ field(:extra).declared.default(false).policy(:policy_with_silent_error)
28
+ end
29
+
30
+ mutation_by!(:name) { :subschema }
31
+
32
+ subschema(:subschema) do
33
+ field(:test_field).present
34
+ end
35
+ subschema(:test_subschema) do
36
+ field(:test1).present
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "#nested" do
43
+ it "generates nested data for documentation generation" do
44
+ result = schema.structure.nested { |k, meta| meta[:block_works] = true unless meta[:present] }
45
+ expect(result[:_subschemes]).to eq({})
46
+ expect(result[:_errors]).to eq([ArgumentError])
47
+ data_structure = result[:data].delete(:structure)
48
+ expect(result[:data]).to eq({
49
+ type: :object,
50
+ required: true,
51
+ present: true,
52
+ json_path: "$.data",
53
+ nested_name: "data",
54
+ })
55
+ expect(data_structure[:_subschemes]).to eq({
56
+ test_subschema: {
57
+ _errors: [],
58
+ _subschemes: {},
59
+ test1: {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"}
60
+ },
61
+ subschema: {
62
+ _errors: [],
63
+ _subschemes: {},
64
+ test_field: {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"}
65
+ }
66
+ })
67
+ expect(data_structure[:user_id]).to eq({
68
+ type: :integer,
69
+ required: true,
70
+ present: true,
71
+ policy_with_error: {errors: [ArgumentError]},
72
+ alias: :user_id,
73
+ json_path: "$.data.user_id",
74
+ nested_name: "data.user_id"
75
+ })
76
+ expect(data_structure[:name]).to eq({
77
+ type: :string,
78
+ label: "very important staff",
79
+ description: "Example description",
80
+ example: "John",
81
+ mutates_schema: true,
82
+ block_works: true,
83
+ json_path: "$.data.name",
84
+ nested_name: "data.name"
85
+ })
86
+ expect(data_structure[:role]).to eq({
87
+ type: :string,
88
+ options: ["admin", "user"],
89
+ default: "user",
90
+ mutates_schema: true,
91
+ block_works: true,
92
+ json_path: "$.data.role",
93
+ nested_name: "data.role"
94
+ })
95
+ expect(data_structure[:extra]).to eq({
96
+ type: :array,
97
+ required: true,
98
+ block_works: true,
99
+ json_path: "$.data.extra[]",
100
+ nested_name: "data.extra",
101
+ structure: {
102
+ extra: {
103
+ default: false,
104
+ block_works: true,
105
+ json_path: "$.data.extra[].extra",
106
+ nested_name: "data.extra.extra",
107
+ policy_with_silent_error: {errors: []}
108
+ },
109
+ _subschemes: {}
110
+ }
111
+ })
112
+ end
113
+ end
114
+
115
+ describe "#flatten" do
116
+ it "generates flatten data for documentation generation" do
117
+ expect(schema.structure.flatten { |key, meta| meta[:block_works] = true if key.split(".").size == 1 }).to eq({
118
+ "data" => {
119
+ type: :object,
120
+ required: true,
121
+ present: true,
122
+ block_works: true,
123
+ json_path: "$.data"
124
+ },
125
+ "data.extra" => {
126
+ type: :array,
127
+ required: true,
128
+ json_path: "$.data.extra[]"
129
+ },
130
+ "data.extra.extra" => {
131
+ default: false,
132
+ json_path: "$.data.extra[].extra",
133
+ policy_with_silent_error: {errors: []}
134
+ },
135
+ "data.user_id" => {
136
+ type: :integer,
137
+ required: true,
138
+ present: true,
139
+ alias: :user_id,
140
+ json_path: "$.data.user_id",
141
+ policy_with_error: {errors: [ArgumentError]}
142
+ },
143
+ "data.name" => {
144
+ type: :string,
145
+ json_path: "$.data.name",
146
+ label: "very important staff",
147
+ description: "Example description",
148
+ example: "John",
149
+ mutates_schema: true
150
+ },
151
+ "data.role" => {
152
+ type: :string,
153
+ options: ["admin", "user"],
154
+ default: "user",
155
+ json_path: "$.data.role",
156
+ mutates_schema: true
157
+ },
158
+ _errors: [ArgumentError],
159
+ _subschemes: {
160
+ test_subschema: {
161
+ _errors: [],
162
+ _subschemes: {},
163
+ "data.test1"=>{
164
+ required: true,
165
+ present: true,
166
+ json_path: "$.data.test1"
167
+ }
168
+ },
169
+ subschema: {
170
+ _errors: [],
171
+ _subschemes: {},
172
+ "data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
173
+ }
174
+ }
175
+ })
176
+ end
177
+ end
178
+
179
+ describe "#all_flatten" do
180
+ it "generates N structures, where N = number of unique combinations of applied subschemas" do
181
+ expect(schema.structure.all_flatten).to eq({
182
+ subschema: {
183
+ _errors: [ArgumentError],
184
+ "data" => {type: :object, required: true, present: true, json_path: "$.data"},
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"},
187
+ "data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
188
+ "data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
189
+ "data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
190
+ "data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
191
+ },
192
+ test_subschema: {
193
+ _errors: [ArgumentError],
194
+ "data" => {type: :object, required: true, present: true, json_path: "$.data"},
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"},
197
+ "data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
198
+ "data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
199
+ "data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
200
+ "data.test1" => {required: true, present: true, json_path: "$.data.test1"}
201
+ }
202
+ })
203
+ end
204
+ end
205
+
206
+ describe "#all_nested" do
207
+ it "generates N structures, where N = number of unique combinations of applied subschemas" do
208
+ result = schema.structure.all_nested
209
+ expect(result[:subschema]).to eq({
210
+ _errors: [ArgumentError],
211
+ "data" => {
212
+ type: :object,
213
+ required: true,
214
+ present: true,
215
+ json_path: "$.data",
216
+ nested_name: "data",
217
+ structure: {
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
+ }
226
+ }
227
+ }
228
+ })
229
+ expect(result[:test_subschema]).to eq({
230
+ _errors: [ArgumentError],
231
+ "data" => {
232
+ type: :object,
233
+ required: true,
234
+ present: true,
235
+ json_path: "$.data",
236
+ nested_name: "data",
237
+ structure: {
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"}}}
245
+ }
246
+ }
247
+ })
248
+ end
249
+ end
250
+ end