json_schematize 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []