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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b3362dbf83838f9f786222294f5438ed7d24bfc
4
- data.tar.gz: 3ac665829a9993b65f21a15d95b71e874e1029db
3
+ metadata.gz: c9ed12715c0df054876f622c3dada9cc4a2d8fd5
4
+ data.tar.gz: bfe8c1cb2eb4aa9f15eb08349425611deccb3517
5
5
  SHA512:
6
- metadata.gz: 38a9d697a211fd51f1e92bb289de5e7397971141188c15093880033cec1fee862d2bc3a1be00b0ac81b19dd97598e60e4f4e1e6d6aaa7222809fcd11850dc170
7
- data.tar.gz: 2834c21794d059e1d1dbb48b09dceacf9379bceb83199709b8d56d92a91a36410418681ed5b0b815272bd51b5dcbc84e7b6a678f4876eb1441f0efdc9bad0af2
6
+ metadata.gz: b9d827bf00b03d7766600e438d8ee16a9938ab099cd807d0d236cfb37758d104d76f26fdf713c7f39c550ce65df56c30b4dcfbeedb5fa7c5ebc93fa67c66199d
7
+ data.tar.gz: c4ac5998e89948246c01b6ec04d39ce5332c66da32902ac2b63a06672746dc002a91dc013372bc4cb33410a72e15c1de85bfb08aab8a17659dc9da84f4c793b5
@@ -0,0 +1,5 @@
1
+ version: 2
2
+
3
+ mkdocs:
4
+ configuration: mkdocs.yml
5
+ fail_on_warning: false
data/README.md CHANGED
@@ -37,14 +37,4 @@ form.errors # => {}
37
37
  ```
38
38
 
39
39
  ## Learn more
40
- - [Getting Started](https://github.com/mtkachenk0/paradocs/wiki/Getting-Started)
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)
@@ -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
+
@@ -0,0 +1,10 @@
1
+ # Configuration
2
+ ```rb
3
+ Paradocs.configure do |config|
4
+ config.explicit_errors = false # set to true if you want all errors from the policies to be explicitly registered in the policy
5
+ config.whitelisted_keys = [] # enrich it with global white-listed keys if you use WhiteList feature
6
+ config.default_schema_name = :schema # this name will be set for unnamed schemas
7
+ config.meta_prefix = "_" # used in #structure and #flatten_structure methods. All the metadata will be prefixed with this prefix.
8
+ config.whitelist_coercion = nil # set up a Proc here, that receives |value, field.meta| for each whitelisted field in order to enrich the whitelisting logic.
9
+ end
10
+ ```
@@ -0,0 +1,304 @@
1
+ # Documentation Generation
2
+
3
+ A `Schema` instance has a `#structure` method that return `Paradocs::Extensions::Structure` instance that allows instrospecting schema meta data.
4
+
5
+ It's supposed to have the following schema:
6
+ ```ruby
7
+ schema = Paradocs::Schema.new do
8
+ field(:data).type(:object).present.schema do
9
+ field(:id).type(:integer).present.policy(:policy_with_error)
10
+ field(:name).type(:string).meta(label: "very important staff")
11
+ field(:role).type(:string).declared.options(["admin", "user"]).default("user").mutates_schema! do |*|
12
+ :test_subschema
13
+ end
14
+ field(:extra).type(:array).required.schema do
15
+ field(:extra).declared.default(false).policy(:policy_with_silent_error)
16
+ end
17
+
18
+ mutation_by!(:name) { :subschema }
19
+
20
+ subschema(:subschema) do
21
+ field(:test_field).present
22
+ end
23
+ subschema(:test_subschema) do
24
+ field(:test1).present
25
+ end
26
+ end
27
+ end
28
+ ```
29
+
30
+ ## Structure#nested
31
+ > This method returns schema structure in a nested way including subschemes.
32
+ ```ruby
33
+ schema.structure.nested.to_json # =>
34
+ {
35
+ "_errors": ["ArgumentError"],
36
+ "_subschemes": {},
37
+ "data": {
38
+ "type": "object",
39
+ "required": true,
40
+ "present": true,
41
+ "json_path": "$.data",
42
+ "nested_name": "data",
43
+ "structure": {
44
+ "_subschemes": {
45
+ "subschema": {
46
+ "_errors": [],
47
+ "_subschemes": {},
48
+ "test_field": {
49
+ "required": true,
50
+ "present": true,
51
+ "json_path": "$.data.test_field",
52
+ "nested_name": "data.test_field"
53
+ }
54
+ },
55
+ "test_subschema": {
56
+ "_errors": [],
57
+ "_subschemes": {},
58
+ "test1": {
59
+ "required": true,
60
+ "present": true,
61
+ "json_path": "$.data.test1",
62
+ "nested_name": "data.test1"
63
+ }
64
+ }
65
+ },
66
+ "id": {
67
+ "type": "integer",
68
+ "required": true,
69
+ "present": true,
70
+ "policy_with_error": {"errors": ["ArgumentError"]},
71
+ "json_path": "$.data.id",
72
+ "nested_name": "data.id"
73
+ },
74
+ "name": {
75
+ "type": "string",
76
+ "label": "very important staff",
77
+ "json_path": "$.data.name",
78
+ "mutates_schema": true,
79
+ "nested_name": "data.name"
80
+ },
81
+ "role": {
82
+ "type": "string",
83
+ "options": ["admin", "user"],
84
+ "default": "user",
85
+ "json_path": "$.data.role",
86
+ "mutates_schema": true,
87
+ "nested_name": "data.role"
88
+ },
89
+ "extra": {
90
+ "type": "array",
91
+ "required": true,
92
+ "json_path": "$.data.extra[]",
93
+ "nested_name": "data.extra",
94
+ "structure": {
95
+ "_subschemes": {},
96
+ "extra": {
97
+ "default": false,
98
+ "policy_with_silent_error": {"errors": []},
99
+ "json_path": "$.data.extra[].extra",
100
+ "nested_name": "data.extra.extra"
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Structure#flatten
110
+ > This method returns schema structure in a flatten (without deep nesting) way including subschemes.
111
+ ```rb
112
+ schema.structure.flatten.to_json # =>
113
+ {
114
+ "_errors": ["ArgumentError"],
115
+ "_subschemes": {
116
+ "subschema": {
117
+ "_errors": [],
118
+ "_subschemes": {},
119
+ "data.test_field": {
120
+ "required": true,
121
+ "present": true,
122
+ "json_path": "$.data.test_field"
123
+ }
124
+ },
125
+ "test_subschema": {
126
+ "_errors": [],
127
+ "_subschemes": {},
128
+ "data.test1": {
129
+ "required": true,
130
+ "present": true,
131
+ "json_path": "$.data.test1"
132
+ }
133
+ }
134
+ },
135
+ "data": {
136
+ "type": "object",
137
+ "required": true,
138
+ "present": true,
139
+ "json_path": "$.data"
140
+ },
141
+ "data.id": {
142
+ "type": "integer",
143
+ "required": true,
144
+ "present": true,
145
+ "policy_with_error": {"errors": ["ArgumentError"]},
146
+ "json_path": "$.data.id"
147
+ },
148
+ "data.name": {
149
+ "type": "string",
150
+ "label": "very important staff",
151
+ "json_path": "$.data.name",
152
+ "mutates_schema": true
153
+ },
154
+ "data.role": {
155
+ "type": "string",
156
+ "options": ["admin", "user"],
157
+ "default": "user",
158
+ "json_path": "$.data.role",
159
+ "mutates_schema": true
160
+ },
161
+ "data.extra": {
162
+ "type": "array",
163
+ "required": true,
164
+ "json_path": "$.data.extra[]"
165
+ },
166
+ "data.extra.extra": {
167
+ "default": false,
168
+ "policy_with_silent_error": {"errors": []},
169
+ "json_path": "$.data.extra[].extra"
170
+ }
171
+ }
172
+ ```
173
+
174
+
175
+ ## Structure#all_nested
176
+
177
+ > This method returns all available combinations of schema (built on subschemas) saving the nesting.
178
+
179
+ Will return a hash with 2 structures named by the names of declared subschemas:
180
+ ```rb
181
+ all_nested = schema.structure.all_nested
182
+ all_nested.keys # => [:subschema, :test_subschema]
183
+ all_nested[:subschema] # =>
184
+ {
185
+ _errors: [],
186
+ "data" => {
187
+ type: :object,
188
+ required: true,
189
+ present: true,
190
+ json_path: "$.data",
191
+ nested_name: "data",
192
+ structure: {
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
+ }
201
+ }
202
+ }
203
+ }
204
+ all_nested[:test_subschema] # =>
205
+ {
206
+ _errors: [],
207
+ "data" => {
208
+ type: :object,
209
+ required: true,
210
+ present: true,
211
+ json_path: "$.data",
212
+ nested_name: "data",
213
+ structure: {
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
+ }
222
+ }
223
+ }
224
+ }
225
+ ```
226
+
227
+ ## Structure#all_flatten
228
+ > This method returns all available combinations of schema (built on subschema) without nesting (the same way as Structure#flatten method does)
229
+
230
+ Schema is the same as described in `Structure#all_nested`
231
+ ```rb
232
+ schema.structure.all_flatten # =>
233
+ {
234
+ subschema: {
235
+ _errors: [],
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"}
243
+ },
244
+ test_subschema: {
245
+ _errors: [],
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"}
253
+ }
254
+ }
255
+ ```
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
+
262
+ ## Schema#walk
263
+
264
+ The `#walk` method can recursively walk a schema definition and extract meta data or field attributes.
265
+
266
+ ```ruby
267
+ schema_documentation = create_user_schema.walk do |field|
268
+ {type: field.meta_data[:type], label: field.meta_data[:label]}
269
+ end.output
270
+
271
+ # Returns
272
+
273
+ {
274
+ name: {type: :string, label: "User's full name"},
275
+ age: {type: :integer, label: "User's age"},
276
+ status: {type: :string, label: nil},
277
+ friends: [
278
+ {
279
+ name: {type: :string, label: "Friend full name"},
280
+ email: {type: nil, label: "Friend email"}
281
+ }
282
+ ]
283
+ }
284
+ ```
285
+
286
+ When passed a _symbol_, it will collect that key from field meta data.
287
+
288
+ ```ruby
289
+ schema_labels = create_user_schema.walk(:label).output
290
+
291
+ # returns
292
+
293
+ {
294
+ name: "User's full name",
295
+ age: "User's age",
296
+ status: nil,
297
+ friends: [
298
+ {name: "Friend full name", email: "Friend email"}
299
+ ]
300
+ }
301
+ ```
302
+
303
+ Potential uses for this are generating documentation (HTML, or [JSON Schema](http://json-schema.org/), [Swagger](http://swagger.io/), or maybe even mock API endpoints with example data.
304
+
@@ -0,0 +1,21 @@
1
+ # FAQ
2
+ ## Defaults
3
+ ### Q: I need the child schema to be enriched with the specified defaults is parent key is absent.
4
+ ```rb
5
+ schema do
6
+ field(:top_level).type(:string).required.default('top_level')
7
+ field(:nested).type(:object).required.schema do
8
+ field(:start_date).type(:datetime).required.default(->(a,b,c) { Time.now })
9
+ field(:ends_after).type(:integer).required.default(5)
10
+ end
11
+ end
12
+ # usage
13
+ TestSchema.schema.resolve({}).output # => {:top_level=>"top_level", :nested=>nil, :configurations=>nil}
14
+ TestSchema.schema.resolve({nested: {}}).output # => {:top_level=>"top_level", :nested=>{:start_date=>#<DateTime: 2020-08-31T15:36:43+02:00 ((2459093j,49003s,0n),+7200s,2299161j)>, :ends_after=>5}, :configurations=>nil}
15
+ # I want resolving on {} to include the :nested structure.
16
+ ```
17
+
18
+ ### A: Set `.default({})` to your `:nested` field.
19
+ > Fields from nested schema are invoked only when the object for the schema exists.
20
+
21
+
@@ -0,0 +1,90 @@
1
+ # Form objects DSL
2
+
3
+ ## DSL
4
+ You can use schemas and fields on their own, or include the `DSL` module in your own classes to define form objects.
5
+
6
+ ```ruby
7
+ require "parametric/dsl"
8
+
9
+ class CreateUserForm
10
+ include Paradocs::DSL
11
+
12
+ schema(:test) do
13
+ field(:name).type(:string).required
14
+ field(:email).policy(:email).required
15
+ field(:age).type(:integer)
16
+ subschema_by(:age) { |age| age > 18 ? :allow : :deny }
17
+ end
18
+
19
+ subschema_for(:test, name: :allow) { field(:role).options(["sign_in"]) }
20
+ subschema_for(:test, name: :deny) { field(:role).options([]) }
21
+
22
+ attr_reader :params, :errors
23
+
24
+ def initialize(input_data)
25
+ results = self.class.schema.resolve(input_data)
26
+ @params = results.output
27
+ @errors = results.errors
28
+ end
29
+
30
+ def run!
31
+ if !valid?
32
+ raise InvalidFormError.new(errors)
33
+ end
34
+
35
+ run
36
+ end
37
+
38
+ def valid?
39
+ !errors.any?
40
+ end
41
+
42
+ private
43
+
44
+ def run
45
+ User.create!(params)
46
+ end
47
+ end
48
+ ```
49
+
50
+ Form schemas can also be defined by passing another form or schema instance. This can be useful when building form classes in runtime.
51
+
52
+ ```ruby
53
+ UserSchema = Paradocs::Schema.new do
54
+ field(:name).type(:string).present
55
+ field(:age).type(:integer)
56
+ end
57
+
58
+ class CreateUserForm
59
+ include Paradocs::DSL
60
+ # copy from UserSchema
61
+ schema UserSchema
62
+ end
63
+ ```
64
+
65
+ ## Form object inheritance
66
+
67
+ Sub classes of classes using the DSL will inherit schemas defined on the parent class.
68
+
69
+ ```ruby
70
+ class UpdateUserForm < CreateUserForm
71
+ # All field definitions in the parent are conserved.
72
+ # New fields can be defined
73
+ # or existing fields overriden
74
+ schema do
75
+ # make this field optional
76
+ field(:name).declared.present
77
+ end
78
+
79
+ def initialize(user, input_data)
80
+ super input_data
81
+ @user = user
82
+ end
83
+
84
+ private
85
+ def run
86
+ @user.update params
87
+ end
88
+ end
89
+ ```
90
+