schema-model 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e515db757ac9610d21f55a046be3dd4b7c9ea089f6ee5db2d027db5857a0804
4
- data.tar.gz: 734e46e76a917d70c0f170bea2b4a1bb4b784f43d00a64b7a5eb71d2d142ccfe
3
+ metadata.gz: 75daea073072722da9fb5e80565a35fe6861ab6293d757a2d91b76769835c119
4
+ data.tar.gz: ed1036610bf036e2bf96baf3f39561cd91063ca48cecace447b80129f7409db2
5
5
  SHA512:
6
- metadata.gz: 8b068c0de3f58564b9a4ec9480cbf4792b8e865cb3cfbce67ee4f19c9410206bfff2921afa9693f634d335ade358df115f04a87e6668d6927a0b95d137fbaf51
7
- data.tar.gz: 996e4ecd5e4b6ff4fc5be81ac349c5fde247781088a40907dfb368146578f57ec51c17947bdca1718289e93c7f35942c9ec4547583bb069497021402985b1dd7
6
+ metadata.gz: 1e5475f9dc0c8b844083619584376988a8d7dabe763fd2abe67929c472093a2bb5e2a6bbb4d42cc1400cdbb5a7c8d8532bc7f3733d44fc66f4cd07b63debe365
7
+ data.tar.gz: 5a214b983dd1c2fbf298ddbc74667cda767ee20fb29c735e2dfc1ca4a8e44f3f1df208412b518b76314ca0c12b0d59e90ba66b7f7b2ba96a6b1f1b1793ef1ab0
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schema
4
+ module Associations
5
+ # Schema::Associations::Base common class methods between associations
6
+ module Base
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ end
10
+
11
+ # no-doc
12
+ module ClassMethods
13
+ attr_accessor :schema_name
14
+
15
+ def base_schema_class=(kls)
16
+ @base_schema_class_name = kls.name
17
+ end
18
+
19
+ def base_schema_class
20
+ Object.const_get(@base_schema_class_name)
21
+ end
22
+
23
+ def schema_options
24
+ base_schema_class.schema[schema_name]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schema
4
+ module Associations
5
+ # Schema::Associations::DynamicTypes adds support for adding dynamic types to associations
6
+ module DynamicTypes
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ end
10
+
11
+ # no-doc
12
+ module ClassMethods
13
+ def add_type(type, options = {}, &block)
14
+ class_name = options[:class_name] || schema_dynamic_type_class_name(type)
15
+ kls = Class.new(self)
16
+ kls = base_schema_class.const_set(class_name, kls)
17
+ schema_add_dynamic_type(type, class_name)
18
+ kls.class_eval(&block) if block
19
+ kls
20
+ end
21
+
22
+ def dynamic_types
23
+ schema_options[:types]
24
+ end
25
+
26
+ def dynamic_type_names
27
+ dynamic_types.keys
28
+ end
29
+
30
+ private
31
+
32
+ def schema_dynamic_type_class_name(type)
33
+ ::Schema::Utils.classify_name(schema_name.to_s) +
34
+ 'AssociationType' +
35
+ ::Schema::Utils.classify_name(type.to_s)
36
+ end
37
+
38
+ def schema_add_dynamic_type(type, class_name)
39
+ new_schema_options = schema_options.dup
40
+ new_schema_options[:types] ||= {}
41
+ new_schema_options[:types][type] = class_name
42
+ base_schema_class.add_value_to_class_method(:schema, schema_name => new_schema_options)
43
+ new_schema_options
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -12,7 +12,7 @@ module Schema
12
12
  module ClassMethods
13
13
  # rubocop:disable Naming/PredicateName
14
14
  def has_many(name, options = {}, &block)
15
- options = ::Schema::Utils.add_association_class(self, name, :has_many, options, &block)
15
+ options = ::Schema::Utils.add_association_class(self, name, :has_many, options)
16
16
 
17
17
  class_eval(
18
18
  <<~STR, __FILE__, __LINE__ + 1
@@ -20,15 +20,19 @@ module Schema
20
20
  #{options[:instance_variable]}
21
21
  end
22
22
 
23
+ def #{name}_schema_creator
24
+ @#{name}_schema_creator ||= ::Schema::Associations::SchemaCreator.new(self, #{name.inspect})
25
+ end
26
+
23
27
  def #{options[:setter]}(v)
24
- if schemas = ::Schema::Utils.create_schemas(self, #{options[:class_name]}, #{name.inspect}, v)
25
- #{options[:instance_variable]} = schemas
26
- end
28
+ #{options[:instance_variable]} = #{name}_schema_creator.create_schemas(self, v)
27
29
  end
28
30
  STR
29
31
  )
30
32
 
31
- const_get(options[:class_name])
33
+ kls = const_get(options[:class_name])
34
+ kls.class_eval(&block) if block
35
+ kls
32
36
  end
33
37
  # rubocop:enable Naming/PredicateName
34
38
  end
@@ -12,7 +12,7 @@ module Schema
12
12
  module ClassMethods
13
13
  # rubocop:disable Naming/PredicateName
14
14
  def has_one(name, options = {}, &block)
15
- options = ::Schema::Utils.add_association_class(self, name, :has_one, options, &block)
15
+ options = ::Schema::Utils.add_association_class(self, name, :has_one, options)
16
16
 
17
17
  class_eval(
18
18
  <<~STR, __FILE__, __LINE__ + 1
@@ -20,15 +20,19 @@ module Schema
20
20
  #{options[:instance_variable]}
21
21
  end
22
22
 
23
+ def #{name}_schema_creator
24
+ @#{name}_schema_creator ||= ::Schema::Associations::SchemaCreator.new(self, #{name.inspect})
25
+ end
26
+
23
27
  def #{options[:setter]}(v)
24
- if schema = ::Schema::Utils.create_schema(self, #{options[:class_name]}, #{name.inspect}, v)
25
- #{options[:instance_variable]} = schema
26
- end
28
+ #{options[:instance_variable]} = #{name}_schema_creator.create_schema(self, v)
27
29
  end
28
30
  STR
29
31
  )
30
32
 
31
- const_get(options[:class_name])
33
+ kls = const_get(options[:class_name])
34
+ kls.class_eval(&block) if block
35
+ kls
32
36
  end
33
37
  # rubocop:enable Naming/PredicateName
34
38
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schema
4
+ module Associations
5
+ # Schema::Associations::SchemaCreator is used to create schema objects for associations
6
+ class SchemaCreator
7
+ def initialize(base_schema, name)
8
+ options = base_schema.class.schema[name]
9
+ @schema_name = name
10
+ @schema_class = base_schema.class.const_get(options[:class_name])
11
+ @aliases = options[:aliases] || []
12
+ @type_field = options[:type_field]
13
+ @external_type_field = options[:external_type_field]
14
+ @types = options[:types]
15
+ @ignorecase = !options[:type_ignorecase].nil?
16
+ end
17
+
18
+ def create_schema(base_schema, data, error_name = nil)
19
+ if data.is_a?(Hash)
20
+ unless (schema_class = get_schema_class(base_schema, data))
21
+ add_parsing_error(base_schema, error_name, :unknown)
22
+ return nil
23
+ end
24
+ schema = schema_class.from_hash(data)
25
+ add_parsing_error(base_schema, error_name, :invalid) unless schema.parsing_errors.empty?
26
+ schema
27
+ elsif !data.nil?
28
+ add_parsing_error(base_schema, error_name, :incompatable)
29
+ nil
30
+ end
31
+ end
32
+
33
+ def create_schemas(base_schema, list)
34
+ if list.is_a?(Array)
35
+ list.each_with_index.map { |data, idx| create_schema(base_schema, data, "#{@schema_name}:#{idx}") }
36
+ elsif !list.nil?
37
+ add_parsing_error(base_schema, @schema_name, :incompatable)
38
+ nil
39
+ end
40
+ end
41
+
42
+ def get_schema_class(base_schema, data)
43
+ if dynamic?
44
+ get_dynamic_schema_class(base_schema, data)
45
+ else
46
+ @schema_class
47
+ end
48
+ end
49
+
50
+ def dynamic?
51
+ !@type_field.nil? || !@external_type_field.nil?
52
+ end
53
+
54
+ def get_dynamic_schema_class(base_schema, data)
55
+ type = get_dynamic_type(base_schema, data)
56
+ type = type.to_s.downcase if @ignorecase
57
+ @types.each do |name, class_name|
58
+ name = name.downcase if @ignorecase
59
+ return base_schema.class.const_get(class_name) if name == type
60
+ end
61
+ nil
62
+ end
63
+
64
+ def get_dynamic_type(base_schema, data)
65
+ if @type_field
66
+ type_fields.each do |name|
67
+ type = data[name]
68
+ return type if type
69
+ end
70
+ nil
71
+ elsif @external_type_field
72
+ base_schema.public_send(@external_type_field)
73
+ end
74
+ end
75
+
76
+ def type_fields
77
+ @type_fields ||= [
78
+ @type_field,
79
+ @type_field.to_s
80
+ ] + @aliases
81
+ end
82
+
83
+ def add_parsing_error(base_schema, error_name, error_msg)
84
+ base_schema.parsing_errors.add(error_name || @schema_name, error_msg)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schema
4
+ module Parsers
5
+ # Schema::Parsers::Array adds the array type to schemas
6
+ module Array
7
+ def parse_array(field_name, parsing_errors, value)
8
+ case value
9
+ when ::Array
10
+ value
11
+ when String
12
+ schema_options = self.class.schema[field_name]
13
+ if (data = Array.parse_string_array(self, field_name, parsing_errors, value, schema_options))
14
+ return data
15
+ end
16
+
17
+ parsing_errors.add(field_name, :incompatable)
18
+ nil
19
+ else
20
+ parsing_errors.add(field_name, :incompatable)
21
+ nil
22
+ end
23
+ end
24
+
25
+ def self.parse_string_array(model, field_name, parsing_errors, value, schema_options)
26
+ return nil unless (separator = schema_options[:separator])
27
+
28
+ convert_array_values(model, field_name, parsing_errors, value.split(separator), schema_options)
29
+ end
30
+
31
+ def self.convert_array_values(model, field_name, parsing_errors, data, schema_options)
32
+ return data unless (data_type = schema_options[:data_type])
33
+
34
+ parser_method = "parse_#{data_type}"
35
+ data.each_with_index.map do |datum, idx|
36
+ model.send(parser_method, "#{field_name}:#{idx}", parsing_errors, datum)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -38,7 +38,7 @@ module Schema
38
38
  case value
39
39
  when String
40
40
  value
41
- when Hash, Array
41
+ when ::Hash, ::Array
42
42
  parsing_errors.add(field_name, :incompatable)
43
43
  nil
44
44
  when nil
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schema
4
+ module Parsers
5
+ # Schema::Parsers::Hash adds the hash type to schemas
6
+ module Hash
7
+ def parse_hash(field_name, parsing_errors, value)
8
+ case value
9
+ when ::Hash
10
+ value
11
+ else
12
+ parsing_errors.add(field_name, :incompatable)
13
+ nil
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+
4
+ module Schema
5
+ module Parsers
6
+ # Schema::Parsers::Json parse the string as json
7
+ module Json
8
+ def parse_json(field_name, parsing_errors, value)
9
+ case value
10
+ when String
11
+ begin
12
+ ::JSON.parse(value)
13
+ rescue ::JSON::ParserError
14
+ parsing_errors.add(field_name, :invalid)
15
+ nil
16
+ end
17
+ else
18
+ parsing_errors.add(field_name, :incompatable)
19
+ nil
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
data/lib/schema/utils.rb CHANGED
@@ -9,15 +9,11 @@ module Schema
9
9
  name.gsub(/[^\da-z_-]/, '').gsub(/(^.|[_|-].)/) { |m| m[-1].upcase }
10
10
  end
11
11
 
12
- def create_schema_class(base_schema_class, class_name, base_class, &block)
13
- kls = Class.new(base_class)
14
- base_schema_class.const_set(class_name, kls)
15
- kls = base_schema_class.const_get(class_name)
16
-
17
- include_schema_modules(kls, base_schema_class.schema_config) if base_class == Object
18
-
19
- kls.class_eval(&block) if block
20
-
12
+ def create_schema_class(base_schema_class, schema_name, options)
13
+ base_schema_class.add_value_to_class_method(:schema, schema_name => options)
14
+ kls = Class.new(options[:base_class] || Object)
15
+ kls = base_schema_class.const_set(options[:class_name], kls)
16
+ include_schema_modules(kls, base_schema_class.schema_config) unless options[:base_class]
21
17
  kls
22
18
  end
23
19
 
@@ -28,41 +24,33 @@ module Schema
28
24
  end
29
25
  end
30
26
 
31
- def create_schema(base_schema, schema_class, schema_name, data)
32
- if data.is_a?(Hash)
33
- schema = schema_class.from_hash(data)
34
- base_schema.parsing_errors.add(schema_name, :invalid) unless schema.parsing_errors.empty?
35
- schema
36
- elsif !data.nil?
37
- base_schema.parsing_errors.add(schema_name, :incompatable)
38
- nil
39
- end
27
+ def association_options(schema_name, schema_type, options)
28
+ options[:class_name] ||= 'Schema' + classify_name(schema_type.to_s) + classify_name(schema_name.to_s)
29
+ ::Schema::Model.default_attribute_options(schema_name, schema_type).merge(options)
40
30
  end
41
31
 
42
- def create_schemas(base_schema, schema_class, schema_name, list)
43
- if list.is_a?(Array)
44
- list.each_with_index.map { |data, idx| create_schema(base_schema, schema_class, "#{idx}:#{schema_name}", data) }
45
- elsif !list.nil?
46
- base_schema.parsing_errors.add(schema_name, :incompatable)
47
- nil
48
- end
32
+ def add_association_class(base_schema_class, schema_name, schema_type, options)
33
+ options = ::Schema::Utils.association_options(schema_name, schema_type, options)
34
+ kls = ::Schema::Utils.create_schema_class(
35
+ base_schema_class,
36
+ schema_name,
37
+ options
38
+ )
39
+ add_association_defaults(kls, base_schema_class, schema_name)
40
+ add_association_dynamic_types(kls, options)
41
+ options
49
42
  end
50
43
 
51
- def association_options(name, type, options)
52
- options[:class_name] ||= 'Schema' + classify_name(type.to_s) + classify_name(name.to_s)
53
- ::Schema::Model.default_attribute_options(name, type).merge(options)
44
+ def add_association_defaults(kls, base_schema_class, schema_name)
45
+ kls.include ::Schema::Associations::Base
46
+ kls.base_schema_class = base_schema_class
47
+ kls.schema_name = schema_name
54
48
  end
55
49
 
56
- def add_association_class(base_schema_class, name, type, options, &block)
57
- options = ::Schema::Utils.association_options(name, type, options)
58
- ::Schema::Utils.create_schema_class(
59
- base_schema_class,
60
- options[:class_name],
61
- options[:base_class] || Object,
62
- &block
63
- )
64
- base_schema_class.add_value_to_class_method(:schema, name => options)
65
- options
50
+ def add_association_dynamic_types(kls, options)
51
+ return if !options[:type_field] && !options[:external_type_field]
52
+
53
+ kls.include ::Schema::Associations::DynamicTypes
66
54
  end
67
55
  end
68
56
  end
data/lib/schema.rb CHANGED
@@ -12,12 +12,18 @@ module Schema
12
12
 
13
13
  # Schema::Parsers are used to convert values into the correct data type
14
14
  module Parsers
15
+ autoload :Array, 'schema/parsers/array'
15
16
  autoload :Common, 'schema/parsers/common'
17
+ autoload :Hash, 'schema/parsers/hash'
18
+ autoload :Json, 'schema/parsers/json'
16
19
  end
17
20
 
18
21
  # Schema::Associations mange the associations between schema models
19
22
  module Associations
23
+ autoload :Base, 'schema/associations/base'
24
+ autoload :DynamicTypes, 'schema/associations/dynamic_types'
20
25
  autoload :HasMany, 'schema/associations/has_many'
21
26
  autoload :HasOne, 'schema/associations/has_one'
27
+ autoload :SchemaCreator, 'schema/associations/schema_creator'
22
28
  end
23
29
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SchemaValidator validates nested schemas
4
+ class SchemaValidator < ActiveModel::EachValidator
5
+ def validate_each(record, attribute, value)
6
+ record.errors.add(attribute, options.fetch(:message, :invalid)) if value && !value.valid?
7
+ end
8
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schema-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doug Youch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-29 00:00:00.000000000 Z
11
+ date: 2019-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inheritance-helper
@@ -33,13 +33,20 @@ files:
33
33
  - lib/schema.rb
34
34
  - lib/schema/array_headers.rb
35
35
  - lib/schema/arrays.rb
36
+ - lib/schema/associations/base.rb
37
+ - lib/schema/associations/dynamic_types.rb
36
38
  - lib/schema/associations/has_many.rb
37
39
  - lib/schema/associations/has_one.rb
40
+ - lib/schema/associations/schema_creator.rb
38
41
  - lib/schema/csv_parser.rb
39
42
  - lib/schema/errors.rb
40
43
  - lib/schema/model.rb
44
+ - lib/schema/parsers/array.rb
41
45
  - lib/schema/parsers/common.rb
46
+ - lib/schema/parsers/hash.rb
47
+ - lib/schema/parsers/json.rb
42
48
  - lib/schema/utils.rb
49
+ - lib/schema_validator.rb
43
50
  homepage: https://github.com/dougyouch/schema
44
51
  licenses:
45
52
  - MIT
@@ -59,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
66
  - !ruby/object:Gem::Version
60
67
  version: '0'
61
68
  requirements: []
62
- rubygems_version: 3.0.3
69
+ rubygems_version: 3.0.4
63
70
  signing_key:
64
71
  specification_version: 4
65
72
  summary: Schema Model