trax_model 0.0.92 → 0.0.93
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'hashie/extensions/ignore_undeclared'
|
2
|
+
|
3
|
+
module Trax
|
4
|
+
module Model
|
5
|
+
module Attributes
|
6
|
+
module Types
|
7
|
+
class String < ::Trax::Model::Attributes::Type
|
8
|
+
def self.define_attribute(klass, attribute_name, **options, &block)
|
9
|
+
klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
|
10
|
+
attribute_klass = if options.key?(:class_name)
|
11
|
+
options[:class_name].constantize
|
12
|
+
else
|
13
|
+
::Trax::Core::NamedClass.new(klass_name, Value, :parent_definition => klass, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
klass.attribute(attribute_name, typecaster_klass.new(target_klass: attribute_klass))
|
17
|
+
klass.default_value_for(attribute_name) { options[:default] } if options.key?(:default)
|
18
|
+
end
|
19
|
+
|
20
|
+
class Value < ::Trax::Model::Attributes::Value
|
21
|
+
def self.type; :string end;
|
22
|
+
end
|
23
|
+
|
24
|
+
class TypeCaster < ActiveRecord::Type::String
|
25
|
+
def initialize(*args, target_klass:)
|
26
|
+
super(*args)
|
27
|
+
|
28
|
+
@target_klass = target_klass
|
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(value) : value
|
37
|
+
end
|
38
|
+
|
39
|
+
def type_cast_for_database(value)
|
40
|
+
value.try(:to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
self.value_klass = ::Trax::Model::Attributes::Types::String::Value
|
45
|
+
self.typecaster_klass = ::Trax::Model::Attributes::Types::String::TypeCaster
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -5,6 +5,23 @@ module Trax
|
|
5
5
|
module Attributes
|
6
6
|
module Types
|
7
7
|
class UuidArray < ::Trax::Model::Attributes::Type
|
8
|
+
def self.define_attribute(klass, attribute_name, **options, &block)
|
9
|
+
klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
|
10
|
+
attribute_klass = if options.key?(:class_name)
|
11
|
+
options[:class_name].constantize
|
12
|
+
else
|
13
|
+
::Trax::Core::NamedClass.new(klass_name, Value, :parent_definition => klass, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
attribute_klass.element_class = options[:of] if options.has_key?(:of)
|
17
|
+
options.has_key?(:default) ? self.default_value_for(attribute_name, options[:default]) : []
|
18
|
+
klass.attribute(attribute_name, typecaster_klass.new(target_klass: attribute_klass))
|
19
|
+
end
|
20
|
+
|
21
|
+
class Attribute < ::Trax::Model::Attributes::Attribute
|
22
|
+
self.type = :uuid_array
|
23
|
+
end
|
24
|
+
|
8
25
|
class Value < ::Trax::Model::Attributes::Value
|
9
26
|
def initialize(*args)
|
10
27
|
@array = ::Trax::Model::UUIDArray.new(*args)
|
@@ -48,34 +65,6 @@ module Trax
|
|
48
65
|
end
|
49
66
|
end
|
50
67
|
end
|
51
|
-
|
52
|
-
module Mixin
|
53
|
-
def self.mixin_registry_key; :uuid_array_attributes end;
|
54
|
-
|
55
|
-
extend ::Trax::Model::Mixin
|
56
|
-
include ::Trax::Model::Attributes::Mixin
|
57
|
-
|
58
|
-
module ClassMethods
|
59
|
-
def uuid_array_attribute(attribute_name, **options, &block)
|
60
|
-
attributes_klass_name = "#{attribute_name}_attributes".classify
|
61
|
-
attributes_klass = const_set(attributes_klass_name, ::Class.new(::Trax::Model::Attributes[:uuid_array]::Value))
|
62
|
-
attributes_klass.instance_eval(&block) if block_given?
|
63
|
-
|
64
|
-
attributes_klass.element_class = options[:of] if options.has_key?(:of)
|
65
|
-
|
66
|
-
trax_attribute_fields[:uuid_array] ||= {}
|
67
|
-
trax_attribute_fields[:uuid_array][attribute_name] = attributes_klass
|
68
|
-
|
69
|
-
if options.has_key?(:default)
|
70
|
-
self.default_value_for(attribute_name, options[:default])
|
71
|
-
else
|
72
|
-
[]
|
73
|
-
end
|
74
|
-
|
75
|
-
attribute(attribute_name, ::Trax::Model::Attributes[:uuid_array]::TypeCaster.new(target_klass: attributes_klass))
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
68
|
end
|
80
69
|
end
|
81
70
|
end
|
@@ -2,7 +2,23 @@ module Trax
|
|
2
2
|
module Model
|
3
3
|
module Attributes
|
4
4
|
class Value < SimpleDelegator
|
5
|
+
include ::ActiveModel::Validations
|
5
6
|
|
7
|
+
def initialize(val)
|
8
|
+
@value = val
|
9
|
+
end
|
10
|
+
|
11
|
+
def __getobj__
|
12
|
+
@value
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.symbolic_name
|
16
|
+
name.demodulize.underscore.to_sym
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.to_sym
|
20
|
+
:value
|
21
|
+
end
|
6
22
|
end
|
7
23
|
end
|
8
24
|
end
|
data/lib/trax/model/errors.rb
CHANGED
@@ -34,6 +34,13 @@ module Trax
|
|
34
34
|
}
|
35
35
|
end
|
36
36
|
|
37
|
+
class FieldDoesNotExist < Trax::Core::Errors::Base
|
38
|
+
argument :field, :required => true
|
39
|
+
argument :model, :required => true
|
40
|
+
|
41
|
+
message { "Field #{field} does not exist for #{model}" }
|
42
|
+
end
|
43
|
+
|
37
44
|
class STIAttributeNotFound < ::Trax::Core::Errors::Base
|
38
45
|
argument :attribute_name
|
39
46
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Mixins
|
4
|
+
module FieldScopes
|
5
|
+
extend ::Trax::Model::Mixin
|
6
|
+
|
7
|
+
mixed_in do |**options|
|
8
|
+
options.each_pair do |field_scope_name, field_scope_options|
|
9
|
+
field_scope_options = {} if [ true, false ].include?(field_scope_options)
|
10
|
+
|
11
|
+
field_scope_options[:field] ||= field_scope_name.to_s.include?("by_") ? field_scope_name.to_s.split("by_").pop.to_sym : field_scope_name
|
12
|
+
field_scope_options[:type] ||= :where
|
13
|
+
|
14
|
+
case field_scope_options[:type]
|
15
|
+
when :where
|
16
|
+
define_where_scope_for_field(field_scope_name, **field_scope_options)
|
17
|
+
when :match
|
18
|
+
define_matching_scope_for_field(field_scope_name, **field_scope_options)
|
19
|
+
when :matching
|
20
|
+
define_matching_scope_for_field(field_scope_name, **field_scope_options)
|
21
|
+
when :not
|
22
|
+
define_where_not_scope_for_field(field_scope_name, **field_scope_options)
|
23
|
+
when :where_not
|
24
|
+
define_where_not_scope_for_field(field_scope_name, **field_scope_options)
|
25
|
+
else
|
26
|
+
define_where_scope_for_field(field_scope_name, **field_scope_options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
private
|
33
|
+
def define_where_scope_for_field(field_scope_name, **options)
|
34
|
+
scope field_scope_name, lambda{ |*_values|
|
35
|
+
_values.flat_compact_uniq!
|
36
|
+
where(options[:field] => _values)
|
37
|
+
}
|
38
|
+
|
39
|
+
# Alias scope names with pluralized versions, i.e. by_id also => by_ids
|
40
|
+
singleton_class.__send__(:alias_method, :"#{field_scope_name.to_s.pluralize}", field_scope_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def define_where_not_scope_for_field(field_scope_name, **options)
|
44
|
+
scope field_scope_name, lambda{ |*_values|
|
45
|
+
_values.flat_compact_uniq!
|
46
|
+
where.not(options[:field] => _values)
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_matching_scope_for_field(field_scope_name, **options)
|
51
|
+
scope field_scope_name, lambda{ |*_values|
|
52
|
+
_values.flat_compact_uniq!
|
53
|
+
matching(options[:field] => _values)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#note this only works with postgres UUID column Types
|
2
|
+
|
3
|
+
module Trax
|
4
|
+
module Model
|
5
|
+
module Mixins
|
6
|
+
module IdScopes
|
7
|
+
extend ::Trax::Model::Mixin
|
8
|
+
|
9
|
+
included do
|
10
|
+
if table_exists?
|
11
|
+
id_column_names = self.columns.select{ |col| col.sql_type == "uuid" }.map(&:name).map(&:to_sym)
|
12
|
+
id_column_names.each do |_id_column_name|
|
13
|
+
scope :"by_#{_id_column_name}", lambda{ |*_record_ids|
|
14
|
+
_record_ids.flat_compact_uniq!
|
15
|
+
where(_id_column_name => _record_ids)
|
16
|
+
}
|
17
|
+
|
18
|
+
scope :"by_#{_id_column_name}_not", lambda{ |*_record_ids|
|
19
|
+
_record_ids.flat_compact_uniq!
|
20
|
+
where.not(_id_column_name => _record_ids)
|
21
|
+
}
|
22
|
+
|
23
|
+
define_singleton_method(:"by_#{_id_column_name}s") do |*_record_ids|
|
24
|
+
__send__("by_#{_id_column_name}", _record_ids)
|
25
|
+
end
|
26
|
+
|
27
|
+
define_singleton_method(:"by_#{_id_column_name}s_not") do |*_record_ids|
|
28
|
+
__send__("by_#{_id_column_name}_not", _record_ids)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Mixins
|
4
|
+
module SortByScopes
|
5
|
+
extend ::Trax::Model::Mixin
|
6
|
+
|
7
|
+
included do
|
8
|
+
scope :sort_by_most_recent, lambda{|field_name='created_at'|
|
9
|
+
order("#{field_name} DESC")
|
10
|
+
}
|
11
|
+
scope :sort_by_least_recent, lambda{|field_name='created_at'|
|
12
|
+
order("#{field_name} ASC")
|
13
|
+
}
|
14
|
+
|
15
|
+
class << self
|
16
|
+
alias_method :sort_by_newest, :sort_by_most_recent
|
17
|
+
alias_method :sort_by_oldest, :sort_by_least_recent
|
18
|
+
alias_method :by_newest, :sort_by_most_recent
|
19
|
+
alias_method :by_oldest, :sort_by_least_recent
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/trax/model/railtie.rb
CHANGED
@@ -4,6 +4,7 @@ module Trax
|
|
4
4
|
::ActiveSupport.on_load(:active_record) do
|
5
5
|
def self.inherited(subklass)
|
6
6
|
subklass.include(::Trax::Model) if ::Trax::Model.config.auto_include
|
7
|
+
subklass.include(::Trax::Model::Attributes::Mixin) if ::Trax::Model.config.auto_include
|
7
8
|
|
8
9
|
super(subklass)
|
9
10
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Scopes
|
4
|
+
extend ::ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def field_scope(attr_name)
|
8
|
+
scope attr_name, lambda{ |*_scope_values|
|
9
|
+
_scope_values.flat_compact_uniq!
|
10
|
+
where(attr_name => _scope_values)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/trax/model/struct.rb
CHANGED
@@ -1,30 +1,184 @@
|
|
1
|
+
require 'hashie/extensions/coercion'
|
2
|
+
require 'hashie/extensions/indifferent_access'
|
3
|
+
require 'hashie/extensions/dash/indifferent_access'
|
4
|
+
|
1
5
|
module Trax
|
2
6
|
module Model
|
3
7
|
class Struct < ::Hashie::Dash
|
4
8
|
include ::Hashie::Extensions::Dash::IndifferentAccess
|
5
9
|
include ::Hashie::Extensions::Coercion
|
6
10
|
include ::Hashie::Extensions::IgnoreUndeclared
|
11
|
+
include ::Hashie::Extensions::Dash::PropertyTranslation
|
7
12
|
include ::ActiveModel::Validations
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
# note that we must explicitly set default or blank values for all properties.
|
15
|
+
# It defeats the whole purpose of being a 'struct'
|
16
|
+
# if we fail to do so, and it makes our data far more error prone
|
17
|
+
DEFAULT_VALUES_FOR_PROPERTY_TYPES = {
|
18
|
+
:boolean_property => nil,
|
19
|
+
:string_property => "",
|
20
|
+
:struct_property => {},
|
21
|
+
:enum_property => nil,
|
22
|
+
}.with_indifferent_access.freeze
|
13
23
|
|
14
|
-
|
15
|
-
|
16
|
-
|
24
|
+
def self.fields_module
|
25
|
+
@fields_module ||= begin
|
26
|
+
module_name = "#{self.name}::Fields"
|
27
|
+
::Trax::Core::NamedModule.new(module_name, ::Trax::Model::Attributes::Fields)
|
17
28
|
end
|
18
29
|
end
|
19
30
|
|
20
|
-
def self.
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
31
|
+
def self.fields
|
32
|
+
fields_module
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.boolean_property(name, *args, **options, &block)
|
36
|
+
name = name.is_a?(Symbol) ? name.to_s : name
|
37
|
+
klass_name = "#{fields_module.name.underscore}/#{name}".camelize
|
38
|
+
boolean_klass = ::Trax::Core::NamedClass.new(klass_name, Trax::Model::Attributes[:boolean]::Attribute, :parent_definition => self, &block)
|
39
|
+
options[:default] = options.key?(:default) ? options[:default] : DEFAULT_VALUES_FOR_PROPERTY_TYPES[__method__]
|
40
|
+
define_where_scopes_for_boolean_property(name, boolean_klass) unless options.key?(:define_scopes) && !options[:define_scopes]
|
25
41
|
property(name, *args, **options)
|
26
|
-
|
27
|
-
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.string_property(name, *args, **options, &block)
|
45
|
+
name = name.is_a?(Symbol) ? name.to_s : name
|
46
|
+
klass_name = "#{fields_module.name.underscore}/#{name}".camelize
|
47
|
+
string_klass = ::Trax::Core::NamedClass.new(klass_name, Trax::Model::Attributes[:string]::Value, :parent_definition => self, &block)
|
48
|
+
options[:default] = options.key?(:default) ? options[:default] : DEFAULT_VALUES_FOR_PROPERTY_TYPES[__method__]
|
49
|
+
define_where_scopes_for_property(name, string_klass) unless options.key?(:define_scopes) && !options[:define_scopes]
|
50
|
+
property(name.to_sym, *args, **options)
|
51
|
+
coerce_key(name.to_sym, string_klass)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.struct_property(name, *args, **options, &block)
|
55
|
+
name = name.is_a?(Symbol) ? name.to_s : name
|
56
|
+
klass_name = "#{fields_module.name.underscore}/#{name}".camelize
|
57
|
+
struct_klass = ::Trax::Core::NamedClass.new(klass_name, Trax::Model::Struct, :parent_definition => self, &block)
|
58
|
+
validates(name, :json_attribute => true) unless options[:validate] && !options[:validate]
|
59
|
+
options[:default] = options.key?(:default) ? options[:default] : DEFAULT_VALUES_FOR_PROPERTY_TYPES[__method__]
|
60
|
+
property(name.to_sym, *args, **options)
|
61
|
+
coerce_key(name.to_sym, struct_klass)
|
62
|
+
end
|
63
|
+
|
64
|
+
#note: cant validate because we are coercing which will turn it into nil
|
65
|
+
def self.enum_property(name, *args, **options, &block)
|
66
|
+
name = name.is_a?(Symbol) ? name.to_s : name
|
67
|
+
klass_name = "#{fields_module.name.underscore}/#{name}".camelize
|
68
|
+
enum_klass = ::Trax::Core::NamedClass.new(klass_name, ::Enum, :parent_definition => self, &block)
|
69
|
+
options[:default] = options.key?(:default) ? options[:default] : DEFAULT_VALUES_FOR_PROPERTY_TYPES[__method__]
|
70
|
+
define_scopes_for_enum(name, enum_klass) unless options.key?(:define_scopes) && !options[:define_scopes]
|
71
|
+
property(name.to_sym, *args, **options)
|
72
|
+
coerce_key(name.to_sym, enum_klass)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.to_schema
|
76
|
+
::Trax::Core::Definition.new(
|
77
|
+
:source => self.name,
|
78
|
+
:name => self.name.demodulize.underscore,
|
79
|
+
:type => :struct,
|
80
|
+
:fields => self.fields_module.to_schema
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.type; :struct end;
|
85
|
+
|
86
|
+
def to_serializable_hash
|
87
|
+
_serializable_hash = to_hash
|
88
|
+
|
89
|
+
self.class.fields_module.enums.keys.each do |attribute_name|
|
90
|
+
_serializable_hash[attribute_name] = _serializable_hash[attribute_name].try(:to_i)
|
91
|
+
end if self.class.fields_module.enums.keys.any?
|
92
|
+
|
93
|
+
_serializable_hash
|
94
|
+
end
|
95
|
+
|
96
|
+
class << self
|
97
|
+
alias :boolean :boolean_property
|
98
|
+
alias :enum :enum_property
|
99
|
+
alias :struct :struct_property
|
100
|
+
alias :string :string_property
|
101
|
+
end
|
102
|
+
|
103
|
+
def value
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
#this only supports properties 1 level deep, but works beautifully
|
109
|
+
#I.E. for this structure
|
110
|
+
# define_attributes do
|
111
|
+
# struct :custom_fields do
|
112
|
+
# enum :color, :default => :blue do
|
113
|
+
# define :blue, 1
|
114
|
+
# define :red, 2
|
115
|
+
# define :green, 3
|
116
|
+
# end
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
# ::Product.by_custom_fields_color(:blue, :red)
|
120
|
+
# will return #{Product color=blue}, #{Product color=red}
|
121
|
+
def self.define_scopes_for_enum(attribute_name, enum_klass)
|
122
|
+
return unless has_active_record_ancestry?(enum_klass)
|
123
|
+
|
124
|
+
model_class = model_class_for_property(enum_klass)
|
125
|
+
field_name = enum_klass.parent_definition.name.demodulize.underscore
|
126
|
+
attribute_name = enum_klass.name.demodulize.underscore
|
127
|
+
scope_name = :"by_#{field_name}_#{attribute_name}"
|
128
|
+
model_class.scope(scope_name, lambda{ |*_scope_values|
|
129
|
+
_integer_values = enum_klass.select_values(*_scope_values.flat_compact_uniq!)
|
130
|
+
_integer_values.map!(&:to_s)
|
131
|
+
model_class.where("#{field_name} -> '#{attribute_name}' IN(?)", _integer_values)
|
132
|
+
})
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.define_where_scopes_for_boolean_property(attribute_name, property_klass)
|
136
|
+
return unless has_active_record_ancestry?(property_klass)
|
137
|
+
|
138
|
+
model_class = model_class_for_property(property_klass)
|
139
|
+
field_name = property_klass.parent_definition.name.demodulize.underscore
|
140
|
+
attribute_name = property_klass.name.demodulize.underscore
|
141
|
+
scope_name = :"by_#{field_name}_#{attribute_name}"
|
142
|
+
model_class.scope(scope_name, lambda{ |*_scope_values|
|
143
|
+
_scope_values.map!(&:to_s).flat_compact_uniq!
|
144
|
+
model_class.where("#{field_name} -> '#{attribute_name}' IN(?)", _scope_values)
|
145
|
+
})
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.define_where_scopes_for_property(attribute_name, property_klass)
|
149
|
+
return unless has_active_record_ancestry?(property_klass)
|
150
|
+
|
151
|
+
model_class = model_class_for_property(property_klass)
|
152
|
+
field_name = property_klass.parent_definition.name.demodulize.underscore
|
153
|
+
attribute_name = property_klass.name.demodulize.underscore
|
154
|
+
scope_name = :"by_#{field_name}_#{attribute_name}"
|
155
|
+
|
156
|
+
model_class.scope(scope_name, lambda{ |*_scope_values|
|
157
|
+
_scope_values.map!(&:to_s).flat_compact_uniq!
|
158
|
+
model_class.where("#{field_name} ->> '#{attribute_name}' IN(?)", _scope_values)
|
159
|
+
})
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.has_active_record_ancestry?(property_klass)
|
163
|
+
return false unless property_klass.respond_to?(:parent_definition)
|
164
|
+
|
165
|
+
result = if property_klass.parent_definition.ancestors.include?(::ActiveRecord::Base)
|
166
|
+
true
|
167
|
+
else
|
168
|
+
has_active_record_ancestry?(property_klass.parent_definition)
|
169
|
+
end
|
170
|
+
|
171
|
+
result
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.model_class_for_property(property_klass)
|
175
|
+
result = if property_klass.parent_definition.ancestors.include?(::ActiveRecord::Base)
|
176
|
+
property_klass.parent_definition
|
177
|
+
else
|
178
|
+
model_class_for_property(property_klass.parent_definition)
|
179
|
+
end
|
180
|
+
|
181
|
+
result
|
28
182
|
end
|
29
183
|
end
|
30
184
|
end
|