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