trax_model 0.0.91 → 0.0.92
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/Gemfile +1 -1
- data/lib/trax/model.rb +71 -12
- data/lib/trax/model/attributes.rb +40 -0
- data/lib/trax/model/attributes/definitions.rb +19 -0
- data/lib/trax/model/attributes/errors.rb +15 -0
- data/lib/trax/model/attributes/mixin.rb +43 -0
- data/lib/trax/model/attributes/type.rb +11 -0
- data/lib/trax/model/attributes/types.rb +11 -0
- data/lib/trax/model/attributes/types/array.rb +92 -0
- data/lib/trax/model/attributes/types/enum.rb +32 -0
- data/lib/trax/model/attributes/types/json.rb +76 -0
- data/lib/trax/model/attributes/types/uuid_array.rb +83 -0
- data/lib/trax/model/attributes/value.rb +9 -0
- data/lib/trax/model/config.rb +1 -9
- data/lib/trax/model/enum.rb +3 -3
- data/lib/trax/model/errors.rb +30 -19
- data/lib/trax/model/freezable.rb +1 -1
- data/lib/trax/model/mixin.rb +16 -4
- data/lib/trax/model/railtie.rb +17 -0
- data/lib/trax/model/registry.rb +3 -3
- data/lib/trax/model/restorable.rb +26 -17
- data/lib/trax/model/struct.rb +31 -0
- data/lib/trax/model/unique_id.rb +56 -14
- data/lib/trax/model/uuid_array.rb +59 -0
- data/lib/trax/model/validators.rb +2 -0
- data/lib/trax/validators/boolean_validator.rb +14 -0
- data/lib/trax/validators/email_validator.rb +6 -2
- data/lib/trax/validators/enum_validator.rb +16 -0
- data/lib/trax/validators/json_attribute_validator.rb +19 -0
- data/lib/trax_model/version.rb +1 -1
- data/spec/support/schema.rb +26 -10
- data/spec/trax/model/struct_spec.rb +16 -0
- data/spec/trax/model_spec.rb +2 -4
- data/trax_model.gemspec +1 -0
- metadata +35 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d24f88faa95bde183c2a7046d8c1d86b604987bd
|
4
|
+
data.tar.gz: 4c124fcd2c0f21166cfc7012b75979af8551d4b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5cac8b9cf60e27ab5c532ba6dc7a778319440392240cb38083676cdcaa10466446e8981fac14bca8397132ecc88f006fd551da8e040116a653e5897049cf23c2
|
7
|
+
data.tar.gz: 89309b58c39783f84f322664750a05214b9522a7d7be892bcc0a00b269077563404c4622a5c549e57d4fdedd64fe5e7be949266e774279676118b234db2085d2
|
data/Gemfile
CHANGED
data/lib/trax/model.rb
CHANGED
@@ -4,10 +4,12 @@ require 'hashie/dash'
|
|
4
4
|
require 'hashie/mash'
|
5
5
|
require 'simple_enum'
|
6
6
|
require_relative './string'
|
7
|
-
|
7
|
+
require_relative './validators/boolean_validator'
|
8
8
|
require_relative './validators/email_validator'
|
9
|
+
require_relative './validators/enum_validator'
|
9
10
|
require_relative './validators/frozen_validator'
|
10
11
|
require_relative './validators/future_validator'
|
12
|
+
require_relative './validators/json_attribute_validator'
|
11
13
|
require_relative './validators/subdomain_validator'
|
12
14
|
require_relative './validators/url_validator'
|
13
15
|
|
@@ -16,6 +18,7 @@ module Trax
|
|
16
18
|
extend ::ActiveSupport::Concern
|
17
19
|
extend ::ActiveSupport::Autoload
|
18
20
|
|
21
|
+
autoload :Attributes
|
19
22
|
autoload :Config
|
20
23
|
autoload :Enum
|
21
24
|
autoload :Errors
|
@@ -28,24 +31,38 @@ module Trax
|
|
28
31
|
autoload :Mixin
|
29
32
|
autoload :MTI
|
30
33
|
autoload :Restorable
|
34
|
+
autoload :Railtie
|
31
35
|
autoload :STI
|
36
|
+
autoload :Struct
|
32
37
|
autoload :Validators
|
33
38
|
|
34
39
|
include ::Trax::Model::Matchable
|
35
40
|
include ::ActiveModel::Dirty
|
36
41
|
|
42
|
+
define_configuration_options! do
|
43
|
+
option :auto_include, :default => false
|
44
|
+
option :auto_include_mixins, :default => []
|
45
|
+
end
|
46
|
+
|
37
47
|
class << self
|
38
48
|
attr_accessor :mixin_registry
|
39
49
|
end
|
40
50
|
|
41
51
|
@mixin_registry = {}
|
42
52
|
|
43
|
-
def self.register_mixin(mixin_klass)
|
44
|
-
mixin_key = mixin_klass.name.demodulize.underscore.to_sym
|
53
|
+
def self.register_mixin(mixin_klass, key = nil)
|
54
|
+
mixin_key = mixin_klass.respond_to?(:mixin_registry_key) ? mixin_klass.mixin_registry_key : (key || mixin_klass.name.demodulize.underscore.to_sym)
|
55
|
+
|
56
|
+
return if mixin_registry.key?(mixin_key)
|
45
57
|
mixin_registry[mixin_key] = mixin_klass
|
46
58
|
end
|
47
59
|
|
60
|
+
def self.root
|
61
|
+
::Pathname.new(::File.path(__FILE__))
|
62
|
+
end
|
63
|
+
|
48
64
|
def self.eager_autoload_mixins!
|
65
|
+
::Trax::Model::Attributes::Mixin
|
49
66
|
::Trax::Model::Enum
|
50
67
|
::Trax::Model::Freezable
|
51
68
|
::Trax::Model::Restorable
|
@@ -55,9 +72,9 @@ module Trax
|
|
55
72
|
eager_autoload_mixins!
|
56
73
|
|
57
74
|
included do
|
58
|
-
class_attribute :
|
75
|
+
class_attribute :registered_mixins
|
59
76
|
|
60
|
-
self.
|
77
|
+
self.registered_mixins = {}
|
61
78
|
|
62
79
|
register_trax_models(self)
|
63
80
|
end
|
@@ -66,19 +83,57 @@ module Trax
|
|
66
83
|
delegate :register_trax_model, :to => "::Trax::Model::Registry"
|
67
84
|
delegate :[], :to => :find
|
68
85
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
86
|
+
def after_inherited(&block)
|
87
|
+
instance_variable_set(:@_after_inherited_block, block)
|
88
|
+
end
|
89
|
+
|
90
|
+
#the tracepoint stuff is to ensure that we call the after_inherited block not
|
91
|
+
#right after the class is defined, but rather, after the class is defined and
|
92
|
+
#evaluated. i.e. this allows us to do stuff like set class attributes
|
93
|
+
# class_attribute :messages_class
|
94
|
+
#
|
95
|
+
# after_inherited do
|
96
|
+
# has_many :computed_subtype_messages, :class_name => message_class
|
97
|
+
# end
|
98
|
+
# - Then in subklass
|
99
|
+
# self.messages_class = "MySubtypeSpecifcModel"
|
100
|
+
|
101
|
+
def inherited(subklass)
|
102
|
+
super(subklass)
|
103
|
+
|
104
|
+
if self.instance_variable_defined?(:@_after_inherited_block)
|
105
|
+
trace = ::TracePoint.new(:end) do |tracepoint|
|
106
|
+
if tracepoint.self == subklass
|
107
|
+
trace.disable
|
108
|
+
|
109
|
+
subklass.instance_eval(&self.instance_variable_get(:@_after_inherited_block))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
trace.enable
|
72
114
|
end
|
73
115
|
end
|
74
116
|
|
75
117
|
def mixin(key, options = {})
|
76
|
-
|
118
|
+
raise ::Trax::Model::Errors::MixinNotRegistered.new(
|
119
|
+
model: self.name,
|
120
|
+
mixin: key
|
121
|
+
) unless ::Trax::Model.mixin_registry.key?(key)
|
122
|
+
|
123
|
+
mixin_module = ::Trax::Model.mixin_registry[key]
|
124
|
+
self.registered_mixins[key] = mixin_module
|
77
125
|
|
78
126
|
self.class_eval do
|
79
|
-
unless self.ancestors.include?(
|
80
|
-
|
81
|
-
|
127
|
+
include(mixin_module) unless self.ancestors.include?(mixin_module)
|
128
|
+
|
129
|
+
options = {} if options.is_a?(TrueClass)
|
130
|
+
options = { options => true } if options.is_a?(Symbol)
|
131
|
+
mixin_module.apply_mixin(self, options) if mixin_module.respond_to?(:apply_mixin)
|
132
|
+
|
133
|
+
if mixin_module.instance_variable_defined?(:@_after_included_block)
|
134
|
+
block = mixin_module.instance_variable_get(:@_after_included_block)
|
135
|
+
|
136
|
+
instance_exec(options, &block)
|
82
137
|
end
|
83
138
|
end
|
84
139
|
end
|
@@ -105,5 +160,9 @@ module Trax
|
|
105
160
|
name.underscore
|
106
161
|
end
|
107
162
|
end
|
163
|
+
|
164
|
+
::ActiveSupport.run_load_hooks(:trax_model, self)
|
108
165
|
end
|
109
166
|
end
|
167
|
+
|
168
|
+
::Trax::Model::Railtie if defined?(Rails)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'hashie/extensions/ignore_undeclared'
|
2
|
+
|
3
|
+
module Trax
|
4
|
+
module Model
|
5
|
+
module Attributes
|
6
|
+
extend ::ActiveSupport::Autoload
|
7
|
+
|
8
|
+
autoload :Mixin
|
9
|
+
autoload :Definitions
|
10
|
+
autoload :Errors
|
11
|
+
autoload :Types
|
12
|
+
autoload :Type
|
13
|
+
autoload :Value
|
14
|
+
|
15
|
+
define_configuration_options! do
|
16
|
+
option :attribute_types, :default => {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.register_attribute_type(mod)
|
20
|
+
key = mod.name.demodulize.underscore.split('_')[0].to_sym
|
21
|
+
|
22
|
+
config.attribute_types[key] = mod
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.[](key)
|
26
|
+
config.attribute_types[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.key?(type)
|
30
|
+
config.attribute_types.has_key?(type)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.eager_autoload_types!
|
34
|
+
::Trax::Model::Attributes::Types
|
35
|
+
end
|
36
|
+
|
37
|
+
eager_autoload_types!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Attributes
|
4
|
+
class Definitions < ::SimpleDelegator
|
5
|
+
def initialize(model)
|
6
|
+
@model = model
|
7
|
+
end
|
8
|
+
|
9
|
+
def __getobj__
|
10
|
+
@model
|
11
|
+
end
|
12
|
+
|
13
|
+
def attribute(*args, type:, **options, &block)
|
14
|
+
@model.trax_attribute(*args, type: type, **options, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Attributes
|
4
|
+
module Errors
|
5
|
+
class UnknownAttributeType < ::Trax::Core::Errors::Base
|
6
|
+
argument :type, :required => true
|
7
|
+
|
8
|
+
message {
|
9
|
+
"#{type} is an unknown trax model attribute type"
|
10
|
+
}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Attributes
|
4
|
+
module Mixin
|
5
|
+
def self.mixin_registry_key; :attributes end;
|
6
|
+
|
7
|
+
extend ::Trax::Model::Mixin
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :trax_attribute_fields
|
11
|
+
|
12
|
+
self.trax_attribute_fields = ::ActiveSupport::HashWithIndifferentAccess.new
|
13
|
+
|
14
|
+
::Trax::Model::Attributes.config.attribute_types.each_pair do |key, mod|
|
15
|
+
include mod::Mixin
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
# so we can keep all our definitions in same place, and largely so we
|
21
|
+
# can use attribute method to define methods
|
22
|
+
#probably overkill but..
|
23
|
+
def define_attributes(&block)
|
24
|
+
model_klass_proxy = ::Trax::Model::Attributes::Definitions.new(self)
|
25
|
+
model_klass_proxy.instance_eval(&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def trax_attribute(name, type:, **options, &block)
|
29
|
+
trax_attribute_fields[type] ||= {}
|
30
|
+
|
31
|
+
raise ::Trax::Model::Attributes::Errors::UnknownAttributeType.new(type: type) unless ::Trax::Model::Attributes.key?(type)
|
32
|
+
attribute_type_definition_method = ::Trax::Model::Attributes[type]::Mixin::ClassMethods.instance_methods.first
|
33
|
+
|
34
|
+
self.send(attribute_type_definition_method, name, **options, &block)
|
35
|
+
|
36
|
+
self.validates(name, options[:validates]) if options.key?(:validates)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'hashie/extensions/ignore_undeclared'
|
2
|
+
#totally not working atm :), dont use me!
|
3
|
+
module Trax
|
4
|
+
module Model
|
5
|
+
module Attributes
|
6
|
+
module Types
|
7
|
+
class Array < ::Trax::Model::Attributes::Type
|
8
|
+
class Value < ::Trax::Model::Attributes::Value
|
9
|
+
class_attribute :element_class
|
10
|
+
include ::Enumerable
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
@array = super(*args)
|
14
|
+
@array.map!{ |ele| self.class.element_class.new(ele) } if self.class.element_class && @array.any?
|
15
|
+
end
|
16
|
+
|
17
|
+
def __getobj__
|
18
|
+
@array
|
19
|
+
end
|
20
|
+
|
21
|
+
def <<(val)
|
22
|
+
if self.class.element_class && val.class == self.class.element_class
|
23
|
+
super(val)
|
24
|
+
else
|
25
|
+
super(self.class.element_class.new(val))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
yield __getobj__.each(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class TypeCaster < ActiveRecord::Type::Value
|
35
|
+
include ::ActiveRecord::Type::Mutable
|
36
|
+
|
37
|
+
def initialize(*args, target_klass:)
|
38
|
+
super(*args)
|
39
|
+
|
40
|
+
@target_klass = target_klass
|
41
|
+
end
|
42
|
+
|
43
|
+
def type
|
44
|
+
:array
|
45
|
+
end
|
46
|
+
|
47
|
+
def type_cast_from_user(value)
|
48
|
+
value.is_a?(@target_klass) ? @target_klass : @target_klass.new(value || {})
|
49
|
+
end
|
50
|
+
|
51
|
+
def type_cast_from_database(value)
|
52
|
+
value.present? ? @target_klass.new(JSON.parse(value)) : value
|
53
|
+
end
|
54
|
+
|
55
|
+
def type_cast_for_database(value)
|
56
|
+
if value.present?
|
57
|
+
value.map{ |element| element.try(:to_json) }
|
58
|
+
else
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module Mixin
|
65
|
+
def self.mixin_registry_key; :array_attributes end;
|
66
|
+
|
67
|
+
extend ::Trax::Model::Mixin
|
68
|
+
include ::Trax::Model::Attributes::Mixin
|
69
|
+
|
70
|
+
module ClassMethods
|
71
|
+
def array_attribute(attribute_name, **options, &block)
|
72
|
+
attributes_klass_name = "#{attribute_name}_attributes".classify
|
73
|
+
attributes_klass = const_set(attributes_klass_name, ::Class.new(::Trax::Model::Attributes[:array]::Value))
|
74
|
+
attributes_klass.instance_eval(&block)
|
75
|
+
|
76
|
+
attributes_klass.element_class = options[:of] if options.has_key?(:of)
|
77
|
+
|
78
|
+
trax_attribute_fields[:array] ||= {}
|
79
|
+
trax_attribute_fields[:array][attribute_name] = attributes_klass
|
80
|
+
|
81
|
+
attribute(attribute_name, ::Trax::Model::Attributes[:array]::TypeCaster.new(target_klass: attributes_klass))
|
82
|
+
|
83
|
+
# self.default_value_for(attribute_name) { self.class.element_class.new }
|
84
|
+
# self.validates(attribute_name, :json_attribute => true)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'hashie/extensions/ignore_undeclared'
|
2
|
+
|
3
|
+
module Trax
|
4
|
+
module Model
|
5
|
+
module Attributes
|
6
|
+
module Types
|
7
|
+
class Enum < ::Trax::Model::Attributes::Type
|
8
|
+
class TypeCaster < ActiveRecord::Type::Integer
|
9
|
+
end
|
10
|
+
|
11
|
+
module Mixin
|
12
|
+
def self.mixin_registry_key; :enum_attributes end;
|
13
|
+
|
14
|
+
extend ::Trax::Model::Mixin
|
15
|
+
include ::Trax::Model::Attributes::Mixin
|
16
|
+
include ::Trax::Model::Enum
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def enum_attribute(attribute_name, values:, **options, &block)
|
20
|
+
attribute(attribute_name, ::Trax::Model::Attributes[:enum]::TypeCaster.new)
|
21
|
+
|
22
|
+
options.delete(:validates) if options.key?(:validates)
|
23
|
+
|
24
|
+
as_enum(attribute_name, values, **options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'hashie/extensions/ignore_undeclared'
|
2
|
+
|
3
|
+
module Trax
|
4
|
+
module Model
|
5
|
+
module Attributes
|
6
|
+
module Types
|
7
|
+
class Json < ::Trax::Model::Attributes::Type
|
8
|
+
class Value < ::Trax::Model::Struct
|
9
|
+
def self.permitted_keys
|
10
|
+
@permitted_keys ||= properties.map(&:to_sym)
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
self.to_hash.inspect
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class TypeCaster < ActiveRecord::Type::Value
|
19
|
+
include ::ActiveRecord::Type::Mutable
|
20
|
+
|
21
|
+
def initialize(*args, target_klass:)
|
22
|
+
super(*args)
|
23
|
+
|
24
|
+
@target_klass = target_klass
|
25
|
+
end
|
26
|
+
|
27
|
+
def type
|
28
|
+
:json
|
29
|
+
end
|
30
|
+
|
31
|
+
def type_cast_from_user(value)
|
32
|
+
value.is_a?(@target_klass) ? @target_klass : @target_klass.new(value || {})
|
33
|
+
end
|
34
|
+
|
35
|
+
def type_cast_from_database(value)
|
36
|
+
value.present? ? @target_klass.new(JSON.parse(value)) : value
|
37
|
+
end
|
38
|
+
|
39
|
+
def type_cast_for_database(value)
|
40
|
+
value.present? ? value.to_hash.to_json : nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module Mixin
|
45
|
+
def self.mixin_registry_key; :json_attributes end;
|
46
|
+
|
47
|
+
extend ::Trax::Model::Mixin
|
48
|
+
include ::Trax::Model::Attributes::Mixin
|
49
|
+
|
50
|
+
included do
|
51
|
+
class_attribute :json_attribute_fields
|
52
|
+
|
53
|
+
self.json_attribute_fields = ::ActiveSupport::HashWithIndifferentAccess.new
|
54
|
+
end
|
55
|
+
|
56
|
+
module ClassMethods
|
57
|
+
def json_attribute(attribute_name, **options, &block)
|
58
|
+
attributes_klass_name = "#{attribute_name}_attributes".classify
|
59
|
+
attributes_klass = const_set(attributes_klass_name, ::Class.new(::Trax::Model::Attributes[:json]::Value))
|
60
|
+
attributes_klass.instance_eval(&block)
|
61
|
+
|
62
|
+
trax_attribute_fields[:json] ||= {}
|
63
|
+
trax_attribute_fields[:json][attribute_name] = attributes_klass
|
64
|
+
|
65
|
+
attribute(attribute_name, ::Trax::Model::Attributes[:json]::TypeCaster.new(target_klass: attributes_klass))
|
66
|
+
|
67
|
+
self.default_value_for(attribute_name) { {} }
|
68
|
+
self.validates(attribute_name, :json_attribute => true) unless options.key?(:validate) && !options[:validate]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|