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
@@ -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
|