schema-model 0.2.0 → 0.3.0

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