schema-model 0.6.11 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +53 -0
- data/.rubocop.yml +70 -8
- data/ARCHITECTURE.md +135 -0
- data/CLAUDE.md +74 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +39 -36
- data/README.md +360 -85
- data/lib/schema/active_model_validations.rb +14 -14
- data/lib/schema/array_headers.rb +6 -4
- data/lib/schema/arrays.rb +11 -13
- data/lib/schema/associations/dynamic_types.rb +3 -5
- data/lib/schema/associations/has_many.rb +2 -4
- data/lib/schema/associations/has_one.rb +3 -5
- data/lib/schema/associations/schema_creator.rb +2 -2
- data/lib/schema/csv_parser.rb +1 -1
- data/lib/schema/model.rb +26 -21
- data/lib/schema/parsers/array.rb +2 -2
- data/lib/schema/parsers/common.rb +6 -8
- data/lib/schema/parsers/hash.rb +1 -1
- data/lib/schema/parsers/json.rb +1 -1
- data/lib/schema/parsing_errors.rb +1 -1
- data/lib/schema/utils.rb +5 -5
- data/lib/schema-model.rb +1 -0
- data/schema-model.gemspec +6 -4
- metadata +10 -11
- data/.cursor-rules +0 -39
- data/.travis.yml +0 -14
- data/Rakefile +0 -11
- data/docs/ARCHITECTURE.md +0 -171
- data/script/bundle_install_all_versions +0 -8
- data/script/rspec_all_versions +0 -5
|
@@ -19,8 +19,8 @@ module Schema
|
|
|
19
19
|
kls
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def default_type(options = {}, &
|
|
23
|
-
add_type(:default, options, &
|
|
22
|
+
def default_type(options = {}, &)
|
|
23
|
+
add_type(:default, options, &)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def dynamic_types
|
|
@@ -34,9 +34,7 @@ module Schema
|
|
|
34
34
|
private
|
|
35
35
|
|
|
36
36
|
def schema_dynamic_type_class_name(type)
|
|
37
|
-
::Schema::Utils.classify_name(schema_name.to_s)
|
|
38
|
-
'AssociationType' +
|
|
39
|
-
::Schema::Utils.classify_name(type.to_s)
|
|
37
|
+
"#{::Schema::Utils.classify_name(schema_name.to_s)}AssociationType#{::Schema::Utils.classify_name(type.to_s)}"
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
def schema_add_dynamic_type(type, class_name)
|
|
@@ -10,12 +10,11 @@ module Schema
|
|
|
10
10
|
|
|
11
11
|
# no-doc
|
|
12
12
|
module ClassMethods
|
|
13
|
-
# rubocop:disable Naming/PredicateName
|
|
14
13
|
def has_many(name, options = {}, &block)
|
|
15
14
|
options = ::Schema::Utils.add_association_class(self, name, :has_many, options)
|
|
16
15
|
|
|
17
16
|
class_eval(
|
|
18
|
-
<<-STR, __FILE__, __LINE__ + 1
|
|
17
|
+
<<-STR, __FILE__, __LINE__ + 1
|
|
19
18
|
def #{options[:getter]}
|
|
20
19
|
#{options[:instance_variable]}
|
|
21
20
|
end
|
|
@@ -32,7 +31,7 @@ module Schema
|
|
|
32
31
|
#{options[:instance_variable]} ||= []
|
|
33
32
|
#{options[:instance_variable]} << #{name}_schema_creator.create_schema(self, v, nil, skip_fields)
|
|
34
33
|
end
|
|
35
|
-
STR
|
|
34
|
+
STR
|
|
36
35
|
)
|
|
37
36
|
|
|
38
37
|
kls = const_get(options[:class_name])
|
|
@@ -44,7 +43,6 @@ STR
|
|
|
44
43
|
add_aliases(name, options)
|
|
45
44
|
kls
|
|
46
45
|
end
|
|
47
|
-
# rubocop:enable Naming/PredicateName
|
|
48
46
|
end
|
|
49
47
|
end
|
|
50
48
|
end
|
|
@@ -10,12 +10,11 @@ module Schema
|
|
|
10
10
|
|
|
11
11
|
# no-doc
|
|
12
12
|
module ClassMethods
|
|
13
|
-
# rubocop:disable Naming/PredicateName
|
|
14
13
|
def has_one(name, options = {}, &block)
|
|
15
14
|
options = ::Schema::Utils.add_association_class(self, name, :has_one, options)
|
|
16
15
|
|
|
17
16
|
class_eval(
|
|
18
|
-
<<-STR, __FILE__, __LINE__ + 1
|
|
17
|
+
<<-STR, __FILE__, __LINE__ + 1
|
|
19
18
|
def #{options[:getter]}
|
|
20
19
|
#{options[:instance_variable]}
|
|
21
20
|
end
|
|
@@ -27,19 +26,18 @@ module Schema
|
|
|
27
26
|
def #{options[:setter]}(v, skip_fields = [])
|
|
28
27
|
#{options[:instance_variable]} = #{name}_schema_creator.create_schema(self, v, nil, skip_fields)
|
|
29
28
|
end
|
|
30
|
-
STR
|
|
29
|
+
STR
|
|
31
30
|
)
|
|
32
31
|
|
|
33
32
|
kls = const_get(options[:class_name])
|
|
34
33
|
kls.class_eval(&block) if block
|
|
35
34
|
if options[:default]
|
|
36
|
-
options[:default_code] = options[:class_name]
|
|
35
|
+
options[:default_code] = "#{options[:class_name]}.new"
|
|
37
36
|
::Schema::Utils.add_association_default_methods(self, options)
|
|
38
37
|
end
|
|
39
38
|
add_aliases(name, options)
|
|
40
39
|
kls
|
|
41
40
|
end
|
|
42
|
-
# rubocop:enable Naming/PredicateName
|
|
43
41
|
end
|
|
44
42
|
end
|
|
45
43
|
end
|
|
@@ -27,7 +27,7 @@ module Schema
|
|
|
27
27
|
add_parsing_error(base_schema, error_name, INVALID) unless schema.parsing_errors.empty?
|
|
28
28
|
schema
|
|
29
29
|
elsif !data.nil?
|
|
30
|
-
add_parsing_error(base_schema, error_name,
|
|
30
|
+
add_parsing_error(base_schema, error_name, INCOMPATIBLE)
|
|
31
31
|
nil
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -42,7 +42,7 @@ module Schema
|
|
|
42
42
|
schema
|
|
43
43
|
end
|
|
44
44
|
elsif !list.nil?
|
|
45
|
-
add_parsing_error(base_schema, @schema_name,
|
|
45
|
+
add_parsing_error(base_schema, @schema_name, INCOMPATIBLE)
|
|
46
46
|
nil
|
|
47
47
|
end
|
|
48
48
|
end
|
data/lib/schema/csv_parser.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Schema
|
|
|
32
32
|
|
|
33
33
|
def get_mapped_headers(mapped_headers)
|
|
34
34
|
indexed_headers = []
|
|
35
|
-
mapped_headers.
|
|
35
|
+
mapped_headers.each_value do |info|
|
|
36
36
|
if (index = info[:index])
|
|
37
37
|
indexed_headers << @headers[index]
|
|
38
38
|
elsif (indexes = info[:indexes])
|
data/lib/schema/model.rb
CHANGED
|
@@ -69,7 +69,7 @@ module Schema
|
|
|
69
69
|
|
|
70
70
|
add_value_to_class_method(:schema, name => options)
|
|
71
71
|
add_attribute_methods(name, options)
|
|
72
|
-
::Schema::Utils.add_attribute_default_methods(self, options) if options.
|
|
72
|
+
::Schema::Utils.add_attribute_default_methods(self, options) if options.key?(:default)
|
|
73
73
|
add_aliases(name, options)
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -82,7 +82,8 @@ module Schema
|
|
|
82
82
|
config[:schema_includes] = config[:schema_includes] + [mod]
|
|
83
83
|
redefine_class_method(:schema_config, config.freeze)
|
|
84
84
|
include mod
|
|
85
|
-
|
|
85
|
+
|
|
86
|
+
schema.each_value do |field_options|
|
|
86
87
|
next unless field_options[:association]
|
|
87
88
|
|
|
88
89
|
const_get(field_options[:class_name]).schema_include(mod)
|
|
@@ -91,7 +92,7 @@ module Schema
|
|
|
91
92
|
|
|
92
93
|
def add_attribute_methods(name, options)
|
|
93
94
|
class_eval(
|
|
94
|
-
<<-STR, __FILE__, __LINE__ + 1
|
|
95
|
+
<<-STR, __FILE__, __LINE__ + 1
|
|
95
96
|
def #{options[:getter]}
|
|
96
97
|
#{options[:instance_variable]}
|
|
97
98
|
end
|
|
@@ -99,7 +100,11 @@ module Schema
|
|
|
99
100
|
def #{options[:setter]}(v)
|
|
100
101
|
#{options[:instance_variable]} = #{options[:parser]}(#{name.inspect}, parsing_errors, v)
|
|
101
102
|
end
|
|
102
|
-
|
|
103
|
+
|
|
104
|
+
def #{options[:getter]}_was_set?
|
|
105
|
+
instance_variable_defined?(:#{options[:instance_variable]})
|
|
106
|
+
end
|
|
107
|
+
STR
|
|
103
108
|
)
|
|
104
109
|
end
|
|
105
110
|
|
|
@@ -123,18 +128,18 @@ STR
|
|
|
123
128
|
|
|
124
129
|
def as_json(opts = {})
|
|
125
130
|
self.class.schema.each_with_object({}) do |(field_name, field_options), memo|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
next if field_options[:alias_of]
|
|
132
|
+
|
|
133
|
+
value = public_send(field_options[:getter])
|
|
134
|
+
next if value.nil? && !opts[:include_nils]
|
|
135
|
+
next if opts[:select_filter] && !opts[:select_filter].call(field_name, value, field_options)
|
|
136
|
+
next if opts[:reject_filter]&.call(field_name, value, field_options)
|
|
137
|
+
|
|
138
|
+
memo[field_name] = if value.is_a?(Array)
|
|
139
|
+
value.map { |e| e.as_json(opts) }
|
|
140
|
+
else
|
|
141
|
+
value.respond_to?(:as_json) ? value.as_json(opts) : value
|
|
142
|
+
end
|
|
138
143
|
end
|
|
139
144
|
end
|
|
140
145
|
|
|
@@ -156,12 +161,12 @@ STR
|
|
|
156
161
|
private
|
|
157
162
|
|
|
158
163
|
def get_schema(data)
|
|
159
|
-
|
|
160
|
-
break unless key.is_a?(Symbol)
|
|
164
|
+
return self.class.schema_with_string_keys unless data.is_a?(Hash)
|
|
161
165
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
first_key = data.each_key.first
|
|
167
|
+
return self.class.schema_with_string_keys unless first_key.is_a?(Symbol)
|
|
168
|
+
|
|
169
|
+
self.class.schema
|
|
165
170
|
end
|
|
166
171
|
|
|
167
172
|
def update_model_attributes(schema, data, skip_fields)
|
data/lib/schema/parsers/array.rb
CHANGED
|
@@ -15,10 +15,10 @@ module Schema
|
|
|
15
15
|
return data
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
parsing_errors.add(field_name, ::Schema::ParsingErrors::
|
|
18
|
+
parsing_errors.add(field_name, ::Schema::ParsingErrors::INCOMPATIBLE)
|
|
19
19
|
nil
|
|
20
20
|
else
|
|
21
|
-
parsing_errors.add(field_name, ::Schema::ParsingErrors::
|
|
21
|
+
parsing_errors.add(field_name, ::Schema::ParsingErrors::INCOMPATIBLE)
|
|
22
22
|
nil
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -6,11 +6,10 @@ module Schema
|
|
|
6
6
|
module Parsers
|
|
7
7
|
# Schema::Parsers::Common are parser methods for basic types
|
|
8
8
|
module Common
|
|
9
|
-
INTEGER_REGEX = /^[-+]?(?:[1-9]\d*|0)(?:\.0+)
|
|
10
|
-
FLOAT_REGEX = /^[-+]?(?:[1-9]\d*|0)(?:\.\d+)?([Ee]-?\d+)
|
|
11
|
-
BOOLEAN_REGEX = /^(?:1|t|true|on|y|yes)$/i
|
|
9
|
+
INTEGER_REGEX = /^[-+]?(?:[1-9]\d*|0)(?:\.0+)?$/
|
|
10
|
+
FLOAT_REGEX = /^[-+]?(?:[1-9]\d*|0)(?:\.\d+)?([Ee]-?\d+)?$/
|
|
11
|
+
BOOLEAN_REGEX = /^(?:1|t|true|on|y|yes)$/i
|
|
12
12
|
|
|
13
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
14
13
|
def parse_integer(field_name, parsing_errors, value)
|
|
15
14
|
case value
|
|
16
15
|
when Integer
|
|
@@ -23,7 +22,7 @@ module Schema
|
|
|
23
22
|
nil
|
|
24
23
|
end
|
|
25
24
|
when Float
|
|
26
|
-
parsing_errors.add(field_name, ::Schema::ParsingErrors::
|
|
25
|
+
parsing_errors.add(field_name, ::Schema::ParsingErrors::INCOMPATIBLE) if (value % 1) > 0.0
|
|
27
26
|
value.to_i
|
|
28
27
|
when nil
|
|
29
28
|
nil
|
|
@@ -32,14 +31,13 @@ module Schema
|
|
|
32
31
|
nil
|
|
33
32
|
end
|
|
34
33
|
end
|
|
35
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
36
34
|
|
|
37
35
|
def parse_string(field_name, parsing_errors, value)
|
|
38
36
|
case value
|
|
39
37
|
when String
|
|
40
38
|
value
|
|
41
39
|
when ::Hash, ::Array
|
|
42
|
-
parsing_errors.add(field_name, ::Schema::ParsingErrors::
|
|
40
|
+
parsing_errors.add(field_name, ::Schema::ParsingErrors::INCOMPATIBLE)
|
|
43
41
|
nil
|
|
44
42
|
when nil
|
|
45
43
|
nil
|
|
@@ -54,7 +52,7 @@ module Schema
|
|
|
54
52
|
when String
|
|
55
53
|
value.empty? ? nil : value
|
|
56
54
|
when ::Hash, ::Array
|
|
57
|
-
parsing_errors.add(field_name, ::Schema::ParsingErrors::
|
|
55
|
+
parsing_errors.add(field_name, ::Schema::ParsingErrors::INCOMPATIBLE)
|
|
58
56
|
nil
|
|
59
57
|
when nil
|
|
60
58
|
nil
|
data/lib/schema/parsers/hash.rb
CHANGED
data/lib/schema/parsers/json.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Schema
|
|
|
4
4
|
# Schema::ParsingErrors is a collection of parsing error messages
|
|
5
5
|
module ParsingErrors
|
|
6
6
|
INVALID = 'invalid'
|
|
7
|
-
|
|
7
|
+
INCOMPATIBLE = 'incompatible'
|
|
8
8
|
UNKNOWN = 'unknown'
|
|
9
9
|
UNKNOWN_ATTRIBUTE = 'unknown_attribute'
|
|
10
10
|
UNHANDLED_TYPE = 'unhandled_type'
|
data/lib/schema/utils.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Schema
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def association_options(schema_name, schema_type, options)
|
|
28
|
-
options[:class_name] ||=
|
|
28
|
+
options[:class_name] ||= "Schema#{classify_name(schema_type.to_s)}#{classify_name(schema_name.to_s)}"
|
|
29
29
|
options[:association] = true
|
|
30
30
|
options[:aliases] = [options[:alias]] if options.key?(:alias)
|
|
31
31
|
options[:hash_key_field] ||= :id if options[:from] == :hash
|
|
@@ -58,7 +58,7 @@ module Schema
|
|
|
58
58
|
|
|
59
59
|
def add_attribute_default_methods(kls, options)
|
|
60
60
|
kls.class_eval(
|
|
61
|
-
|
|
61
|
+
<<-STR, __FILE__, __LINE__ + 1
|
|
62
62
|
def #{options[:default_method]}
|
|
63
63
|
#{options[:default].inspect}
|
|
64
64
|
end
|
|
@@ -70,13 +70,13 @@ module Schema
|
|
|
70
70
|
#{options[:instance_variable]}
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
|
-
STR
|
|
73
|
+
STR
|
|
74
74
|
)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def add_association_default_methods(kls, options)
|
|
78
78
|
kls.class_eval(
|
|
79
|
-
|
|
79
|
+
<<-STR, __FILE__, __LINE__ + 1
|
|
80
80
|
def #{options[:default_method]}
|
|
81
81
|
#{options[:default_code]}
|
|
82
82
|
end
|
|
@@ -84,7 +84,7 @@ STR
|
|
|
84
84
|
def #{options[:getter]}
|
|
85
85
|
#{options[:instance_variable]} ||= #{options[:default_method]}
|
|
86
86
|
end
|
|
87
|
-
STR
|
|
87
|
+
STR
|
|
88
88
|
)
|
|
89
89
|
end
|
|
90
90
|
end
|
data/lib/schema-model.rb
CHANGED
data/schema-model.gemspec
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = 'schema-model'
|
|
5
|
-
s.version = '0.
|
|
5
|
+
s.version = '0.7.0'
|
|
6
6
|
s.licenses = ['MIT']
|
|
7
|
-
s.summary = '
|
|
8
|
-
s.description = '
|
|
7
|
+
s.summary = 'Data transformation, validation, and type safety for Ruby'
|
|
8
|
+
s.description = 'A flexible DSL for defining strongly-typed data models with automatic parsing, ' \
|
|
9
|
+
'nested associations, dynamic types, and ActiveModel validations'
|
|
9
10
|
s.authors = ['Doug Youch']
|
|
10
11
|
s.email = 'dougyouch@gmail.com'
|
|
11
12
|
s.homepage = 'https://github.com/dougyouch/schema'
|
|
@@ -13,5 +14,6 @@ Gem::Specification.new do |s|
|
|
|
13
14
|
s.bindir = 'bin'
|
|
14
15
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
15
16
|
|
|
16
|
-
s.
|
|
17
|
+
s.add_dependency 'inheritance-helper'
|
|
18
|
+
s.metadata['rubygems_mfa_required'] = 'true'
|
|
17
19
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: schema-model
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Doug Youch
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-02-01 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: inheritance-helper
|
|
@@ -23,26 +23,26 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
-
description:
|
|
26
|
+
description: A flexible DSL for defining strongly-typed data models with automatic
|
|
27
|
+
parsing, nested associations, dynamic types, and ActiveModel validations
|
|
27
28
|
email: dougyouch@gmail.com
|
|
28
29
|
executables:
|
|
29
30
|
- schema-json2csv
|
|
30
31
|
extensions: []
|
|
31
32
|
extra_rdoc_files: []
|
|
32
33
|
files:
|
|
33
|
-
- ".
|
|
34
|
+
- ".github/workflows/ci.yml"
|
|
34
35
|
- ".gitignore"
|
|
35
36
|
- ".rubocop.yml"
|
|
36
37
|
- ".ruby-gemset"
|
|
37
38
|
- ".ruby-version"
|
|
38
|
-
-
|
|
39
|
+
- ARCHITECTURE.md
|
|
40
|
+
- CLAUDE.md
|
|
39
41
|
- Gemfile
|
|
40
42
|
- Gemfile.lock
|
|
41
43
|
- LICENSE.txt
|
|
42
44
|
- README.md
|
|
43
|
-
- Rakefile
|
|
44
45
|
- bin/schema-json2csv
|
|
45
|
-
- docs/ARCHITECTURE.md
|
|
46
46
|
- lib/schema-model.rb
|
|
47
47
|
- lib/schema/active_model_validations.rb
|
|
48
48
|
- lib/schema/all.rb
|
|
@@ -65,13 +65,12 @@ files:
|
|
|
65
65
|
- lib/schema/utils.rb
|
|
66
66
|
- lib/schema_validator.rb
|
|
67
67
|
- schema-model.gemspec
|
|
68
|
-
- script/bundle_install_all_versions
|
|
69
68
|
- script/console
|
|
70
|
-
- script/rspec_all_versions
|
|
71
69
|
homepage: https://github.com/dougyouch/schema
|
|
72
70
|
licenses:
|
|
73
71
|
- MIT
|
|
74
|
-
metadata:
|
|
72
|
+
metadata:
|
|
73
|
+
rubygems_mfa_required: 'true'
|
|
75
74
|
rdoc_options: []
|
|
76
75
|
require_paths:
|
|
77
76
|
- lib
|
|
@@ -88,5 +87,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
88
87
|
requirements: []
|
|
89
88
|
rubygems_version: 3.6.2
|
|
90
89
|
specification_version: 4
|
|
91
|
-
summary:
|
|
90
|
+
summary: Data transformation, validation, and type safety for Ruby
|
|
92
91
|
test_files: []
|
data/.cursor-rules
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
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/.travis.yml
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
env:
|
|
2
|
-
global:
|
|
3
|
-
- CC_TEST_REPORTER_ID=f06234974aa4c4c52d14cb9ef800df79de52013c38e1d149fd04f2284e722f57
|
|
4
|
-
rvm:
|
|
5
|
-
- 3.1.0
|
|
6
|
-
language: ruby
|
|
7
|
-
before_script:
|
|
8
|
-
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
|
9
|
-
- chmod +x ./cc-test-reporter
|
|
10
|
-
- ./cc-test-reporter before-build
|
|
11
|
-
script:
|
|
12
|
-
- bundle exec rspec
|
|
13
|
-
after_script:
|
|
14
|
-
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/Rakefile
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'rubygems'
|
|
4
|
-
require 'bundler/setup'
|
|
5
|
-
require 'rake'
|
|
6
|
-
require 'rspec/core/rake_task'
|
|
7
|
-
RSpec::Core::RakeTask.new(:spec) do |t|
|
|
8
|
-
t.pattern = Dir.glob('spec/**/*_spec.rb')
|
|
9
|
-
t.rspec_opts = '--format documentation'
|
|
10
|
-
end
|
|
11
|
-
task default: :spec
|
data/docs/ARCHITECTURE.md
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
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
|