schema-model 0.1.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 +7 -0
- data/lib/schema/errors.rb +25 -0
- data/lib/schema/model.rb +119 -0
- data/lib/schema/parsers/common.rb +129 -0
- data/lib/schema/relation/has_many.rb +37 -0
- data/lib/schema/relation/has_one.rb +37 -0
- data/lib/schema/utils.rb +55 -0
- data/lib/schema.rb +14 -0
- metadata +62 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3dbf6656e98c4b1fb37ef7efcc4495c3fc56f39dcfd87c3e38f5caf4b3607670
|
4
|
+
data.tar.gz: 32583217eae45307d91662e063e447a8dcd7dbd709c60fa6757bb12a428ebfc7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b23a4e93ca896b22d5bba542b5e530046cd74e0b4cab5e8b11c217efadb1f20bc87a6111bc048c7f8b62905838eee8060a27fc8847f2b4c2533b3321eb68fa7b
|
7
|
+
data.tar.gz: 0dc15eefe3675480d601ea8f4d04282598b6bcdd52d6b0d558d95978b1a70bfb7445c0b0c125ccdbc424806fde06c7d8dea6e3297a4ecfc55d35786425333695
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Schema
|
2
|
+
class Errors
|
3
|
+
attr_reader :errors
|
4
|
+
|
5
|
+
EMPTY_ARRAY = [].freeze
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@errors = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](name)
|
12
|
+
@errors[name] || EMPTY_ARRAY
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(name, error)
|
16
|
+
@errors[name] ||= []
|
17
|
+
@errors[name] << error
|
18
|
+
end
|
19
|
+
alias []= add
|
20
|
+
|
21
|
+
def empty?
|
22
|
+
@errors.empty?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/schema/model.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'inheritance-helper'
|
2
|
+
|
3
|
+
module Schema
|
4
|
+
module Model
|
5
|
+
def self.included base
|
6
|
+
base.extend InheritanceHelper::Methods
|
7
|
+
base.include Schema::Parsers::Common
|
8
|
+
base.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.default_attribute_options(name, type)
|
12
|
+
{
|
13
|
+
key: name.to_s.freeze,
|
14
|
+
name: name,
|
15
|
+
type: type,
|
16
|
+
getter: name.to_s.freeze,
|
17
|
+
setter: "#{name}=".freeze,
|
18
|
+
instance_variable: "@#{name}".freeze,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def schema
|
24
|
+
{}.freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
def schema_config
|
28
|
+
{
|
29
|
+
schema_includes: []
|
30
|
+
}.freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
def attribute(name, type, options={})
|
34
|
+
if options.has_key?(:alias)
|
35
|
+
options[:aliases] = [options[:alias]]
|
36
|
+
end
|
37
|
+
|
38
|
+
options = ::Schema::Model.default_attribute_options(name, type)
|
39
|
+
.merge(
|
40
|
+
parser: "parse_#{type}".freeze
|
41
|
+
).merge(options)
|
42
|
+
|
43
|
+
add_value_to_class_method(:schema, name => options)
|
44
|
+
|
45
|
+
class_eval(<<-STR
|
46
|
+
def #{options[:getter]}
|
47
|
+
#{options[:instance_variable]}
|
48
|
+
end
|
49
|
+
|
50
|
+
def #{options[:setter]}(v)
|
51
|
+
#{options[:instance_variable]} = #{options[:parser]}(#{name.inspect}, parsing_errors, v)
|
52
|
+
end
|
53
|
+
STR
|
54
|
+
)
|
55
|
+
|
56
|
+
if options[:aliases]
|
57
|
+
options[:aliases].each do |alias_name|
|
58
|
+
add_value_to_class_method(:schema, alias_name.to_sym => options.merge(key: alias_name.to_s, alias_of: name))
|
59
|
+
alias_method(alias_name, options[:getter])
|
60
|
+
alias_method("#{alias_name}=", options[:setter])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def from_hash(data)
|
66
|
+
new.update_attributes(data)
|
67
|
+
end
|
68
|
+
|
69
|
+
def schema_include(mod)
|
70
|
+
config = schema_config.dup
|
71
|
+
config[:schema_includes] = config[:schema_includes] + [mod]
|
72
|
+
redefine_class_method(:schema_config, config.freeze)
|
73
|
+
include mod
|
74
|
+
end
|
75
|
+
|
76
|
+
def schema_base_class=(kls)
|
77
|
+
config = schema_config.dup
|
78
|
+
config[:schema_base_class] = kls
|
79
|
+
redefine_class_method(:schema_config, config.freeze)
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_schema_base_class_to_superclass
|
83
|
+
self.schema_base_class = superclass
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_attributes(data)
|
88
|
+
self.class.schema.each do |field_name, field_options|
|
89
|
+
next if ! data.has_key?(field_options[:key]) && ! data.has_key?(field_name)
|
90
|
+
|
91
|
+
public_send(
|
92
|
+
field_options[:setter],
|
93
|
+
data[field_options[:key]] || data[field_name.to_sym]
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def as_json(opts={})
|
101
|
+
self.class.schema.inject({}) do |memo, (field_name, field_options)|
|
102
|
+
unless field_options[:alias_of]
|
103
|
+
value = public_send(field_options[:getter])
|
104
|
+
memo[field_name] = value if ! value.nil? || opts[:include_nils]
|
105
|
+
end
|
106
|
+
memo
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_hash
|
111
|
+
as_json(include_nils: true)
|
112
|
+
end
|
113
|
+
alias to_h to_hash
|
114
|
+
|
115
|
+
def parsing_errors
|
116
|
+
@parsing_errors ||= ::Schema::Errors.new
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Schema
|
4
|
+
module Parsers
|
5
|
+
module Common
|
6
|
+
INTEGER_REGEX = /^(?:[1-9]\d*|0)$/
|
7
|
+
FLOAT_REGEX = /^(?:[1-9]\d*|0)(?:\.\d+)?$/
|
8
|
+
BOOLEAN_REGEX = /^(?:1|t|true|on|y|yes)$/i
|
9
|
+
|
10
|
+
def parse_integer(field_name, parsing_errors, value)
|
11
|
+
case value
|
12
|
+
when Integer
|
13
|
+
value
|
14
|
+
when String
|
15
|
+
if INTEGER_REGEX.match?(value)
|
16
|
+
Integer(value)
|
17
|
+
else
|
18
|
+
parsing_errors.add(field_name, :invalid)
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
when Float
|
22
|
+
if (value % 1) > 0.0
|
23
|
+
parsing_errors.add(field_name, :incompatable)
|
24
|
+
end
|
25
|
+
value.to_i
|
26
|
+
when nil
|
27
|
+
nil
|
28
|
+
else
|
29
|
+
parsing_errors.add(field_name, :unhandled_type)
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_string(field_name, parsing_errors, value)
|
35
|
+
case value
|
36
|
+
when String
|
37
|
+
value
|
38
|
+
when Hash, Array
|
39
|
+
parsing_errors.add(field_name, :incompatable)
|
40
|
+
nil
|
41
|
+
when nil
|
42
|
+
nil
|
43
|
+
else
|
44
|
+
String(value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_float(field_name, parsing_errors, value)
|
49
|
+
case value
|
50
|
+
when Float
|
51
|
+
value
|
52
|
+
when Integer
|
53
|
+
value.to_f
|
54
|
+
when String
|
55
|
+
if FLOAT_REGEX.match?(value)
|
56
|
+
Float(value)
|
57
|
+
else
|
58
|
+
parsing_errors.add(field_name, :invalid)
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
when nil
|
62
|
+
nil
|
63
|
+
else
|
64
|
+
parsing_errors.add(field_name, :unhandled_type)
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_time(field_name, parsing_errors, value)
|
70
|
+
case value
|
71
|
+
when Time
|
72
|
+
value
|
73
|
+
when Date
|
74
|
+
value.to_time
|
75
|
+
when String
|
76
|
+
begin
|
77
|
+
Time.xmlschema(value)
|
78
|
+
rescue ArgumentError
|
79
|
+
parsing_errors.add(field_name, :invalid)
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
when nil
|
83
|
+
nil
|
84
|
+
else
|
85
|
+
parsing_errors.add(field_name, :unhandled_type)
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_date(field_name, parsing_errors, value)
|
91
|
+
case value
|
92
|
+
when Date
|
93
|
+
value
|
94
|
+
when Time
|
95
|
+
value.to_date
|
96
|
+
when String
|
97
|
+
begin
|
98
|
+
Date.parse(value)
|
99
|
+
rescue ArgumentError
|
100
|
+
parsing_errors.add(field_name, :invalid)
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
when nil
|
104
|
+
nil
|
105
|
+
else
|
106
|
+
parsing_errors.add(field_name, :unhandled_type)
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_boolean(field_name, parsing_errors, value)
|
112
|
+
case value
|
113
|
+
when TrueClass, FalseClass
|
114
|
+
value
|
115
|
+
when Integer, Float
|
116
|
+
value != 0
|
117
|
+
when String
|
118
|
+
BOOLEAN_REGEX.match?(value)
|
119
|
+
when nil
|
120
|
+
nil
|
121
|
+
else
|
122
|
+
parsing_errors.add(field_name, :unhandled_type)
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Schema
|
2
|
+
module Relation
|
3
|
+
module HasMany
|
4
|
+
def self.included base
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def has_many(name, options={}, &block)
|
10
|
+
_, class_name = ::Schema::Utils.create_schema_class(self, 'SchemaHasMany', name, &block)
|
11
|
+
|
12
|
+
options = ::Schema::Model.default_attribute_options(name, :has_many)
|
13
|
+
.merge(
|
14
|
+
class_name: class_name
|
15
|
+
).merge(options)
|
16
|
+
|
17
|
+
add_value_to_class_method(:schema, name => options)
|
18
|
+
|
19
|
+
class_eval(<<-STR
|
20
|
+
def #{options[:getter]}
|
21
|
+
#{options[:instance_variable]}
|
22
|
+
end
|
23
|
+
|
24
|
+
def #{options[:setter]}(v)
|
25
|
+
if schemas = ::Schema::Utils.create_schemas(self, #{options[:class_name]}, #{name.inspect}, v)
|
26
|
+
#{options[:instance_variable]} = schemas
|
27
|
+
end
|
28
|
+
end
|
29
|
+
STR
|
30
|
+
)
|
31
|
+
|
32
|
+
const_get(class_name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Schema
|
2
|
+
module Relation
|
3
|
+
module HasOne
|
4
|
+
def self.included base
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def has_one(name, options={}, &block)
|
10
|
+
_, class_name = ::Schema::Utils.create_schema_class(self, 'SchemaHasOne', name, &block)
|
11
|
+
|
12
|
+
options = ::Schema::Model.default_attribute_options(name, :has_one)
|
13
|
+
.merge(
|
14
|
+
class_name: class_name
|
15
|
+
).merge(options)
|
16
|
+
|
17
|
+
add_value_to_class_method(:schema, name => options)
|
18
|
+
|
19
|
+
class_eval(<<-STR
|
20
|
+
def #{options[:getter]}
|
21
|
+
#{options[:instance_variable]}
|
22
|
+
end
|
23
|
+
|
24
|
+
def #{options[:setter]}(v)
|
25
|
+
if schema = ::Schema::Utils.create_schema(self, #{options[:class_name]}, #{name.inspect}, v)
|
26
|
+
#{options[:instance_variable]} = schema
|
27
|
+
end
|
28
|
+
end
|
29
|
+
STR
|
30
|
+
)
|
31
|
+
|
32
|
+
const_get(class_name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/schema/utils.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Schema
|
2
|
+
module Utils
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def classify_name(prefix, name)
|
6
|
+
prefix + name.gsub(/[^\da-z_-]/, '').gsub(/(^.|[_|-].)/) { |m| m[-1].upcase }
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_schema_class(base_schema_class, class_name_prefix, name, &block)
|
10
|
+
schema_config = base_schema_class.schema_config
|
11
|
+
kls = Class.new(schema_config[:schema_base_class] || Object) do
|
12
|
+
include ::Schema::Model
|
13
|
+
end
|
14
|
+
|
15
|
+
class_name = classify_name(class_name_prefix, name.to_s)
|
16
|
+
base_schema_class.const_set(class_name, kls)
|
17
|
+
kls = base_schema_class.const_get(class_name)
|
18
|
+
|
19
|
+
# make sure additional schema classes use this base class
|
20
|
+
kls.schema_base_class = schema_config[:schema_base_class] if schema_config[:schema_base_class]
|
21
|
+
|
22
|
+
schema_config[:schema_includes].each do |mod|
|
23
|
+
kls.schema_include(mod)
|
24
|
+
end
|
25
|
+
|
26
|
+
kls.class_eval(&block)
|
27
|
+
|
28
|
+
return kls, class_name
|
29
|
+
end
|
30
|
+
|
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
|
+
else
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_schemas(base_schema, schema_class, schema_name, list)
|
45
|
+
if list.is_a?(Array)
|
46
|
+
list.each_with_index.map { |data, idx| create_schema(base_schema, schema_class, "#{idx}:#{schema_name}", data) }
|
47
|
+
elsif ! list.nil?
|
48
|
+
base_schema.parsing_errors.add(schema_name, :incompatable)
|
49
|
+
nil
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/schema.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Schema
|
2
|
+
autoload :Errors, 'schema/errors'
|
3
|
+
autoload :Model, 'schema/model'
|
4
|
+
autoload :Utils, 'schema/utils'
|
5
|
+
|
6
|
+
module Parsers
|
7
|
+
autoload :Common, 'schema/parsers/common'
|
8
|
+
end
|
9
|
+
|
10
|
+
module Relation
|
11
|
+
autoload :HasMany, 'schema/relation/has_many'
|
12
|
+
autoload :HasOne, 'schema/relation/has_one'
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: schema-model
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Doug Youch
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-06-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: inheritance-helper
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Easy way to create models from payloads
|
28
|
+
email: dougyouch@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/schema.rb
|
34
|
+
- lib/schema/errors.rb
|
35
|
+
- lib/schema/model.rb
|
36
|
+
- lib/schema/parsers/common.rb
|
37
|
+
- lib/schema/relation/has_many.rb
|
38
|
+
- lib/schema/relation/has_one.rb
|
39
|
+
- lib/schema/utils.rb
|
40
|
+
homepage: https://github.com/dougyouch/schema
|
41
|
+
licenses: []
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubygems_version: 3.0.3
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Schema Model
|
62
|
+
test_files: []
|