json_schematize 0.1.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 +7 -0
- data/.circleci/config.yml +189 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +26 -0
- data/CODEOWNERS +1 -0
- data/Dockerfile +16 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +62 -0
- data/Makefile +21 -0
- data/README.md +101 -0
- data/bin/console +10 -0
- data/docker-compose.yml +15 -0
- data/json_schematize.gemspec +36 -0
- data/lib/json_schematize/field.rb +98 -0
- data/lib/json_schematize/field_transformations.rb +37 -0
- data/lib/json_schematize/field_validators.rb +85 -0
- data/lib/json_schematize/generator.rb +107 -0
- data/lib/json_schematize/version.rb +5 -0
- data/lib/json_schematize.rb +13 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cba47a2b20c23bb096ad36a9f71c0ebb8608da8b5ecf42746dcb7c1c223b4aeb
|
4
|
+
data.tar.gz: 227345574280f3dc5de25990b600976393e2bb6f970370b90cf00d1a371913e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d53863642a7b6b1fec0e22917508ee6c9a0a8a6452ea36955f3d8349878edf42487d267978d4175f3da72b0fc87651b66f4f4a28a3c1a98b7cc8c61858b0eb1e
|
7
|
+
data.tar.gz: 639c2eabdcabb64fbbcc89aa83e8e6f9e314424d2bcc31a44b710aef887e98fda3a2120edae8952c2ff3d34caacbaf450a5a40ed06417397d68743c7883c9a60
|
@@ -0,0 +1,189 @@
|
|
1
|
+
version: 2.1
|
2
|
+
|
3
|
+
# Declare the orbs that we'll use in our config.
|
4
|
+
# read more about orbs: https://circleci.com/docs/2.0/using-orbs/
|
5
|
+
orbs:
|
6
|
+
ruby: circleci/ruby@1.0
|
7
|
+
|
8
|
+
common_envs: &common_envs
|
9
|
+
environment:
|
10
|
+
APP_BUNDLER_VERSION: "2.3.8"
|
11
|
+
|
12
|
+
executors:
|
13
|
+
gem:
|
14
|
+
parameters:
|
15
|
+
ruby-version:
|
16
|
+
default: "3.0.0"
|
17
|
+
type: string
|
18
|
+
docker:
|
19
|
+
- image: cimg/ruby:<< parameters.ruby-version >>
|
20
|
+
<<: *common_envs
|
21
|
+
|
22
|
+
commands:
|
23
|
+
bundler-preamble:
|
24
|
+
description: "Install ruby and the Gemfile dependencies"
|
25
|
+
parameters:
|
26
|
+
ruby-version:
|
27
|
+
type: string
|
28
|
+
default: "3"
|
29
|
+
description: "Ruby Gem version"
|
30
|
+
key-name:
|
31
|
+
type: string
|
32
|
+
default: ""
|
33
|
+
description: Custom name to add to the cache key
|
34
|
+
cache-breaker:
|
35
|
+
type: string
|
36
|
+
default: "0"
|
37
|
+
description: "Cache breaker to force new cache instantiation"
|
38
|
+
keep-lock-file:
|
39
|
+
type: boolean
|
40
|
+
default: false
|
41
|
+
description: "Retain lock file when bit is set to true. Otherwise generate a new one specific for this build"
|
42
|
+
steps:
|
43
|
+
- checkout
|
44
|
+
- attach_workspace:
|
45
|
+
at: workspace
|
46
|
+
- unless:
|
47
|
+
condition:
|
48
|
+
equal: [ true, << parameters.keep-lock-file >> ]
|
49
|
+
steps:
|
50
|
+
- run:
|
51
|
+
name: Remove Gemfile.lock
|
52
|
+
command: rm Gemfile.lock
|
53
|
+
- restore_cache:
|
54
|
+
key: bundle-<< parameters.ruby-version >>-<< parameters.key-name >>-{{ checksum "Gemfile" }}-<< parameters.cache-breaker >>
|
55
|
+
- ruby/install-deps:
|
56
|
+
path: vendor/bundle
|
57
|
+
with-cache: false
|
58
|
+
- run:
|
59
|
+
name: "Update dependencies"
|
60
|
+
command: bundle update
|
61
|
+
- save_cache:
|
62
|
+
key: bundle-<< parameters.ruby-version >>-<< parameters.key-name >>-{{ checksum "Gemfile" }}-<< parameters.cache-breaker >>
|
63
|
+
paths:
|
64
|
+
- vendor/bundle
|
65
|
+
|
66
|
+
jobs:
|
67
|
+
publish-rubygems:
|
68
|
+
executor: gem
|
69
|
+
steps:
|
70
|
+
- bundler-preamble:
|
71
|
+
keep-lock-file: true
|
72
|
+
- run:
|
73
|
+
name: Publish to Ruby Gems
|
74
|
+
command: |
|
75
|
+
if [ -z "$GEM_HOST_API_KEY" ]; then
|
76
|
+
echo 'Environment variable GEM_HOST_API_KEY is not present'
|
77
|
+
exit 1
|
78
|
+
fi
|
79
|
+
gem_output=$(gem build json_schematize.gemspec)
|
80
|
+
VERSION=$(printf "$gem_output" | awk '/Version: / {print $2}')
|
81
|
+
FILE=$(printf "$gem_output" | awk '/File: / {print $2}')
|
82
|
+
|
83
|
+
gem push ${FILE}
|
84
|
+
|
85
|
+
publish-github:
|
86
|
+
executor: gem
|
87
|
+
steps:
|
88
|
+
- bundler-preamble:
|
89
|
+
keep-lock-file: true
|
90
|
+
- run:
|
91
|
+
name: 'Get Go'
|
92
|
+
command: |
|
93
|
+
sudo apt-get update -qq
|
94
|
+
sudo apt-get -y --no-install-recommends install golang-go
|
95
|
+
- run:
|
96
|
+
name: 'Set Git stats'
|
97
|
+
command: |
|
98
|
+
git config user.name $GITHUB_USER
|
99
|
+
git config user.email $GITHUB_EMAIL
|
100
|
+
- run:
|
101
|
+
name: Publish Git Release
|
102
|
+
command: |
|
103
|
+
curl -sSL https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz | tar xz -f - -C . ghr_v0.13.0_linux_amd64/ghr
|
104
|
+
mv ghr_v0.13.0_linux_amd64/ghr .
|
105
|
+
rm -rf ghr_v0.13.0_linux_amd64
|
106
|
+
chmod 0755 ghr
|
107
|
+
|
108
|
+
if [ -z "$GITHUB_TOKEN" ]; then
|
109
|
+
echo 'Environment variable GITHUB_TOKEN is not present'
|
110
|
+
exit 1
|
111
|
+
fi
|
112
|
+
|
113
|
+
if [ -z "$GITHUB_USER" ]; then
|
114
|
+
echo 'Environment variable GITHUB_USER is not present'
|
115
|
+
exit 1
|
116
|
+
fi
|
117
|
+
|
118
|
+
if [ $(git tag -l "$VERSION") ]; then
|
119
|
+
echo "Pre existing version $VERSION, not tagging."
|
120
|
+
exit 0
|
121
|
+
fi
|
122
|
+
|
123
|
+
gem_output=$(gem build json_schematize.gemspec)
|
124
|
+
VERSION=$(printf "$gem_output" | awk '/Version: / {print $2}')
|
125
|
+
FILE=$GEM_PATH$(printf "$gem_output" | awk '/File: / {print $2}')
|
126
|
+
|
127
|
+
git tag -a $VERSION -m "$VERSION"
|
128
|
+
|
129
|
+
SHA=$(git rev-parse HEAD)
|
130
|
+
./ghr -soft -t ${GITHUB_TOKEN} -u ${GITHUB_USER} -r json_schematize -c ${SHA} ${VERSION} ${FILE}
|
131
|
+
|
132
|
+
test:
|
133
|
+
parameters:
|
134
|
+
ruby-version:
|
135
|
+
default: "3.0.0"
|
136
|
+
type: string
|
137
|
+
description: "Ruby version to test against"
|
138
|
+
executor:
|
139
|
+
name: gem
|
140
|
+
ruby-version: << parameters.ruby-version >>
|
141
|
+
steps:
|
142
|
+
- bundler-preamble:
|
143
|
+
ruby-version: "<< parameters.ruby-version >>"
|
144
|
+
- run:
|
145
|
+
name: Setup Climate Control test-reporter
|
146
|
+
command: |
|
147
|
+
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
148
|
+
chmod +x ./cc-test-reporter
|
149
|
+
- run:
|
150
|
+
name: Run and Report tests
|
151
|
+
command: |
|
152
|
+
./cc-test-reporter before-build
|
153
|
+
SIMPLE_COV_RUN=true bundle exec rspec --format RspecJunitFormatter --out test-results/rspec/rspec.xml --format progress --color
|
154
|
+
- when:
|
155
|
+
condition:
|
156
|
+
equal:
|
157
|
+
[
|
158
|
+
required-test-ruby2.7.5,
|
159
|
+
required-test-ruby<< parameters.ruby-version >>,
|
160
|
+
]
|
161
|
+
steps:
|
162
|
+
- run:
|
163
|
+
name: Report CodeClimate Coverage
|
164
|
+
command: ./cc-test-reporter after-build --coverage-input-type simplecov
|
165
|
+
workflows:
|
166
|
+
version: 2
|
167
|
+
yeet-le-jobs:
|
168
|
+
jobs:
|
169
|
+
- test:
|
170
|
+
matrix:
|
171
|
+
parameters:
|
172
|
+
ruby-version: ["2.7.5" , "3.0.0", "3.0.3"]
|
173
|
+
alias: required-matrix-tests
|
174
|
+
name: required-test-ruby<< matrix.ruby-version >>
|
175
|
+
- publish-github:
|
176
|
+
requires:
|
177
|
+
- required-matrix-tests
|
178
|
+
filters:
|
179
|
+
branches:
|
180
|
+
only:
|
181
|
+
- main
|
182
|
+
- publish-rubygems:
|
183
|
+
requires:
|
184
|
+
- required-matrix-tests
|
185
|
+
# filters:
|
186
|
+
# branches:
|
187
|
+
# only:
|
188
|
+
# - main
|
189
|
+
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
## [0.1.0] - YYYY-MM-DD
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- New feature 1
|
14
|
+
- New feature 2
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
- Changed feature 1
|
18
|
+
- Changed feature 2
|
19
|
+
|
20
|
+
### Removed
|
21
|
+
- Removed feature 1
|
22
|
+
- Removed feature 2
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
- Bug fix 1
|
26
|
+
- Bug fix 2
|
data/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@matt-taylor
|
data/Dockerfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
FROM ruby:3.0.1
|
2
|
+
RUN cd /tmp && curl -L --output ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.12.0/ghr_v0.12.0_linux_amd64.tar.gz && \
|
3
|
+
tar -xzvf ghr.tar.gz && chmod +x ghr_v0.12.0_linux_amd64/ghr && mv ghr_v0.12.0_linux_amd64/ghr /usr/local/bin/ghr && rm -rf /tmp/*
|
4
|
+
|
5
|
+
WORKDIR /gem
|
6
|
+
COPY Gemfile /gem/Gemfile
|
7
|
+
|
8
|
+
COPY json_schematize.gemspec /gem/json_schematize.gemspec
|
9
|
+
COPY lib/json_schematize/version.rb /gem/lib/json_schematize/version.rb
|
10
|
+
|
11
|
+
|
12
|
+
RUN gem update --system && gem install bundler && bundle install --jobs=3 --retry=3 && \
|
13
|
+
rm -rf /usr/local/bundle/cache
|
14
|
+
|
15
|
+
COPY . /gem
|
16
|
+
RUN gem build json_schematize
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
json_schematize (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
byebug (11.1.3)
|
10
|
+
coderay (1.1.3)
|
11
|
+
concurrent-ruby (1.1.9)
|
12
|
+
diff-lcs (1.5.0)
|
13
|
+
docile (1.4.0)
|
14
|
+
faker (2.20.0)
|
15
|
+
i18n (>= 1.8.11, < 2)
|
16
|
+
i18n (1.10.0)
|
17
|
+
concurrent-ruby (~> 1.0)
|
18
|
+
method_source (1.0.0)
|
19
|
+
pry (0.13.1)
|
20
|
+
coderay (~> 1.1)
|
21
|
+
method_source (~> 1.0)
|
22
|
+
pry-byebug (3.9.0)
|
23
|
+
byebug (~> 11.0)
|
24
|
+
pry (~> 0.13.0)
|
25
|
+
rake (12.3.3)
|
26
|
+
rspec (3.11.0)
|
27
|
+
rspec-core (~> 3.11.0)
|
28
|
+
rspec-expectations (~> 3.11.0)
|
29
|
+
rspec-mocks (~> 3.11.0)
|
30
|
+
rspec-core (3.11.0)
|
31
|
+
rspec-support (~> 3.11.0)
|
32
|
+
rspec-expectations (3.11.0)
|
33
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
34
|
+
rspec-support (~> 3.11.0)
|
35
|
+
rspec-mocks (3.11.0)
|
36
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
37
|
+
rspec-support (~> 3.11.0)
|
38
|
+
rspec-support (3.11.0)
|
39
|
+
rspec_junit_formatter (0.5.1)
|
40
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
41
|
+
simplecov (0.21.2)
|
42
|
+
docile (~> 1.1)
|
43
|
+
simplecov-html (~> 0.11)
|
44
|
+
simplecov_json_formatter (~> 0.1)
|
45
|
+
simplecov-html (0.12.3)
|
46
|
+
simplecov_json_formatter (0.1.4)
|
47
|
+
|
48
|
+
PLATFORMS
|
49
|
+
x86_64-linux
|
50
|
+
|
51
|
+
DEPENDENCIES
|
52
|
+
faker
|
53
|
+
json_schematize!
|
54
|
+
pry
|
55
|
+
pry-byebug
|
56
|
+
rake (~> 12.0)
|
57
|
+
rspec (~> 3.0)
|
58
|
+
rspec_junit_formatter
|
59
|
+
simplecov
|
60
|
+
|
61
|
+
BUNDLED WITH
|
62
|
+
2.3.8
|
data/Makefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
.PHONY: bash build bundle rspec
|
2
|
+
|
3
|
+
APP_NAME?=json_schematize
|
4
|
+
|
5
|
+
build: #: Build the containers that we'll need
|
6
|
+
docker-compose build --pull
|
7
|
+
|
8
|
+
bash: #: Get a bash prompt on the core container
|
9
|
+
docker-compose run --rm -e RAILS_ENV=development $(APP_NAME) bash
|
10
|
+
|
11
|
+
bash_test: #: Get a test bash prompt on the core container
|
12
|
+
docker-compose run --rm -e RAILS_ENV=test $(APP_NAME) bash
|
13
|
+
|
14
|
+
down: #: Bring down the service -- Destroys everything in redis and all containers
|
15
|
+
docker-compose down
|
16
|
+
|
17
|
+
clean: #: Clean up stopped/exited containers
|
18
|
+
docker-compose rm -f
|
19
|
+
|
20
|
+
bundle: #: install gems for Dummy App with
|
21
|
+
docker-compose run --rm $(APP_NAME) bundle install
|
data/README.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# JsonSchematize
|
2
|
+
|
3
|
+
`JsonSchematize` is emant to be a simple schema control version used to aprse data returned from any API.
|
4
|
+
|
5
|
+
It can handle nested Schematized versions and build the tree all the way down
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'json_schematize'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle install
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install json_schematize
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Given hash value of the following:
|
26
|
+
```json
|
27
|
+
{
|
28
|
+
"status":"complete",
|
29
|
+
"id":"127392",
|
30
|
+
"body":
|
31
|
+
[
|
32
|
+
{
|
33
|
+
"status":"failed",
|
34
|
+
"id":"12345",
|
35
|
+
"field_i_dont_care_about": false
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"status":"failed",
|
39
|
+
"id":"6347",
|
40
|
+
"field_i_dont_care_about": true
|
41
|
+
}
|
42
|
+
]
|
43
|
+
}
|
44
|
+
```
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# lib/schema/internal_body
|
48
|
+
require 'json_schematize/generator'
|
49
|
+
|
50
|
+
class InternalBody < JsonSchematize::Generator
|
51
|
+
ALLOWED_STATUSES = [:failed, :completed, :success]
|
52
|
+
|
53
|
+
add_field name: :id, type: Integer
|
54
|
+
add_field name: :status, type: Symbol, validator: ->(transformed_value, raw_value) { ALLOWED_STATUSES.include?(transformed_value) }
|
55
|
+
end
|
56
|
+
|
57
|
+
###
|
58
|
+
# lib/schema/my_first_schema
|
59
|
+
require 'json_schematize/generator'
|
60
|
+
require 'internal_body' #dependeing on load order
|
61
|
+
|
62
|
+
class MyFirstSchema < JsonSchematize::Generator
|
63
|
+
add_field name: :internals, type: InternalBody, array_of_types: true, dig: ["body"]
|
64
|
+
add_field name: :id, type: Integer
|
65
|
+
add_field name: :status, type: Symbol
|
66
|
+
end
|
67
|
+
|
68
|
+
schema = MyFirstSchema.new(**json_hash)
|
69
|
+
schema.internals.count #=> 2
|
70
|
+
schema.internals.first.status #=> :failed
|
71
|
+
schema.id #=> 127392
|
72
|
+
schema.id = 999999 #assignments are still subject to validation logic for each field
|
73
|
+
schema.id #=> 999999
|
74
|
+
```
|
75
|
+
|
76
|
+
### Field options:
|
77
|
+
```bash
|
78
|
+
name -- Name of the field. Field name can be accessed from the instance
|
79
|
+
type -- Class of the expected field type
|
80
|
+
types -- To be used when you want the field to have multiple types. Useful for similar classes like DateTime, Date, Time (converter must be supplied when multiple types are given)
|
81
|
+
dig_type -- Methodolgy of how to dig into the given param for the field. All values of the `dig` array will be converted accordingly. Default is `none` and will attempt to use what is given. [:symbol, :string, :none].
|
82
|
+
dig -- Array telling JsonSchematize how to dig into the provided hash
|
83
|
+
validator -- Proc value to validate the data found in the params. Proc given (transformed_value, original_value) when calling in
|
84
|
+
required -- Default is true. When not set, each instance class can optionally decide if they want to raise when an this is set to false.
|
85
|
+
converter -- Proc return is set to the field value. No furter validation is done. Given (value) as a parameter
|
86
|
+
array_of_types -- Detailed example above. Set this value to true when the dig param is to an array and you want all values in array to be parsed the given type
|
87
|
+
```
|
88
|
+
|
89
|
+
|
90
|
+
## Development
|
91
|
+
|
92
|
+
This gem can be developed against local machine or while using docker. Simpleified Docker commands can be found in the `Makefile` or execute `make help`
|
93
|
+
|
94
|
+
## Contributing
|
95
|
+
|
96
|
+
This gem welcomes contribution.
|
97
|
+
|
98
|
+
Bug reports and pull requests are welcome on GitHub at
|
99
|
+
https://github.com/matt-taylor/json_schematize.
|
100
|
+
|
101
|
+
|
data/bin/console
ADDED
data/docker-compose.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/json_schematize/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "json_schematize"
|
7
|
+
spec.version = JsonSchematize::VERSION
|
8
|
+
spec.authors = ["Matt Taylor"]
|
9
|
+
spec.email = ["mattius.taylor@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Describe the gem here"
|
12
|
+
spec.description = "Describe the gem here"
|
13
|
+
spec.homepage = "https://github.com/matt-taylor/json_schematize"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
|
17
|
+
|
18
|
+
spec.metadata = {
|
19
|
+
"homepage_uri" => spec.homepage,
|
20
|
+
"source_code_uri" => spec.homepage,
|
21
|
+
}
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
+
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
end
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_development_dependency "pry-byebug"
|
33
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
34
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
35
|
+
spec.add_development_dependency "simplecov", "~> 0.17.0"
|
36
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json_schematize/field_transformations'
|
4
|
+
require 'json_schematize/field_validators'
|
5
|
+
|
6
|
+
class JsonSchematize::Field
|
7
|
+
|
8
|
+
attr_reader :name, :types, :dig, :symbol, :validator, :acceptable_types, :required, :converter, :array_of_types
|
9
|
+
|
10
|
+
EXPECTED_DIG_TYPE = [DIG_SYMBOL = :symbol, DEFAULT_DIG = DIG_NONE =:none, DIG_STRING = :string]
|
11
|
+
|
12
|
+
def initialize(name:, types:, dig:, dig_type:, validator:, type:, required:, converter:, array_of_types: false)
|
13
|
+
@name = name
|
14
|
+
@types = types
|
15
|
+
@type = type
|
16
|
+
@dig = dig.nil? ? [name] : dig
|
17
|
+
@dig_type = dig_type || DEFAULT_DIG
|
18
|
+
@required = required
|
19
|
+
@validator = validator
|
20
|
+
@acceptable_types = []
|
21
|
+
@converter = converter
|
22
|
+
@array_of_types = array_of_types
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup!
|
26
|
+
# validations must be done beofre transformations
|
27
|
+
valiadtions!
|
28
|
+
transformations!
|
29
|
+
end
|
30
|
+
|
31
|
+
def value_transform(value:)
|
32
|
+
return iterate_array_of_types(value: value) if array_of_types
|
33
|
+
|
34
|
+
raw_converter_call(value: value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def acceptable_value?(transformed_value:, raise_on_error:)
|
38
|
+
if array_of_types
|
39
|
+
boolean = transformed_value.all? { |val| @acceptable_types.include?(val.class) }
|
40
|
+
else
|
41
|
+
boolean = @acceptable_types.include?(transformed_value.class)
|
42
|
+
end
|
43
|
+
|
44
|
+
if raise_on_error && (boolean==false)
|
45
|
+
raise JsonSchematize::InvalidFieldByType, ":#{name} is an invalid option based on acceptable klass types [#{@acceptable_types}]#{ " -- array_of_types enabled" if array_of_types }"
|
46
|
+
end
|
47
|
+
|
48
|
+
boolean
|
49
|
+
end
|
50
|
+
|
51
|
+
def acceptable_value_by_validator?(transformed_value:, raw_value:, raise_on_error:)
|
52
|
+
if array_of_types
|
53
|
+
boolean = transformed_value.all? { |val| validator.call(transformed_value, raw_value) }
|
54
|
+
else
|
55
|
+
boolean = validator.call(transformed_value, raw_value)
|
56
|
+
end
|
57
|
+
|
58
|
+
boolean = validator.call(transformed_value, raw_value)
|
59
|
+
if raise_on_error && (boolean==false)
|
60
|
+
raise JsonSchematize::InvalidFieldByValidator, ":#{name} is an invalid option based on validator :proc option; #{validator}#{ " -- array_of_types enabled" if array_of_types }"
|
61
|
+
end
|
62
|
+
|
63
|
+
boolean
|
64
|
+
end
|
65
|
+
|
66
|
+
def value_from_field(params)
|
67
|
+
begin
|
68
|
+
value = params.dig(*dig)
|
69
|
+
rescue TypeError => e
|
70
|
+
msg = "Unable to dig #{dig} for field :#{name}. Returning nil"
|
71
|
+
::Kernel.warn(msg)
|
72
|
+
return nil
|
73
|
+
end
|
74
|
+
|
75
|
+
{ raw_value: value, transformed_value: value_transform(value: value)}
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def iterate_array_of_types(value:)
|
81
|
+
return raw_converter_call(value: value) unless array_of_types
|
82
|
+
|
83
|
+
unless value.is_a?(Array)
|
84
|
+
raise JsonSchematize::InvalidFieldByArrayOfTypes, ":#{name} expected to be an array based on :array_of_types flag. Given #{value.class}"
|
85
|
+
end
|
86
|
+
|
87
|
+
value.map do |val|
|
88
|
+
raw_converter_call(value: val)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def raw_converter_call(value:)
|
93
|
+
converter.call(value)
|
94
|
+
end
|
95
|
+
|
96
|
+
include JsonSchematize::FieldTransformations
|
97
|
+
include JsonSchematize::FieldValidators
|
98
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchematize::FieldTransformations
|
4
|
+
DEFAULT_CONVERTERS = {
|
5
|
+
Float => ->(val) { val.to_f },
|
6
|
+
Integer => ->(val) { val.to_i },
|
7
|
+
String => ->(val) { val.to_s },
|
8
|
+
Symbol => ->(val) { val.to_sym },
|
9
|
+
}
|
10
|
+
|
11
|
+
def transformations!
|
12
|
+
transform_converter_type!
|
13
|
+
transform_dig_type!
|
14
|
+
end
|
15
|
+
|
16
|
+
def transform_converter_type!
|
17
|
+
return unless @converter.nil?
|
18
|
+
|
19
|
+
# validations have already happened -- We know @acceptable types is a single klass
|
20
|
+
|
21
|
+
@converter = DEFAULT_CONVERTERS[@acceptable_types[0]]
|
22
|
+
if @converter.nil?
|
23
|
+
@converter = ->(val) { @acceptable_types[0].new(val) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def transform_dig_type!
|
28
|
+
case @dig_type
|
29
|
+
when JsonSchematize::Field::DIG_SYMBOL
|
30
|
+
@dig = @dig.map(&:to_sym)
|
31
|
+
when JsonSchematize::Field::DIG_STRING
|
32
|
+
@dig = @dig.map(&:to_s)
|
33
|
+
when JsonSchematize::Field::DIG_NONE
|
34
|
+
@dig
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonSchematize::FieldValidators
|
4
|
+
|
5
|
+
def valiadtions!
|
6
|
+
validate_type!(t: @type)
|
7
|
+
validate_types!
|
8
|
+
validate_name!
|
9
|
+
validate_validator!
|
10
|
+
validate_required!
|
11
|
+
validate_dig_type!
|
12
|
+
validate_dig!
|
13
|
+
validate_converter!
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_converter!
|
17
|
+
return if validate_converter_nil!
|
18
|
+
|
19
|
+
return if validate_converter_proc!
|
20
|
+
|
21
|
+
return if validate_converter_hash!
|
22
|
+
|
23
|
+
raise JsonSchematize::FieldError, ":converter passed unexpected type. Expected [Hash, Proc, nil]. Given [#{@converter.class}]"
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate_converter_nil!
|
27
|
+
return false unless @converter.nil?
|
28
|
+
|
29
|
+
return true if @acceptable_types.length == 1
|
30
|
+
|
31
|
+
raise JsonSchematize::FieldError, ":converter expected to be populated with multiple accepted types [#{@acceptable_types}]"
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate_converter_proc!
|
35
|
+
@converter.is_a?(Proc)
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_converter_hash!
|
39
|
+
return false unless converter.is_a?(Hash)
|
40
|
+
|
41
|
+
if @converter.keys.map(&:name).sort != @acceptable_types.map(&:name).sort
|
42
|
+
raise JsonSchematize::FieldError, ":converter given a hash. Keys of hash do not match klass types of accepted types. Given [#{converter.keys}]. Expected [#{@acceptable_types}]"
|
43
|
+
end
|
44
|
+
|
45
|
+
return true if @converter.values.all? { |klass| klass.is_a?(Proc) }
|
46
|
+
|
47
|
+
raise JsonSchematize::FieldError, ":converter given a hash. Values of proc must all be of type Proc"
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_required!
|
51
|
+
raise JsonSchematize::FieldError, ":required expected to be an boolean" unless [true, false].include?(@required)
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_dig_type!
|
55
|
+
values = JsonSchematize::Field::EXPECTED_DIG_TYPE
|
56
|
+
raise JsonSchematize::FieldError, ":dig_type expected to be an #{values}" unless values.include?(@dig_type)
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_validator!
|
60
|
+
raise JsonSchematize::FieldError, ":validator expected to be an proc" unless @validator.is_a?(Proc)
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_dig!
|
64
|
+
raise JsonSchematize::FieldError, ":dig expected to be an Array" unless @dig.is_a?(Array)
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate_name!
|
68
|
+
raise JsonSchematize::FieldError, ":name expected to be symbol" unless @name.is_a?(Symbol)
|
69
|
+
end
|
70
|
+
|
71
|
+
def validate_type!(t:, message: ":type expected to be a Class object")
|
72
|
+
raise JsonSchematize::FieldError, message unless t.class == Class
|
73
|
+
|
74
|
+
return if @acceptable_types.include?(t)
|
75
|
+
|
76
|
+
@acceptable_types << t
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate_types!
|
80
|
+
raise JsonSchematize::FieldError, ":types expected to be an array" unless @types.is_a?(Array)
|
81
|
+
@types.each do |t|
|
82
|
+
validate_type!(t: t, message: ":types expected to be an array with class Objects")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json_schematize/field"
|
4
|
+
|
5
|
+
# SchemafiedJSON
|
6
|
+
# JSONSchematize
|
7
|
+
|
8
|
+
class JsonSchematize::Generator
|
9
|
+
EMPTY_VALIDATOR = ->(_transformed_value,_raw_value) { true }
|
10
|
+
PROTECTED_METHODS = [:assign_values!, :convenience_methods, :validate_required!, :validate_optional!, :validate_value]
|
11
|
+
|
12
|
+
def self.add_field(name:, type: nil, types: [], dig_type: nil, dig: nil, validator: EMPTY_VALIDATOR, required: true, converter: nil, array_of_types: false)
|
13
|
+
field_params = {
|
14
|
+
converter: converter,
|
15
|
+
dig: dig,
|
16
|
+
dig_type: dig_type,
|
17
|
+
name: name,
|
18
|
+
required: required,
|
19
|
+
type: type,
|
20
|
+
types: types,
|
21
|
+
validator: validator,
|
22
|
+
}
|
23
|
+
field = JsonSchematize::Field.new(**field_params)
|
24
|
+
field.setup!
|
25
|
+
|
26
|
+
if required
|
27
|
+
required_fields << field
|
28
|
+
else
|
29
|
+
optional_fields << field
|
30
|
+
end
|
31
|
+
convenience_methods(field: field)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.fields
|
35
|
+
required_fields + optional_fields
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.required_fields
|
39
|
+
@required_fields ||= []
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.optional_fields
|
43
|
+
@optional_fields ||= []
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.convenience_methods(field:)
|
47
|
+
unless self.instance_methods.include?(:"#{field.name}=")
|
48
|
+
define_method(:"#{field.name}=") do |value|
|
49
|
+
validate_params = {
|
50
|
+
field: field,
|
51
|
+
raw_value: value,
|
52
|
+
transformed_value: value,
|
53
|
+
raise_on_error: raise_on_error,
|
54
|
+
}
|
55
|
+
return false unless validate_value(**validate_params)
|
56
|
+
|
57
|
+
instance_variable_set(:"@#{field.name}", value)
|
58
|
+
return true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
unless self.instance_methods.include?(:"#{field.name}")
|
62
|
+
define_method(:"#{field.name}") do
|
63
|
+
instance_variable_get("@#{field.name}".to_sym)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_reader :__raw_params, :raise_on_error
|
69
|
+
|
70
|
+
|
71
|
+
def initialize(raise_on_error: true, **params)
|
72
|
+
@__params = params
|
73
|
+
@raise_on_error = raise_on_error
|
74
|
+
|
75
|
+
validate_required!
|
76
|
+
validate_optional!
|
77
|
+
assign_values!
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def assign_values!
|
83
|
+
self.class.fields.each do |field|
|
84
|
+
value = field.value_from_field(@__params)[:transformed_value]
|
85
|
+
|
86
|
+
instance_variable_set(:"@#{field.name}", value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate_required!
|
91
|
+
self.class.required_fields.each do |field|
|
92
|
+
value = field.value_from_field(@__params)
|
93
|
+
validate_value(field: field, raise_on_error: true, **value)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def validate_optional!
|
98
|
+
self.class.optional_fields.each do |field|
|
99
|
+
value = field.value_from_field(@__params)
|
100
|
+
validate_value(field: field, raise_on_error: raise_on_error, **value)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def validate_value(field:, raw_value: nil, **params)
|
105
|
+
field.acceptable_value?(**params) && field.acceptable_value_by_validator?(raw_value: raw_value, **params)
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json_schematize/version"
|
4
|
+
require "json_schematize/generator"
|
5
|
+
|
6
|
+
module JsonSchematize
|
7
|
+
class Error < StandardError; end
|
8
|
+
class FieldError < Error; end
|
9
|
+
class InvalidField < Error; end
|
10
|
+
class InvalidFieldByValidator < InvalidField; end
|
11
|
+
class InvalidFieldByType < InvalidField; end
|
12
|
+
class InvalidFieldByArrayOfTypes < InvalidField; end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json_schematize
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Taylor
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-03-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pry-byebug
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '12.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '12.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.17.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.17.0
|
69
|
+
description: Describe the gem here
|
70
|
+
email:
|
71
|
+
- mattius.taylor@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".circleci/config.yml"
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- ".ruby-version"
|
80
|
+
- CHANGELOG.md
|
81
|
+
- CODEOWNERS
|
82
|
+
- Dockerfile
|
83
|
+
- Gemfile
|
84
|
+
- Gemfile.lock
|
85
|
+
- Makefile
|
86
|
+
- README.md
|
87
|
+
- bin/console
|
88
|
+
- docker-compose.yml
|
89
|
+
- json_schematize.gemspec
|
90
|
+
- lib/json_schematize.rb
|
91
|
+
- lib/json_schematize/field.rb
|
92
|
+
- lib/json_schematize/field_transformations.rb
|
93
|
+
- lib/json_schematize/field_validators.rb
|
94
|
+
- lib/json_schematize/generator.rb
|
95
|
+
- lib/json_schematize/version.rb
|
96
|
+
homepage: https://github.com/matt-taylor/json_schematize
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata:
|
100
|
+
homepage_uri: https://github.com/matt-taylor/json_schematize
|
101
|
+
source_code_uri: https://github.com/matt-taylor/json_schematize
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.7'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubygems_version: 3.2.3
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Describe the gem here
|
121
|
+
test_files: []
|