permittable 0.8.0 → 0.10.0
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/CHANGELOG.md +155 -0
- data/README.md +361 -33
- data/lib/permittable/audit.rb +268 -0
- data/lib/permittable/authored_values.rb +63 -0
- data/lib/permittable/column_guard.rb +133 -6
- data/lib/permittable/contract.rb +11 -2
- data/lib/permittable/error_envelope.rb +79 -4
- data/lib/permittable/field_group.rb +72 -0
- data/lib/permittable/generator.rb +822 -51
- data/lib/permittable/json_schema/ecma_pattern.rb +248 -0
- data/lib/permittable/json_schema.rb +252 -47
- data/lib/permittable/open_api.rb +247 -42
- data/lib/permittable/railtie.rb +1 -0
- data/lib/permittable/rspec.rb +451 -25
- data/lib/permittable/tasks/audit.rake +34 -0
- data/lib/permittable/tasks/generate.rake +3 -2
- data/lib/permittable/version.rb +1 -1
- data/lib/permittable.rb +985 -116
- metadata +9 -4
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Permittable
|
|
2
|
+
# A reusable field list — the same frozen data a contract's `fields` is,
|
|
3
|
+
# without the contract around it. The answer to the duplication a growing
|
|
4
|
+
# API produces: an `address` block wanted by three controllers, and an
|
|
5
|
+
# `update` contract that is the `create` contract with everything relaxed.
|
|
6
|
+
#
|
|
7
|
+
# AddressFields = Permittable.fields do
|
|
8
|
+
# required :city, :string, length: 1..80
|
|
9
|
+
# optional :zip, :string, format: /\A\d{5}\z/
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# UserFields = Permittable.fields do
|
|
13
|
+
# required :name, :string
|
|
14
|
+
# required :email, :string, format: URI::MailTo::EMAIL_REGEXP
|
|
15
|
+
# optional :plan, :string, in: %w[free pro], default: "free"
|
|
16
|
+
# optional :address do
|
|
17
|
+
# use AddressFields
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# class UsersController < ApplicationController
|
|
22
|
+
# include Permittable
|
|
23
|
+
#
|
|
24
|
+
# permit_params :create, root: :user, model: User do
|
|
25
|
+
# use UserFields
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# # PATCH: same fields, nothing mandatory.
|
|
29
|
+
# permit_params :update, root: :user, model: User do
|
|
30
|
+
# use UserFields, optional: true
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# A group is built by the same ContractBuilder a contract is, so every
|
|
35
|
+
# declaration is validated when the GROUP is defined — a typo fails at the
|
|
36
|
+
# group, once, rather than at each contract that uses it. It is frozen on
|
|
37
|
+
# construction and its fields are frozen hashes, so one group is safely
|
|
38
|
+
# shared by any number of contracts, and `use` splices without copying.
|
|
39
|
+
#
|
|
40
|
+
# What a group deliberately is not: a contract. It has no `root:`,
|
|
41
|
+
# `unknown:`, `model:` or `mode:` — those belong to the request being
|
|
42
|
+
# validated, not to a set of fields — and `finalize` is rejected for the
|
|
43
|
+
# same reason.
|
|
44
|
+
class FieldGroup
|
|
45
|
+
# The frozen field hashes, in declaration order.
|
|
46
|
+
attr_reader :fields
|
|
47
|
+
|
|
48
|
+
class << self
|
|
49
|
+
alias define new
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def initialize(&block)
|
|
53
|
+
raise ArgumentError, "#{LABEL}: Permittable.fields requires a block declaring the fields" unless block
|
|
54
|
+
|
|
55
|
+
builder = ContractBuilder.new
|
|
56
|
+
@fields = builder.build(&block)
|
|
57
|
+
raise ArgumentError, "#{LABEL}: a field group must declare at least one field" if @fields.empty?
|
|
58
|
+
if builder.finalizer
|
|
59
|
+
raise ArgumentError, "#{LABEL}: finalize belongs to a contract, not a field group — " \
|
|
60
|
+
"declare it in the permit_params block that uses this group"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
freeze
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Top-level field names, in declaration order — what `only:`/`except:`
|
|
67
|
+
# select from.
|
|
68
|
+
def names
|
|
69
|
+
@fields.map { |field| field[:name] }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|