schema-model 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|