trax_model 0.0.92 → 0.0.93
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 +0 -1
- data/README.md +227 -86
- data/lib/trax.rb +0 -1
- data/lib/trax/model.rb +23 -29
- data/lib/trax/model/attributes.rb +3 -1
- data/lib/trax/model/attributes/attribute.rb +11 -0
- data/lib/trax/model/attributes/definitions.rb +16 -0
- data/lib/trax/model/attributes/errors.rb +8 -0
- data/lib/trax/model/attributes/fields.rb +74 -0
- data/lib/trax/model/attributes/mixin.rb +48 -19
- data/lib/trax/model/attributes/type.rb +4 -0
- data/lib/trax/model/attributes/types/array.rb +8 -25
- data/lib/trax/model/attributes/types/boolean.rb +51 -0
- data/lib/trax/model/attributes/types/enum.rb +53 -12
- data/lib/trax/model/attributes/types/json.rb +36 -33
- data/lib/trax/model/attributes/types/string.rb +50 -0
- data/lib/trax/model/attributes/types/uuid_array.rb +17 -28
- data/lib/trax/model/attributes/value.rb +16 -0
- data/lib/trax/model/errors.rb +7 -0
- data/lib/trax/model/mixins.rb +11 -0
- data/lib/trax/model/mixins/field_scopes.rb +60 -0
- data/lib/trax/model/mixins/id_scopes.rb +36 -0
- data/lib/trax/model/mixins/sort_by_scopes.rb +25 -0
- data/lib/trax/model/railtie.rb +1 -0
- data/lib/trax/model/scopes.rb +16 -0
- data/lib/trax/model/struct.rb +168 -14
- data/lib/trax/model/unique_id.rb +14 -21
- data/lib/trax/model/uuid.rb +1 -1
- data/lib/trax/validators/enum_attribute_validator.rb +9 -0
- data/lib/trax/validators/future_validator.rb +1 -1
- data/lib/trax/validators/json_attribute_validator.rb +3 -3
- data/lib/trax/validators/string_attribute_validator.rb +17 -0
- data/lib/trax_model/version.rb +1 -1
- data/spec/db/database.yml +16 -0
- data/spec/db/schema/default_tables.rb +68 -0
- data/spec/db/schema/pg_tables.rb +27 -0
- data/spec/spec_helper.rb +20 -3
- data/spec/support/models.rb +123 -0
- data/spec/support/pg/models.rb +103 -0
- data/spec/trax/model/attributes/fields_spec.rb +88 -0
- data/spec/trax/model/attributes/types/enum_spec.rb +51 -0
- data/spec/trax/model/attributes/types/json_spec.rb +107 -0
- data/spec/trax/model/attributes_spec.rb +13 -0
- data/spec/trax/model/errors_spec.rb +1 -2
- data/spec/trax/model/mixins/field_scopes_spec.rb +7 -0
- data/spec/trax/model/struct_spec.rb +1 -1
- data/spec/trax/model/unique_id_spec.rb +1 -3
- data/spec/trax/validators/url_validator_spec.rb +1 -1
- data/trax_model.gemspec +4 -4
- metadata +57 -19
- data/lib/trax/model/config.rb +0 -16
- data/lib/trax/model/validators.rb +0 -15
- data/lib/trax/validators/enum_validator.rb +0 -16
- data/spec/support/schema.rb +0 -151
- data/spec/trax/model/config_spec.rb +0 -13
@@ -5,9 +5,11 @@ module Trax
|
|
5
5
|
module Attributes
|
6
6
|
extend ::ActiveSupport::Autoload
|
7
7
|
|
8
|
+
autoload :Attribute
|
8
9
|
autoload :Mixin
|
9
10
|
autoload :Definitions
|
10
11
|
autoload :Errors
|
12
|
+
autoload :Fields
|
11
13
|
autoload :Types
|
12
14
|
autoload :Type
|
13
15
|
autoload :Value
|
@@ -17,7 +19,7 @@ module Trax
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def self.register_attribute_type(mod)
|
20
|
-
key = mod.name.demodulize.underscore.
|
22
|
+
key = mod.name.demodulize.underscore.to_sym
|
21
23
|
|
22
24
|
config.attribute_types[key] = mod
|
23
25
|
end
|
@@ -13,6 +13,22 @@ module Trax
|
|
13
13
|
def attribute(*args, type:, **options, &block)
|
14
14
|
@model.trax_attribute(*args, type: type, **options, &block)
|
15
15
|
end
|
16
|
+
|
17
|
+
def boolean(*args, **options, &block)
|
18
|
+
attribute(*args, :type => :boolean, **options, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def enum(*args, **options, &block)
|
22
|
+
attribute(*args, type: :enum, **options, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def string(*args, **options, &block)
|
26
|
+
attribute(*args, :type => :string, **options, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def struct(*args, **options, &block)
|
30
|
+
attribute(*args, :type => :json, **options, &block)
|
31
|
+
end
|
16
32
|
end
|
17
33
|
end
|
18
34
|
end
|
@@ -9,6 +9,14 @@ module Trax
|
|
9
9
|
"#{type} is an unknown trax model attribute type"
|
10
10
|
}
|
11
11
|
end
|
12
|
+
|
13
|
+
class DefineAttributeMethodNotDefined < ::Trax::Core::Errors::Base
|
14
|
+
argument :type, :required => true
|
15
|
+
|
16
|
+
message {
|
17
|
+
"#{type} must define a define_attribute method"
|
18
|
+
}
|
19
|
+
end
|
12
20
|
end
|
13
21
|
end
|
14
22
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Attributes
|
4
|
+
module Fields
|
5
|
+
def self.extended(base)
|
6
|
+
base.module_attribute(:_blank_fields_hash) {
|
7
|
+
::Hashie::Mash.new
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def all
|
12
|
+
@all ||= begin
|
13
|
+
constants.map{|const_name| const_get(const_name) }.each_with_object(self._blank_fields_hash) do |klass, result|
|
14
|
+
result[klass.name.symbolize] = klass
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def by_type(*type_names)
|
20
|
+
all.select{|k,v| type_names.include?(v.type) }
|
21
|
+
.try(:with_indifferent_access)
|
22
|
+
end
|
23
|
+
|
24
|
+
def each(&block)
|
25
|
+
all.values(&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def each_pair(*args, &block)
|
29
|
+
all.each_pair(*args, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def booleans
|
33
|
+
@booleans ||= by_type(:boolean)
|
34
|
+
end
|
35
|
+
|
36
|
+
def enums
|
37
|
+
@enums ||= by_type(:enum)
|
38
|
+
end
|
39
|
+
|
40
|
+
def structs
|
41
|
+
@structs ||= by_type(:struct)
|
42
|
+
end
|
43
|
+
|
44
|
+
def strings
|
45
|
+
@strings ||= by_type(:string)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_schema
|
49
|
+
schema = all.inject(::Hashie::Mash.new) do |result, (k,v)|
|
50
|
+
case v.try(:type)
|
51
|
+
when :enum
|
52
|
+
result[k] = v.to_schema
|
53
|
+
when :struct
|
54
|
+
result[k] = v.to_schema
|
55
|
+
else
|
56
|
+
result[k] = v.try(:to_schema)
|
57
|
+
end
|
58
|
+
|
59
|
+
result
|
60
|
+
end
|
61
|
+
schema
|
62
|
+
end
|
63
|
+
|
64
|
+
def values
|
65
|
+
all.values
|
66
|
+
end
|
67
|
+
|
68
|
+
def [](_name)
|
69
|
+
const_get(_name.to_s.camelize)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -2,41 +2,70 @@ module Trax
|
|
2
2
|
module Model
|
3
3
|
module Attributes
|
4
4
|
module Mixin
|
5
|
-
|
6
|
-
|
7
|
-
extend ::Trax::Model::Mixin
|
5
|
+
extend ::Trax::Core::Concern
|
8
6
|
|
9
7
|
included do
|
10
|
-
class_attribute :trax_attribute_fields
|
11
|
-
|
12
|
-
self.trax_attribute_fields = ::ActiveSupport::HashWithIndifferentAccess.new
|
13
|
-
|
14
8
|
::Trax::Model::Attributes.config.attribute_types.each_pair do |key, mod|
|
15
|
-
include mod::Mixin
|
9
|
+
include mod::Mixin if mod.const_defined?("Mixin")
|
16
10
|
end
|
17
11
|
end
|
18
12
|
|
13
|
+
after_included do
|
14
|
+
evaluate_attribute_definitions_blocks
|
15
|
+
end
|
16
|
+
|
19
17
|
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
18
|
def define_attributes(&block)
|
24
|
-
|
25
|
-
model_klass_proxy.instance_eval(&block)
|
19
|
+
self.instance_variable_set("@_attribute_definitions_block", block)
|
26
20
|
end
|
27
21
|
|
28
|
-
|
29
|
-
|
22
|
+
#recursively search direct parent classes for attribute definitions, so we can fully support
|
23
|
+
#inheritance
|
24
|
+
def fetch_attribute_definitions_in_chain(_attribute_definitions_blocks = [], klass=nil)
|
25
|
+
_attribute_definitions_blocks.push(klass.instance_variable_get("@_attribute_definitions_block")) if klass && klass.instance_variable_defined?("@_attribute_definitions_block")
|
26
|
+
|
27
|
+
if klass && klass.superclass != ::ActiveRecord::Base
|
28
|
+
return fetch_attribute_definitions_in_chain(_attribute_definitions_blocks, klass.superclass)
|
29
|
+
else
|
30
|
+
return _attribute_definitions_blocks.compact
|
31
|
+
end
|
32
|
+
end
|
30
33
|
|
34
|
+
def fields_module
|
35
|
+
@fields_module ||= begin
|
36
|
+
module_name = "#{self.name}::Fields"
|
37
|
+
::Trax::Core::NamedModule.new(module_name, ::Trax::Model::Attributes::Fields, :definition_context => self)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def fields
|
42
|
+
@fields ||= fields_module
|
43
|
+
end
|
44
|
+
|
45
|
+
def trax_attribute(name, type:, **options, &block)
|
31
46
|
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
47
|
|
34
|
-
|
48
|
+
if ::Trax::Model::Attributes[type].const_defined?("Mixin")
|
49
|
+
attribute_type_definition_method = ::Trax::Model::Attributes[type]::Mixin::ClassMethods.instance_methods.first
|
50
|
+
self.send(attribute_type_definition_method, name, **options, &block)
|
51
|
+
self.validates(name, options[:validates]) if options.key?(:validates)
|
52
|
+
elsif ::Trax::Model::Attributes[type].respond_to?(:define_attribute)
|
53
|
+
::Trax::Model::Attributes[type].define_attribute(self, name, **options, &block)
|
54
|
+
self.validates(name, options[:validates]) if options.key?(:validates)
|
55
|
+
else
|
56
|
+
raise ::Trax::Model::Attributes::Errors::UnknownAttributeType.new(type: type)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def evaluate_attribute_definitions_blocks
|
61
|
+
model_klass_proxy = ::Trax::Model::Attributes::Definitions.new(self)
|
62
|
+
attribute_definition_blocks = self.superclasses_until(::ActiveRecord::Base, self, [ self ]).map{ |klass| klass.instance_variable_get(:@_attribute_definitions_block) }.compact
|
35
63
|
|
36
|
-
|
64
|
+
attribute_definition_blocks.each do |blk|
|
65
|
+
model_klass_proxy.instance_eval(&blk)
|
66
|
+
end if attribute_definition_blocks.any?
|
37
67
|
end
|
38
68
|
end
|
39
|
-
|
40
69
|
end
|
41
70
|
end
|
42
71
|
end
|
@@ -2,6 +2,10 @@ module Trax
|
|
2
2
|
module Model
|
3
3
|
module Attributes
|
4
4
|
class Type
|
5
|
+
class_attribute :value_klass
|
6
|
+
class_attribute :attribute_klass
|
7
|
+
class_attribute :typecaster_klass
|
8
|
+
|
5
9
|
def self.inherited(subklass)
|
6
10
|
::Trax::Model::Attributes.register_attribute_type(subklass)
|
7
11
|
end
|
@@ -5,6 +5,14 @@ module Trax
|
|
5
5
|
module Attributes
|
6
6
|
module Types
|
7
7
|
class Array < ::Trax::Model::Attributes::Type
|
8
|
+
def self.define_attribute(source_klass, attribute_name, **options, &block)
|
9
|
+
klass.fields_module.const_set(attributes_klass_name, ::Class.new(::Trax::Model::Attributes[:array]::Value))
|
10
|
+
end
|
11
|
+
|
12
|
+
class Attribute < ::Trax::Model::Attributes::Attribute
|
13
|
+
self.type = :array
|
14
|
+
end
|
15
|
+
|
8
16
|
class Value < ::Trax::Model::Attributes::Value
|
9
17
|
class_attribute :element_class
|
10
18
|
include ::Enumerable
|
@@ -60,31 +68,6 @@ module Trax
|
|
60
68
|
end
|
61
69
|
end
|
62
70
|
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
71
|
end
|
89
72
|
end
|
90
73
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'hashie/extensions/ignore_undeclared'
|
2
|
+
|
3
|
+
module Trax
|
4
|
+
module Model
|
5
|
+
module Attributes
|
6
|
+
module Types
|
7
|
+
class Boolean < ::Trax::Model::Attributes::Type
|
8
|
+
#this will by default validate boolean values
|
9
|
+
def self.define_attribute(klass, attribute_name, **options, &block)
|
10
|
+
klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
|
11
|
+
attribute_klass = if options.key?(:class_name)
|
12
|
+
options[:class_name].constantize
|
13
|
+
else
|
14
|
+
::Trax::Core::NamedClass.new(klass_name, ::Trax::Model::Attributes[:boolean]::Attribute, :parent_definition => klass, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
klass.attribute(attribute_name, ::Trax::Model::Attributes::Types::Boolean::TypeCaster.new)
|
18
|
+
klass.validates(attribute_name, :boolean => true) unless options.key?(:validate) && !options[:validate]
|
19
|
+
klass.default_value_for(attribute_name) { options[:default] } if options.key?(:default)
|
20
|
+
end
|
21
|
+
|
22
|
+
class Attribute < ::Trax::Model::Attributes::Attribute
|
23
|
+
self.type = :boolean
|
24
|
+
|
25
|
+
def self.to_schema
|
26
|
+
::Trax::Core::Definition.new({
|
27
|
+
:name => attribute_name,
|
28
|
+
:type => type.to_s,
|
29
|
+
:source => name,
|
30
|
+
:values => values
|
31
|
+
})
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def self.attribute_name
|
37
|
+
name.demodulize.underscore
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.values
|
41
|
+
[ true, false ]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class TypeCaster < ActiveRecord::Type::Boolean
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -5,24 +5,65 @@ module Trax
|
|
5
5
|
module Attributes
|
6
6
|
module Types
|
7
7
|
class Enum < ::Trax::Model::Attributes::Type
|
8
|
-
|
8
|
+
#note: we dont validate enum attribute value because typecaster will turn it into nil which we allow
|
9
|
+
def self.define_attribute(klass, attribute_name, **options, &block)
|
10
|
+
klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
|
11
|
+
attribute_klass = if options.key?(:class_name)
|
12
|
+
options[:class_name].constantize
|
13
|
+
else
|
14
|
+
::Trax::Core::NamedClass.new(klass_name, ::Enum, :parent_definition => klass, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
klass.attribute(attribute_name, ::Trax::Model::Attributes::Types::Enum::TypeCaster.new(target_klass: attribute_klass))
|
18
|
+
klass.default_value_for(attribute_name) { options[:default] } if options.key?(:default)
|
19
|
+
define_scopes(klass, attribute_name, attribute_klass) unless options.key?(:define_scopes) && !options[:define_scopes]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.define_scopes(klass, attribute_name, attribute_klass)
|
23
|
+
klass.class_eval do
|
24
|
+
scope_method_name = :"by_#{attribute_name}"
|
25
|
+
scope_not_method_name = :"by_#{attribute_name}_not"
|
26
|
+
|
27
|
+
scope scope_method_name, lambda { |*values|
|
28
|
+
values.flat_compact_uniq!
|
29
|
+
where(attribute_name => attribute_klass.select_values(*values))
|
30
|
+
}
|
31
|
+
scope scope_not_method_name, lambda { |*values|
|
32
|
+
values.flat_compact_uniq!
|
33
|
+
where.not(attribute_name => attribute_klass.select_values(*values))
|
34
|
+
}
|
35
|
+
end
|
9
36
|
end
|
10
37
|
|
11
|
-
|
12
|
-
|
38
|
+
class TypeCaster < ActiveRecord::Type::Value
|
39
|
+
include ::ActiveRecord::Type::Mutable
|
13
40
|
|
14
|
-
|
15
|
-
include ::Trax::Model::Attributes::Mixin
|
16
|
-
include ::Trax::Model::Enum
|
41
|
+
def type; :enum end;
|
17
42
|
|
18
|
-
|
19
|
-
|
20
|
-
|
43
|
+
def initialize(*args, target_klass:)
|
44
|
+
super(*args)
|
45
|
+
|
46
|
+
@target_klass = target_klass
|
47
|
+
end
|
21
48
|
|
22
|
-
|
49
|
+
def type_cast_from_user(value)
|
50
|
+
@target_klass === value ? @target_klass.new(value) : nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def type_cast_from_database(value)
|
54
|
+
return if value.nil?
|
55
|
+
|
56
|
+
value.present? ? @target_klass.new(value.to_i) : value
|
57
|
+
end
|
58
|
+
|
59
|
+
def type_cast_for_database(value)
|
60
|
+
return if value.nil?
|
61
|
+
|
62
|
+
value.try(:to_i) { @target_klass.new(value).to_i }
|
63
|
+
end
|
23
64
|
|
24
|
-
|
25
|
-
|
65
|
+
def changed_in_place?(raw_old_value, new_value)
|
66
|
+
raw_old_value.try(:to_i) != type_cast_for_database(new_value)
|
26
67
|
end
|
27
68
|
end
|
28
69
|
end
|
@@ -5,14 +5,43 @@ module Trax
|
|
5
5
|
module Attributes
|
6
6
|
module Types
|
7
7
|
class Json < ::Trax::Model::Attributes::Type
|
8
|
-
|
9
|
-
|
10
|
-
@permitted_keys ||= properties.map(&:to_sym)
|
11
|
-
end
|
8
|
+
module ValueExtensions
|
9
|
+
extend ::ActiveSupport::Concern
|
12
10
|
|
13
11
|
def inspect
|
14
12
|
self.to_hash.inspect
|
15
13
|
end
|
14
|
+
|
15
|
+
def to_json
|
16
|
+
self.to_hash.to_json
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def type; :struct end;
|
21
|
+
|
22
|
+
def permitted_keys
|
23
|
+
@permitted_keys ||= properties.map(&:to_sym)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.define_attribute(klass, attribute_name, **options, &block)
|
29
|
+
klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
|
30
|
+
attribute_klass = if options.key?(:class_name)
|
31
|
+
_klass = options[:class_name].constantize
|
32
|
+
_klass.include(ValueExtensions)
|
33
|
+
_klass
|
34
|
+
else
|
35
|
+
::Trax::Core::NamedClass.new(klass_name, Value, :parent_definition => klass, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
klass.attribute(attribute_name, typecaster_klass.new(target_klass: attribute_klass))
|
39
|
+
klass.validates(attribute_name, :json_attribute => true) unless options.key?(:validate) && !options[:validate]
|
40
|
+
klass.default_value_for(attribute_name) { {} }
|
41
|
+
end
|
42
|
+
|
43
|
+
class Value < ::Trax::Model::Struct
|
44
|
+
include ValueExtensions
|
16
45
|
end
|
17
46
|
|
18
47
|
class TypeCaster < ActiveRecord::Type::Value
|
@@ -37,38 +66,12 @@ module Trax
|
|
37
66
|
end
|
38
67
|
|
39
68
|
def type_cast_for_database(value)
|
40
|
-
value.present? ? value.
|
69
|
+
value.present? ? value.to_serializable_hash.to_json : nil
|
41
70
|
end
|
42
71
|
end
|
43
72
|
|
44
|
-
|
45
|
-
|
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
|
73
|
+
self.value_klass = ::Trax::Model::Attributes::Types::Json::Value
|
74
|
+
self.typecaster_klass = ::Trax::Model::Attributes::Types::Json::TypeCaster
|
72
75
|
end
|
73
76
|
end
|
74
77
|
end
|