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 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
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt*
9
+ /tmp/
10
+ .DS_Store
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
14
+
15
+ # generated gems
16
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem 'faker'
8
+ gem 'pry'
9
+ gem 'rspec', '~> 3.0'
10
+ gem 'rspec_junit_formatter'
11
+ gem 'simplecov', require: false, group: :test
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
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'json_schematize'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier.
8
+
9
+ require 'pry'
10
+ Pry.start
@@ -0,0 +1,15 @@
1
+ version: '3'
2
+
3
+ services:
4
+ json_schematize:
5
+ command: tail -f /dev/null
6
+ build:
7
+ context: .
8
+ dockerfile: ./Dockerfile
9
+ volumes:
10
+ - .:/gem
11
+ - ..:/local
12
+ - bundle-cache:/usr/local/bundle:delegated
13
+
14
+ volumes:
15
+ bundle-cache:
@@ -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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonSchematize
4
+ VERSION = "0.1.0"
5
+ 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: []