schema-model 0.6.10 → 0.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f8ccd138c2e52700656fe5bcf67ba518cdfb4c756cf656f821928758872c795
4
- data.tar.gz: 90f75db3fc2e4fbae2bf350894b973cea392e652ca0be9b81368f0e9195f6c4b
3
+ metadata.gz: 0604e96e9d70ef3b47b7581ef0c6c43005960af46dde776565700abf86482ad5
4
+ data.tar.gz: cdfdbf20d77cfd81521df44308083b1134062c19e041752a2a194ee05c420c4d
5
5
  SHA512:
6
- metadata.gz: ed515074d1a4ec263196f6217c9917f03dadb24dd6dc0b116168198b0c78e73c2cb18f268ed23ea19997d3d06a7fc46b269a51bd42b4e33c38325be0226b86d1
7
- data.tar.gz: 58ccd5d4e9902f0dfff14e92222dcb2a6095cbd5bf281004ca738bdbc33618c38c300f76115d25ca91749d3d8b08cd4ec3548c0eb1fdeb43eabb0494b893055e
6
+ metadata.gz: 590a840b90865bc31c4c75c0f34f10135c9d410532c6585d64afa20e39f712262bb43794cea9414877eb8df3602567cf057deb75867d9436eea0315a313aea15
7
+ data.tar.gz: 0d9b8ee1ba799df269f396c68692e9210f90bde87192f3f121809a42db99647630b12c5624ed108ff58079a661a1f51b6547e876797dc32a0a887ebb7bcbfc41
data/.cursor-rules ADDED
@@ -0,0 +1,39 @@
1
+ # Cursor Rules Configuration
2
+
3
+ # Ignore common development and system files
4
+ ignore {
5
+ ".git/"
6
+ ".DS_Store"
7
+ "*.log"
8
+ "tmp/"
9
+ "node_modules/"
10
+ "coverage/"
11
+ }
12
+
13
+ # Define common code block markers
14
+ markers {
15
+ start "# START"
16
+ end "# END"
17
+ }
18
+
19
+ # Define language-specific comment styles
20
+ comments {
21
+ ruby "#"
22
+ javascript "//"
23
+ python "#"
24
+ html "<!--"
25
+ css "/*"
26
+ }
27
+
28
+ # Define safe edit boundaries
29
+ boundaries {
30
+ function_start "^(def|function|class) "
31
+ block_start "^(if|while|for|begin|module) "
32
+ }
33
+
34
+ # Define merge conflict markers to avoid
35
+ conflicts {
36
+ start "<<<<<<< "
37
+ middle "======="
38
+ end ">>>>>>> "
39
+ }
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.4.2
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gem 'inheritance-helper'
6
6
 
7
7
  group :development do
8
8
  gem 'activemodel'
9
+ gem 'csv'
9
10
  gem 'rake'
10
11
  gem 'rubocop'
11
12
  end
data/Gemfile.lock CHANGED
@@ -1,68 +1,97 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activemodel (7.0.2.3)
5
- activesupport (= 7.0.2.3)
6
- activesupport (7.0.2.3)
7
- concurrent-ruby (~> 1.0, >= 1.0.2)
4
+ activemodel (8.0.2)
5
+ activesupport (= 8.0.2)
6
+ activesupport (8.0.2)
7
+ base64
8
+ benchmark (>= 0.3)
9
+ bigdecimal
10
+ concurrent-ruby (~> 1.0, >= 1.3.1)
11
+ connection_pool (>= 2.2.5)
12
+ drb
8
13
  i18n (>= 1.6, < 2)
14
+ logger (>= 1.4.2)
9
15
  minitest (>= 5.1)
10
- tzinfo (~> 2.0)
11
- ast (2.4.2)
12
- concurrent-ruby (1.1.9)
13
- diff-lcs (1.5.0)
14
- docile (1.4.0)
15
- i18n (1.10.0)
16
+ securerandom (>= 0.3)
17
+ tzinfo (~> 2.0, >= 2.0.5)
18
+ uri (>= 0.13.1)
19
+ ast (2.4.3)
20
+ base64 (0.2.0)
21
+ benchmark (0.4.0)
22
+ bigdecimal (3.1.9)
23
+ concurrent-ruby (1.3.5)
24
+ connection_pool (2.5.0)
25
+ csv (3.3.3)
26
+ diff-lcs (1.6.1)
27
+ docile (1.4.1)
28
+ drb (2.2.1)
29
+ i18n (1.14.7)
16
30
  concurrent-ruby (~> 1.0)
17
31
  inheritance-helper (0.2.5)
18
- minitest (5.15.0)
19
- parallel (1.21.0)
20
- parser (3.1.1.0)
32
+ json (2.10.2)
33
+ language_server-protocol (3.17.0.4)
34
+ lint_roller (1.1.0)
35
+ logger (1.7.0)
36
+ minitest (5.25.5)
37
+ parallel (1.26.3)
38
+ parser (3.3.7.4)
21
39
  ast (~> 2.4.1)
40
+ racc
41
+ prism (1.4.0)
42
+ racc (1.8.1)
22
43
  rainbow (3.1.1)
23
- rake (13.0.6)
24
- regexp_parser (2.2.1)
25
- rexml (3.2.5)
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)
44
+ rake (13.2.1)
45
+ regexp_parser (2.10.0)
46
+ rspec (3.13.0)
47
+ rspec-core (~> 3.13.0)
48
+ rspec-expectations (~> 3.13.0)
49
+ rspec-mocks (~> 3.13.0)
50
+ rspec-core (3.13.3)
51
+ rspec-support (~> 3.13.0)
52
+ rspec-expectations (3.13.3)
33
53
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.11.0)
35
- rspec-mocks (3.11.0)
54
+ rspec-support (~> 3.13.0)
55
+ rspec-mocks (3.13.2)
36
56
  diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.11.0)
38
- rspec-support (3.11.0)
39
- rubocop (1.26.0)
57
+ rspec-support (~> 3.13.0)
58
+ rspec-support (3.13.2)
59
+ rubocop (1.75.2)
60
+ json (~> 2.3)
61
+ language_server-protocol (~> 3.17.0.2)
62
+ lint_roller (~> 1.1.0)
40
63
  parallel (~> 1.10)
41
- parser (>= 3.1.0.0)
64
+ parser (>= 3.3.0.2)
42
65
  rainbow (>= 2.2.2, < 4.0)
43
- regexp_parser (>= 1.8, < 3.0)
44
- rexml
45
- rubocop-ast (>= 1.16.0, < 2.0)
66
+ regexp_parser (>= 2.9.3, < 3.0)
67
+ rubocop-ast (>= 1.44.0, < 2.0)
46
68
  ruby-progressbar (~> 1.7)
47
- unicode-display_width (>= 1.4.0, < 3.0)
48
- rubocop-ast (1.16.0)
49
- parser (>= 3.1.1.0)
50
- ruby-progressbar (1.11.0)
51
- simplecov (0.21.2)
69
+ unicode-display_width (>= 2.4.0, < 4.0)
70
+ rubocop-ast (1.44.0)
71
+ parser (>= 3.3.7.2)
72
+ prism (~> 1.4)
73
+ ruby-progressbar (1.13.0)
74
+ securerandom (0.4.1)
75
+ simplecov (0.22.0)
52
76
  docile (~> 1.1)
53
77
  simplecov-html (~> 0.11)
54
78
  simplecov_json_formatter (~> 0.1)
55
- simplecov-html (0.12.3)
79
+ simplecov-html (0.13.1)
56
80
  simplecov_json_formatter (0.1.4)
57
- tzinfo (2.0.4)
81
+ tzinfo (2.0.6)
58
82
  concurrent-ruby (~> 1.0)
59
- unicode-display_width (2.1.0)
83
+ unicode-display_width (3.1.4)
84
+ unicode-emoji (~> 4.0, >= 4.0.4)
85
+ unicode-emoji (4.0.4)
86
+ uri (1.0.3)
60
87
 
61
88
  PLATFORMS
62
- x86_64-darwin-21
89
+ arm64-darwin-24
90
+ ruby
63
91
 
64
92
  DEPENDENCIES
65
93
  activemodel
94
+ csv
66
95
  inheritance-helper
67
96
  rake
68
97
  rspec
@@ -70,4 +99,4 @@ DEPENDENCIES
70
99
  simplecov
71
100
 
72
101
  BUNDLED WITH
73
- 2.3.3
102
+ 2.6.2
data/README.md CHANGED
@@ -1,163 +1,190 @@
1
- # schema
1
+ # Schema
2
2
 
3
- Fast and easy way to transform data into models for validation and type safety.
3
+ A powerful Ruby gem for data transformation, validation, and type safety. Schema provides a flexible and intuitive way to define data models with support for complex nested structures, dynamic associations, and robust validation.
4
4
 
5
5
  [![Build Status](https://travis-ci.org/dougyouch/schema.svg?branch=master)](https://travis-ci.org/dougyouch/schema)
6
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/c142d46a7a37d4a8c2e5/maintainability)](https://codeclimate.com/github/dougyouch/schema/maintainability)
7
7
  [![Test Coverage](https://api.codeclimate.com/v1/badges/c142d46a7a37d4a8c2e5/test_coverage)](https://codeclimate.com/github/dougyouch/schema/test_coverage)
8
8
 
9
- Attributes of a model have a name and type. Any value passed in goes through a parser method. If the value can not be parsed successfully the error is added to parsing_errors.
9
+ ## Features
10
10
 
11
- Associations are nested schema models. Each association can have its own set of attributes.
11
+ - **Type Safety**: Strong typing with automatic parsing and validation
12
+ - **Flexible Attributes**: Support for aliases and custom data types
13
+ - **Nested Models**: Complex data structures with nested associations
14
+ - **Dynamic Associations**: Runtime type-based model creation
15
+ - **ActiveModel Integration**: Seamless integration with ActiveModel validations
16
+ - **Error Handling**: Comprehensive error collection and reporting
17
+ - **CSV Support**: Built-in CSV parsing capabilities
12
18
 
13
- Dynamic associations are useful when creating custom logic around schema validation.
19
+ ## Installation
14
20
 
15
- #### Example that show cases multiple features
21
+ Add this line to your application's Gemfile:
16
22
 
17
- ###### spec/examples/company_schema.rb
18
23
  ```ruby
19
- # frozen_string_literal: true
24
+ gem 'schema-model'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ ```bash
30
+ $ bundle install
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ Here's a simple example to get you started:
20
36
 
21
- # Example that show cases multiple features
22
- class CompanySchema
23
- # includes model, associations, parsers and active model validations
37
+ ```ruby
38
+ class UserSchema
24
39
  include Schema::All
25
40
 
26
- # add common attributes
27
- # attributes support additional names through the alias(es) option
28
- attribute :name, :string, alias: 'CompanyName'
29
- attribute :industry_type, :string, aliases: %w[IndustryType industry]
41
+ attribute :name, :string
42
+ attribute :age, :integer
43
+ attribute :email, :string
44
+ attribute :tags, :array, separator: ',', data_type: :string
45
+
46
+ validates :name, presence: true
47
+ validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
48
+ end
49
+
50
+ # Usage
51
+ user_data = {
52
+ name: 'John Doe',
53
+ age: '30',
54
+ email: 'john@example.com',
55
+ tags: 'ruby,rails,developer'
56
+ }
57
+
58
+ user = UserSchema.from_hash(user_data)
59
+ if user.valid?
60
+ puts "User created: #{user.name}"
61
+ else
62
+ puts "Validation errors: #{user.errors.full_messages}"
63
+ end
64
+ ```
30
65
 
31
- # will take a string split on the separator and use the parse_<data_type> method on every element
32
- # basically take a list of comma separated numbers and create an array of integers
33
- # code snippet: str.split(',').map { |v| parse_integer(field_name, parsing_errors, v) }
34
- attribute :number_list, :array, separator: ',', data_type: :integer
66
+ ## Core Concepts
35
67
 
36
- # creates a nested dynamic schema based on the industry_type which is part of the main company data
37
- industry_schema = has_one(:industry, external_type_field: :industry_type) do
38
- attribute :name, :string
68
+ ### Attributes
39
69
 
40
- validates :name, presence: true
70
+ Attributes define the structure of your data model. Each attribute has:
71
+ - A name
72
+ - A type
73
+ - Optional aliases
74
+ - Custom parsing rules
41
75
 
42
- add_type('tech') do
43
- attribute :custom_description, :string
44
- end
76
+ ```ruby
77
+ attribute :name, :string, alias: 'FullName'
78
+ attribute :age, :integer
79
+ attribute :tags, :array, separator: ',', data_type: :string
80
+ ```
45
81
 
46
- add_type('qsr') do
47
- attribute :number_of_locations, :integer
82
+ ### Associations
48
83
 
49
- # custom validation
50
- validates :number_of_locations, presence: true
51
- end
52
- end
84
+ Schema supports various types of associations:
53
85
 
54
- # create multiple dynamic location schemas based on the type field in the location data
55
- has_many(:locations, type_field: :type) do
56
- attribute :type, :string
57
- attribute :address, :string
58
- attribute :city, :string
59
- attribute :state, :string
60
- attribute :zip, :string
86
+ 1. **Has One**: Single nested model
87
+ 2. **Has Many**: Multiple nested models
88
+ 3. **Dynamic Associations**: Type-based model creation
89
+
90
+ ```ruby
91
+ has_one(:profile) do
92
+ attribute :bio, :string
93
+ attribute :website, :string
94
+ end
95
+
96
+ has_many(:posts) do
97
+ attribute :title, :string
98
+ attribute :content, :string
99
+ end
100
+ ```
61
101
 
62
- add_type('headquarters') do
63
- attribute :main_floor, :integer
102
+ ### Dynamic Types
64
103
 
65
- validates :city, presence: true
66
- validates :main_floor, presence: true
67
- end
104
+ Create different model structures based on a type field:
68
105
 
69
- add_type('store_front') do
70
- attribute :main_entrance, :string
106
+ ```ruby
107
+ has_many(:vehicles, type_field: :type) do
108
+ attribute :type, :string
109
+ attribute :color, :string
71
110
 
72
- validates :address, presence: true
73
- validates :main_entrance, presence: true
74
- end
111
+ add_type('car') do
112
+ attribute :doors, :integer
75
113
  end
76
114
 
77
- # create multiple dynamic employee schemas based on the type field in the employee data
78
- has_many(:employees, type_field: :type) do
79
- attribute :type, :integer
80
- attribute :name, :string
81
- attribute :start_date, :date
82
- add_type(1) do # worker
83
- attribute :manager_name, :string
84
- end
85
- add_type(2) do # manager
86
- attribute :rank, :float
87
- end
88
- # if no or an invalid type is specified, create a default employee schema object
89
- # useful for communicating errors in an API
90
- default_type
91
-
92
- # dynamic_type_names returns all the types used, except for :default
93
- validates :type, inclusion: { in: dynamic_type_names }
115
+ add_type('truck') do
116
+ attribute :bed_length, :float
94
117
  end
118
+ end
119
+ ```
95
120
 
96
- has_many(:admins, from: :hash, hash_key_field: :username) do
97
- attribute :username, :string
98
- attribute :email, :string
99
- attribute :name, :string
121
+ ## Advanced Features
100
122
 
101
- validates :username, presence: true
102
- validates :email, presence: true
123
+ ### Custom Parsers
124
+
125
+ Define custom parsing logic for your attributes:
126
+
127
+ ```ruby
128
+ attribute :custom_field, :custom_type do
129
+ def parse_custom_type(field_name, errors, value)
130
+ # Custom parsing logic
103
131
  end
132
+ end
133
+ ```
104
134
 
105
- validates :name, presence: true
106
- validates :industry_type, inclusion: { in: industry_schema.dynamic_type_names }
135
+ ### CSV Integration
107
136
 
108
- # use the schema validator
109
- validates :industry, presence: true, schema: true
110
- validates :locations, presence: true, schema: true
111
- validates :employees, presence: true, schema: true
112
- validates :admins, presence: true, schema: true
137
+ Parse CSV data directly into your models:
138
+
139
+ ```ruby
140
+ class UserCSVSchema
141
+ include Schema::CSVParser
142
+
143
+ attribute :name, :string
144
+ attribute :email, :string
113
145
  end
146
+
147
+ users = UserCSVSchema.parse_csv(csv_data)
114
148
  ```
115
149
 
116
- ###### spec/examples/company_schema.json
117
- ```javascript
118
- {
119
- "CompanyName": "Good Burger",
120
- "IndustryType": "qsr",
121
- "industry": {
122
- "name": "Food & Beverage",
123
- "number_of_locations": 2
124
- },
125
- "locations": [
126
- {
127
- "type": "headquarters",
128
- "city": "Boston",
129
- "main_floor": 5
130
- },
131
- {
132
- "type": "store_front",
133
- "address": "1st Ave",
134
- "zip": "02211",
135
- "main_entrance": "side door"
136
- }
137
- ],
138
- "employees": [
139
- {
140
- "type": 2,
141
- "name": "Queen Bee",
142
- "start_date": "2016-01-09",
143
- "rank": "0.9"
144
- },
145
- {
146
- "type": 1,
147
- "name": "Worker Bee",
148
- "start_date": "2018-05-10",
149
- "manager_name": "Queen Bee"
150
- }
151
- ],
152
- "admins": {
153
- "captain": {
154
- "email": "captain@example.com",
155
- "name": "Captain Kurk"
156
- },
157
- "joe": {
158
- "email": "joe.smith@example.com",
159
- "name": "Joe Smith"
160
- }
161
- }
150
+ ## Using `skip_fields` to Protect Internal Fields
151
+
152
+ When instantiating a schema with `from_hash`, you can use the `skip_fields` argument to prevent certain fields (such as `id`, `created_at`, `updated_at`) from being set by user input. This is especially useful for fields managed by the database or internal logic, ensuring end users cannot override these values.
153
+
154
+ **Example:**
155
+
156
+ ```ruby
157
+ user_data = {
158
+ id: 123, # Should be managed by DB
159
+ name: 'John Doe',
160
+ email: 'john@example.com',
161
+ created_at: '2024-06-01T12:00:00Z', # Should be managed by DB
162
+ updated_at: '2024-06-01T12:00:00Z' # Should be managed by DB
162
163
  }
164
+
165
+ # Skip DB-managed fields
166
+ user = UserSchema.from_hash(user_data, [:id, :created_at, :updated_at])
167
+
168
+ puts user.id # => nil (not set from user input)
169
+ puts user.created_at # => nil (not set from user input)
170
+ puts user.updated_at # => nil (not set from user input)
171
+ puts user.name # => 'John Doe'
163
172
  ```
173
+
174
+ **Benefit:**
175
+ - Prevents end users from setting or changing internal DB values.
176
+ - Ensures only safe, intended fields are settable from external input.
177
+ - Helps maintain data integrity and security in your application.
178
+
179
+ ## Contributing
180
+
181
+ 1. Fork the repository
182
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
183
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
184
+ 4. Push to the branch (`git push origin my-new-feature`)
185
+ 5. Create a new Pull Request
186
+
187
+ ## License
188
+
189
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
190
+
@@ -0,0 +1,171 @@
1
+ # Schema Architecture Documentation
2
+
3
+ ## Overview
4
+
5
+ Schema is a Ruby gem that provides a robust framework for data transformation, validation, and type safety. It follows a modular architecture that allows for flexible and extensible data modeling.
6
+
7
+ ## Core Components
8
+
9
+ ### 1. Model System (`lib/schema/model.rb`)
10
+
11
+ The foundation of the Schema system, providing:
12
+ - Attribute definition and management
13
+ - Type system integration
14
+ - Data parsing and validation
15
+ - Association handling
16
+
17
+ Key features:
18
+ - Dynamic attribute registration
19
+ - Type coercion and validation
20
+ - Nested model support
21
+ - Error collection and reporting
22
+
23
+ ### 2. Association System (`lib/schema/associations/`)
24
+
25
+ Handles relationships between models:
26
+ - `has_one`: Single nested model
27
+ - `has_many`: Multiple nested models
28
+ - Dynamic associations based on type fields
29
+ - Hash-based associations
30
+
31
+ ### 3. Parser System (`lib/schema/parsers/`)
32
+
33
+ Responsible for data type conversion and validation:
34
+ - Built-in parsers for common types (string, integer, float, etc.)
35
+ - Custom parser support
36
+ - Array parsing with separators
37
+ - CSV data parsing
38
+
39
+ ### 4. Validation System (`lib/schema/active_model_validations.rb`)
40
+
41
+ Integrates with ActiveModel validations to provide:
42
+ - Attribute presence validation
43
+ - Format validation
44
+ - Custom validation rules
45
+ - Nested model validation
46
+
47
+ ### 5. Error Handling (`lib/schema/errors.rb`, `lib/schema/parsing_errors.rb`)
48
+
49
+ Comprehensive error management:
50
+ - Parsing errors
51
+ - Validation errors
52
+ - Nested model errors
53
+ - Error message formatting
54
+
55
+ ## Data Flow
56
+
57
+ 1. **Initialization**
58
+ - Schema class definition
59
+ - Attribute registration
60
+ - Association setup
61
+ - Validation rules configuration
62
+
63
+ 2. **Data Processing**
64
+ - Input data parsing
65
+ - Type coercion
66
+ - Association resolution
67
+ - Validation execution
68
+
69
+ 3. **Error Collection**
70
+ - Parsing error collection
71
+ - Validation error aggregation
72
+ - Nested model error propagation
73
+
74
+ ## Extension Points
75
+
76
+ ### Custom Parsers
77
+
78
+ ```ruby
79
+ module Schema::Parsers
80
+ class CustomParser
81
+ def self.parse(field_name, errors, value)
82
+ # Custom parsing logic
83
+ end
84
+ end
85
+ end
86
+ ```
87
+
88
+ ### Custom Validators
89
+
90
+ ```ruby
91
+ class CustomValidator < ActiveModel::Validator
92
+ def validate(record)
93
+ # Custom validation logic
94
+ end
95
+ end
96
+ ```
97
+
98
+ ### Custom Associations
99
+
100
+ ```ruby
101
+ module Schema::Associations
102
+ class CustomAssociation < Base
103
+ def initialize(name, options = {}, &block)
104
+ # Custom association logic
105
+ end
106
+ end
107
+ end
108
+ ```
109
+
110
+ ## Performance Considerations
111
+
112
+ 1. **Attribute Access**
113
+ - Dynamic method generation for attribute accessors
114
+ - Caching of parsed values
115
+ - Lazy loading of associations
116
+
117
+ 2. **Validation**
118
+ - Early termination on first error
119
+ - Parallel validation of independent attributes
120
+ - Caching of validation results
121
+
122
+ 3. **Memory Management**
123
+ - Efficient error object creation
124
+ - Minimal object allocation during parsing
125
+ - Garbage collection optimization
126
+
127
+ ## Security Considerations
128
+
129
+ 1. **Data Validation**
130
+ - Strict type checking
131
+ - Input sanitization
132
+ - Size limits on collections
133
+
134
+ 2. **Error Handling**
135
+ - Safe error message generation
136
+ - No sensitive data exposure
137
+ - Controlled error propagation
138
+
139
+ ## Testing Strategy
140
+
141
+ 1. **Unit Tests**
142
+ - Individual component testing
143
+ - Mock dependencies
144
+ - Edge case coverage
145
+
146
+ 2. **Integration Tests**
147
+ - Component interaction testing
148
+ - Real-world usage scenarios
149
+ - Performance benchmarks
150
+
151
+ 3. **Regression Tests**
152
+ - Backward compatibility
153
+ - Bug fixes verification
154
+ - Performance regression detection
155
+
156
+ ## Future Considerations
157
+
158
+ 1. **Planned Features**
159
+ - JSON Schema integration
160
+ - GraphQL type generation
161
+ - Async validation support
162
+
163
+ 2. **Performance Improvements**
164
+ - Parallel processing
165
+ - Memory optimization
166
+ - Caching strategies
167
+
168
+ 3. **API Evolution**
169
+ - Backward compatibility
170
+ - Deprecation strategy
171
+ - Version management
@@ -24,13 +24,13 @@ module Schema
24
24
  @#{name}_schema_creator ||= ::Schema::Associations::SchemaCreator.new(self, #{name.inspect})
25
25
  end
26
26
 
27
- def #{options[:setter]}(v)
28
- #{options[:instance_variable]} = #{name}_schema_creator.create_schemas(self, v)
27
+ def #{options[:setter]}(v, skip_fields = [])
28
+ #{options[:instance_variable]} = #{name}_schema_creator.create_schemas(self, v, skip_fields)
29
29
  end
30
30
 
31
- def append_to_#{options[:getter]}(v)
31
+ def append_to_#{options[:getter]}(v, skip_fields = [])
32
32
  #{options[:instance_variable]} ||= []
33
- #{options[:instance_variable]} << #{name}_schema_creator.create_schema(self, v)
33
+ #{options[:instance_variable]} << #{name}_schema_creator.create_schema(self, v, nil, skip_fields)
34
34
  end
35
35
  STR
36
36
  )
@@ -24,8 +24,8 @@ module Schema
24
24
  @#{name}_schema_creator ||= ::Schema::Associations::SchemaCreator.new(self, #{name.inspect})
25
25
  end
26
26
 
27
- def #{options[:setter]}(v)
28
- #{options[:instance_variable]} = #{name}_schema_creator.create_schema(self, v)
27
+ def #{options[:setter]}(v, skip_fields = [])
28
+ #{options[:instance_variable]} = #{name}_schema_creator.create_schema(self, v, nil, skip_fields)
29
29
  end
30
30
  STR
31
31
  )
@@ -17,13 +17,13 @@ module Schema
17
17
  configure_dynamic_schema_options(options)
18
18
  end
19
19
 
20
- def create_schema(base_schema, data, error_name = nil)
20
+ def create_schema(base_schema, data, error_name = nil, skip_fields = [])
21
21
  if data.is_a?(Hash)
22
22
  unless (schema_class = get_schema_class(base_schema, data))
23
23
  add_parsing_error(base_schema, error_name, UNKNOWN) if base_schema.class.capture_unknown_attributes?
24
24
  return nil
25
25
  end
26
- schema = schema_class.from_hash(data)
26
+ schema = schema_class.from_hash(data, skip_fields)
27
27
  add_parsing_error(base_schema, error_name, INVALID) unless schema.parsing_errors.empty?
28
28
  schema
29
29
  elsif !data.nil?
@@ -32,12 +32,12 @@ module Schema
32
32
  end
33
33
  end
34
34
 
35
- def create_schemas(base_schema, list)
35
+ def create_schemas(base_schema, list, skip_fields = [])
36
36
  if is_list? && list.is_a?(Array)
37
- list.each_with_index.map { |data, idx| create_schema(base_schema, data, "#{@schema_name}:#{idx}") }
37
+ list.each_with_index.map { |data, idx| create_schema(base_schema, data, "#{@schema_name}:#{idx}", skip_fields) }
38
38
  elsif !is_list? && list.is_a?(Hash)
39
39
  list.map do |(key, data)|
40
- schema = create_schema(base_schema, data, "#{@schema_name}:#{key}")
40
+ schema = create_schema(base_schema, data, "#{@schema_name}:#{key}", skip_fields)
41
41
  schema.send(schema.class.schema[@hash_key_field][:setter], key)
42
42
  schema
43
43
  end
data/lib/schema/model.rb CHANGED
@@ -73,8 +73,8 @@ module Schema
73
73
  add_aliases(name, options)
74
74
  end
75
75
 
76
- def from_hash(data)
77
- new.update_attributes(data)
76
+ def from_hash(data = nil, skip_fields = [])
77
+ new.update_attributes(data, skip_fields)
78
78
  end
79
79
 
80
80
  def schema_include(mod)
@@ -114,10 +114,10 @@ STR
114
114
  end
115
115
  end
116
116
 
117
- def update_attributes(data)
117
+ def update_attributes(data = nil, skip_fields = [])
118
118
  schema = get_schema(data)
119
- update_model_attributes(schema, data)
120
- update_associations(schema, data)
119
+ update_model_attributes(schema, data, skip_fields)
120
+ update_associations(schema, data, skip_fields)
121
121
  self
122
122
  end
123
123
 
@@ -164,7 +164,7 @@ STR
164
164
  self.class.schema_with_string_keys
165
165
  end
166
166
 
167
- def update_model_attributes(schema, data)
167
+ def update_model_attributes(schema, data, skip_fields)
168
168
  data.each do |key, value|
169
169
  unless schema.key?(key)
170
170
  parsing_errors.add(key, ::Schema::ParsingErrors::UNKNOWN_ATTRIBUTE) if self.class.capture_unknown_attributes?
@@ -172,17 +172,20 @@ STR
172
172
  end
173
173
 
174
174
  next if schema[key][:association]
175
+ next if skip_fields.include?(key)
175
176
 
176
177
  public_send(schema[key][:setter], value)
177
178
  end
178
179
  end
179
180
 
180
- def update_associations(schema, data)
181
+ def update_associations(schema, data, skip_fields)
181
182
  data.each do |key, value|
182
183
  next unless schema.key?(key)
183
184
  next unless schema[key][:association]
184
185
 
185
- public_send(schema[key][:setter], value)
186
+ association_skip_fields = skip_fields.detect { |f| f.is_a?(Hash) && f.include?(key) }
187
+ association_skip_fields = association_skip_fields ? association_skip_fields[key] : []
188
+ public_send(schema[key][:setter], value, association_skip_fields)
186
189
  end
187
190
  end
188
191
  end
data/schema-model.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'schema-model'
5
- s.version = '0.6.10'
5
+ s.version = '0.6.11'
6
6
  s.licenses = ['MIT']
7
7
  s.summary = 'Schema Model'
8
8
  s.description = 'Easy way to create models from payloads'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schema-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.10
4
+ version: 0.6.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doug Youch
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-03-28 00:00:00.000000000 Z
10
+ date: 2025-06-21 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: inheritance-helper
@@ -31,6 +30,7 @@ executables:
31
30
  extensions: []
32
31
  extra_rdoc_files: []
33
32
  files:
33
+ - ".cursor-rules"
34
34
  - ".gitignore"
35
35
  - ".rubocop.yml"
36
36
  - ".ruby-gemset"
@@ -42,6 +42,7 @@ files:
42
42
  - README.md
43
43
  - Rakefile
44
44
  - bin/schema-json2csv
45
+ - docs/ARCHITECTURE.md
45
46
  - lib/schema-model.rb
46
47
  - lib/schema/active_model_validations.rb
47
48
  - lib/schema/all.rb
@@ -71,7 +72,6 @@ homepage: https://github.com/dougyouch/schema
71
72
  licenses:
72
73
  - MIT
73
74
  metadata: {}
74
- post_install_message:
75
75
  rdoc_options: []
76
76
  require_paths:
77
77
  - lib
@@ -86,8 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  - !ruby/object:Gem::Version
87
87
  version: '0'
88
88
  requirements: []
89
- rubygems_version: 3.3.3
90
- signing_key:
89
+ rubygems_version: 3.6.2
91
90
  specification_version: 4
92
91
  summary: Schema Model
93
92
  test_files: []