trax_model 0.0.98 → 0.0.99

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -0
  3. data/lib/trax/model.rb +6 -16
  4. data/lib/trax/model/attributes.rb +1 -1
  5. data/lib/trax/model/attributes/definitions.rb +4 -0
  6. data/lib/trax/model/attributes/{mixin.rb → dsl.rb} +2 -12
  7. data/lib/trax/model/attributes/types/boolean.rb +1 -2
  8. data/lib/trax/model/attributes/types/enum.rb +0 -2
  9. data/lib/trax/model/attributes/types/integer.rb +2 -2
  10. data/lib/trax/model/attributes/types/set.rb +67 -0
  11. data/lib/trax/model/attributes/types/string.rb +3 -3
  12. data/lib/trax/model/attributes/types/struct.rb +5 -3
  13. data/lib/trax/model/attributes/types/uuid_array.rb +0 -2
  14. data/lib/trax/model/{sti.rb → core_extensions.rb} +2 -2
  15. data/lib/trax/model/core_extensions/string.rb +17 -0
  16. data/lib/trax/model/extensions_for.rb +18 -0
  17. data/lib/trax/model/extensions_for/base.rb +31 -0
  18. data/lib/trax/model/extensions_for/boolean.rb +29 -0
  19. data/lib/trax/model/extensions_for/enumerable.rb +18 -0
  20. data/lib/trax/model/extensions_for/integer.rb +10 -0
  21. data/lib/trax/model/extensions_for/numeric.rb +46 -0
  22. data/lib/trax/model/extensions_for/set.rb +10 -0
  23. data/lib/trax/model/extensions_for/string.rb +30 -0
  24. data/lib/trax/model/extensions_for/struct.rb +191 -0
  25. data/lib/trax/model/extensions_for/struct_fields.rb +21 -0
  26. data/lib/trax/model/extensions_for/struct_fields/boolean.rb +32 -0
  27. data/lib/trax/model/extensions_for/struct_fields/enum.rb +20 -0
  28. data/lib/trax/model/extensions_for/struct_fields/float.rb +12 -0
  29. data/lib/trax/model/extensions_for/struct_fields/integer.rb +12 -0
  30. data/lib/trax/model/extensions_for/struct_fields/numeric.rb +52 -0
  31. data/lib/trax/model/extensions_for/struct_fields/string.rb +29 -0
  32. data/lib/trax/model/extensions_for/struct_fields/time.rb +54 -0
  33. data/lib/trax/model/matchable.rb +3 -10
  34. data/lib/trax/model/mixins.rb +3 -0
  35. data/lib/trax/model/mixins/freezable.rb +31 -0
  36. data/lib/trax/model/mixins/restorable.rb +63 -0
  37. data/lib/trax/model/mixins/sti_enum.rb +4 -5
  38. data/lib/trax/model/mixins/unique_id.rb +85 -0
  39. data/lib/trax/model/railtie.rb +4 -2
  40. data/lib/trax/validators/json_attribute_validator.rb +1 -0
  41. data/lib/trax_model/version.rb +1 -1
  42. data/spec/db/schema/default_tables.rb +1 -17
  43. data/spec/db/schema/pg_tables.rb +15 -0
  44. data/spec/support/models.rb +11 -16
  45. data/spec/support/pg/models.rb +31 -6
  46. data/spec/trax/model/attributes/types/set_spec.rb +68 -0
  47. data/spec/trax/model/attributes/types/struct_spec.rb +6 -0
  48. data/spec/trax/model/extensions_for/boolean_spec.rb +26 -0
  49. data/spec/trax/model/extensions_for/numeric_spec.rb +53 -0
  50. data/spec/trax/model/extensions_for/string_spec.rb +25 -0
  51. data/spec/trax/model/extensions_for/struct_spec.rb +223 -0
  52. data/spec/trax/model/extensions_for_spec.rb +0 -0
  53. data/spec/trax/model/{freezable_spec.rb → mixins/freezable_spec.rb} +1 -1
  54. data/spec/trax/model/{restorable_spec.rb → mixins/restorable_spec.rb} +1 -1
  55. data/spec/trax/model/{unique_id_spec.rb → mixins/unique_id_spec.rb} +1 -1
  56. data/trax_model.gemspec +4 -5
  57. metadata +49 -59
  58. data/lib/trax/model/enum.rb +0 -64
  59. data/lib/trax/model/freezable.rb +0 -29
  60. data/lib/trax/model/mti.rb +0 -11
  61. data/lib/trax/model/mti/abstract.rb +0 -65
  62. data/lib/trax/model/mti/entity.rb +0 -62
  63. data/lib/trax/model/mti/namespace.rb +0 -21
  64. data/lib/trax/model/restorable.rb +0 -61
  65. data/lib/trax/model/scopes.rb +0 -16
  66. data/lib/trax/model/sti/attributes.rb +0 -94
  67. data/lib/trax/model/string_extensions.rb +0 -11
  68. data/lib/trax/model/struct_extensions.rb +0 -185
  69. data/lib/trax/model/unique_id.rb +0 -83
  70. data/spec/trax/model/sti/attributes_spec.rb +0 -15
  71. data/spec/trax/model/struct_extensions_spec.rb +0 -16
@@ -0,0 +1,46 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module Numeric
5
+ extend ::ActiveSupport::Concern
6
+
7
+ include ::Trax::Model::ExtensionsFor::Base
8
+
9
+ module ClassMethods
10
+ def between(lower_value, upper_value)
11
+ gt(lower_value).merge(lt(upper_value))
12
+ end
13
+
14
+ def in_range(lower_value, upper_value)
15
+ gte(lower_value).merge(lte(upper_value))
16
+ end
17
+
18
+ def eq(*_scope_values)
19
+ _scope_values.flat_compact_uniq!
20
+ model_class.where({field_name => _scope_values})
21
+ end
22
+
23
+ def gt(value)
24
+ operator = '>'
25
+ model_class.where("#{field_name} #{operator} ?", value)
26
+ end
27
+
28
+ def gte(value)
29
+ operator = '>='
30
+ model_class.where("#{field_name} #{operator} ?", value)
31
+ end
32
+
33
+ def lt(value)
34
+ operator = '<'
35
+ model_class.where("#{field_name} #{operator} ?", value)
36
+ end
37
+
38
+ def lte(value)
39
+ operator = '<='
40
+ model_class.where("#{field_name} #{operator} ?", value)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module Set
5
+ extend ::ActiveSupport::Concern
6
+ include ::Trax::Model::ExtensionsFor::Enumerable
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module String
5
+ extend ::ActiveSupport::Concern
6
+
7
+ include ::Trax::Model::ExtensionsFor::Base
8
+
9
+ module ClassMethods
10
+ def eq(*_values)
11
+ _values.flat_compact_uniq!
12
+ model_class.where({field_name => _values})
13
+ end
14
+
15
+ def eq_lower(*_values)
16
+ _values.flat_compact_uniq!
17
+ _values.map!(&:downcase)
18
+ model_class.where("lower(#{field_name}) IN(?)", _values)
19
+ end
20
+
21
+ def matches(*_values)
22
+ _values.flat_compact_uniq!
23
+ _values.map!(&:to_matchable)
24
+ model_class.where("(#{field_name}) ilike ANY(array[?])", _values)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,191 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module Struct
5
+ extend ::ActiveSupport::Concern
6
+ include ::ActiveModel::Validations
7
+ include ::Trax::Model::ExtensionsFor::Base
8
+
9
+ included do
10
+ attr_reader :record
11
+ end
12
+
13
+ def inspect
14
+ self.to_hash.inspect
15
+ end
16
+
17
+ def to_json
18
+ self.to_hash.to_json
19
+ end
20
+
21
+ def value
22
+ self
23
+ end
24
+
25
+ module ClassMethods
26
+ def [](val)
27
+ fields[val]
28
+ end
29
+ #bit of a hack for the sake of strong params for now
30
+ def permitted_keys
31
+ @permitted_keys ||= properties.map(&:to_sym)
32
+ end
33
+
34
+ def property(property_name, *args, **options)
35
+ super(property_name, *args, **options)
36
+
37
+ extensions_for_property_type = ::Trax::Model::ExtensionsFor::StructFields[self.fields[property_name].type]
38
+
39
+ if extensions_for_property_type
40
+ self.fields[property_name].include(extensions_for_property_type)
41
+ end
42
+ end
43
+
44
+ def define_model_scopes_for(*attribute_names)
45
+ attribute_names.each do |attribute_name|
46
+ define_model_scope_for(attribute_name)
47
+ end
48
+ end
49
+
50
+ def define_model_scope_for(attribute_name, **options)
51
+ attribute_klass = fields[attribute_name]
52
+
53
+ case fields[attribute_name].type
54
+ when :boolean
55
+ define_where_scopes_for_boolean_property(attribute_name, attribute_klass, **options)
56
+ when :enum
57
+ define_scopes_for_enum(attribute_name, attribute_klass, **options)
58
+ when :array
59
+ define_scopes_for_array(attribute_name, attribute_klass, **options)
60
+ when :integer
61
+ define_scopes_for_numeric(attribute_name, attribute_klass, **options)
62
+ when :time
63
+ define_scopes_for_time(attribute_name, attribute_klass, **options)
64
+ else
65
+ define_where_scopes_for_property(attribute_name, attribute_klass, **options)
66
+ end
67
+ end
68
+
69
+ def define_scopes_for_array(attribute_name, property_klass, as:nil)
70
+ return unless has_active_record_ancestry?(property_klass)
71
+
72
+ model_class = model_class_for_property(property_klass)
73
+ field_name = property_klass.parent_definition.name.demodulize.underscore
74
+ attribute_name = property_klass.name.demodulize.underscore
75
+ scope_name = as || :"by_#{field_name}_#{attribute_name}"
76
+
77
+ model_class.scope(scope_name, lambda{ |*_scope_values|
78
+ _scope_values.flat_compact_uniq!
79
+ model_class.where("#{field_name} -> '#{attribute_name}' ?| array[:values]", :values => _scope_values)
80
+ })
81
+ end
82
+
83
+ def define_scopes_for_numeric(attribute_name, property_klass, as:nil)
84
+ return unless has_active_record_ancestry?(property_klass)
85
+
86
+ model_class = model_class_for_property(property_klass)
87
+ field_name = property_klass.parent_definition.name.demodulize.underscore
88
+ attribute_name = property_klass.name.demodulize.underscore
89
+ cast_type = property_klass.type
90
+
91
+ { :gt => '>', :gte => '>=', :lt => '<', :lte => '<=', :eq => '='}.each_pair do |k, operator|
92
+ scope_name = as ? :"#{as}_#{k}" : :"by_#{field_name}_#{attribute_name}_#{k}"
93
+
94
+ model_class.scope(scope_name, lambda{ |*_scope_values|
95
+ _scope_values.flat_compact_uniq!
96
+ model_class.where("(#{field_name} ->> '#{attribute_name}')::#{cast_type} #{operator} ?", _scope_values)
97
+ })
98
+ end
99
+ end
100
+
101
+ def define_scopes_for_time(attribute_name, property_klass, as:nil)
102
+ return unless has_active_record_ancestry?(property_klass)
103
+
104
+ model_class = model_class_for_property(property_klass)
105
+ field_name = property_klass.parent_definition.name.demodulize.underscore
106
+ attribute_name = property_klass.name.demodulize.underscore
107
+ cast_type = 'timestamp'
108
+
109
+ { :gt => '>', :lt => '<'}.each_pair do |k, operator|
110
+ scope_prefix = as ? as : :"by_#{field_name}_#{attribute_name}"
111
+ scope_name = "#{scope_prefix}_#{k}"
112
+ scope_alias = "#{scope_prefix}_#{{:gt => 'after', :lt => 'before' }[k]}"
113
+
114
+ model_class.scope(scope_name, lambda{ |*_scope_values|
115
+ _scope_values.flat_compact_uniq!
116
+ model_class.where("(#{field_name} ->> '#{attribute_name}')::#{cast_type} #{operator} ?", _scope_values)
117
+ })
118
+ model_class.singleton_class.__send__("alias_method", scope_alias.to_sym, scope_name)
119
+ end
120
+ end
121
+
122
+ #this only supports properties 1 level deep, but works beautifully
123
+ #I.E. for this structure
124
+ # define_attributes do
125
+ # struct :custom_fields do
126
+ # enum :color, :default => :blue do
127
+ # define :blue, 1
128
+ # define :red, 2
129
+ # define :green, 3
130
+ # end
131
+ # end
132
+ # end
133
+ # ::Product.by_custom_fields_color(:blue, :red)
134
+ # will return #{Product color=blue}, #{Product color=red}
135
+ def define_scopes_for_enum(attribute_name, enum_klass, as:nil)
136
+ return unless has_active_record_ancestry?(enum_klass)
137
+
138
+ model_class = model_class_for_property(enum_klass)
139
+ field_name = enum_klass.parent_definition.name.demodulize.underscore
140
+ attribute_name = enum_klass.name.demodulize.underscore
141
+ scope_name = as || :"by_#{field_name}_#{attribute_name}"
142
+ model_class.scope(scope_name, lambda{ |*_scope_values|
143
+ _integer_values = enum_klass.select_values(*_scope_values.flat_compact_uniq!)
144
+ _integer_values.map!(&:to_s)
145
+ model_class.where("#{field_name} -> '#{attribute_name}' IN(?)", _integer_values)
146
+ })
147
+ end
148
+
149
+ def define_where_scopes_for_boolean_property(attribute_name, property_klass, as:nil)
150
+ return unless has_active_record_ancestry?(property_klass)
151
+
152
+ model_class = model_class_for_property(property_klass)
153
+ field_name = property_klass.parent_definition.name.demodulize.underscore
154
+ attribute_name = property_klass.name.demodulize.underscore
155
+ scope_name = as || :"by_#{field_name}_#{attribute_name}"
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 define_where_scopes_for_property(attribute_name, property_klass, as:nil)
163
+ return unless has_active_record_ancestry?(property_klass)
164
+
165
+ model_class = model_class_for_property(property_klass)
166
+ field_name = property_klass.parent_definition.name.demodulize.underscore
167
+ attribute_name = property_klass.name.demodulize.underscore
168
+ scope_name = as || :"by_#{field_name}_#{attribute_name}"
169
+
170
+ model_class.scope(scope_name, lambda{ |*_scope_values|
171
+ _scope_values.map!(&:to_s).flat_compact_uniq!
172
+ model_class.where("#{field_name} ->> '#{attribute_name}' IN(?)", _scope_values)
173
+ })
174
+ end
175
+
176
+ def has_active_record_ancestry?(property_klass)
177
+ return false unless property_klass.respond_to?(:parent_definition)
178
+
179
+ result = if property_klass.parent_definition.ancestors.include?(::ActiveRecord::Base)
180
+ true
181
+ else
182
+ has_active_record_ancestry?(property_klass.parent_definition)
183
+ end
184
+
185
+ result
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,21 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module StructFields
5
+ extend ::ActiveSupport::Autoload
6
+
7
+ autoload :Boolean
8
+ autoload :Enum
9
+ autoload :Float
10
+ autoload :Integer
11
+ autoload :Numeric
12
+ autoload :String
13
+ autoload :Time
14
+
15
+ def self.[](val)
16
+ "#{name}::#{val.to_s.classify}".safe_constantize
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module StructFields
5
+ module Boolean
6
+ extend ::ActiveSupport::Concern
7
+ include ::Trax::Model::ExtensionsFor::Base
8
+
9
+ module ClassMethods
10
+ def eq(*_scope_values)
11
+ _scope_values.flat_compact_uniq!
12
+ cast_type = type
13
+ model_class.where("(#{parent_definition.field_name} ->> '#{field_name}')::#{cast_type} IN(?)", _scope_values)
14
+ end
15
+
16
+ def is_nil
17
+ eq(nil)
18
+ end
19
+
20
+ def is_true
21
+ eq(true)
22
+ end
23
+
24
+ def is_false
25
+ eq(false)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module StructFields
5
+ module Enum
6
+ extend ::ActiveSupport::Concern
7
+ include ::Trax::Model::ExtensionsFor::Base
8
+
9
+ module ClassMethods
10
+ def eq(*_scope_values)
11
+ _integer_values = select_values(*_scope_values.flat_compact_uniq!)
12
+ _integer_values.map!(&:to_s)
13
+ model_class.where("#{parent_definition.field_name} -> '#{field_name}' IN(?)", _integer_values)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module StructFields
5
+ module Float
6
+ extend ::ActiveSupport::Concern
7
+ include ::Trax::Model::ExtensionsFor::StructFields::Numeric
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module StructFields
5
+ module Integer
6
+ extend ::ActiveSupport::Concern
7
+ include ::Trax::Model::ExtensionsFor::StructFields::Numeric
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module StructFields
5
+ module Numeric
6
+ extend ::ActiveSupport::Concern
7
+ include ::Trax::Model::ExtensionsFor::Base
8
+
9
+ module ClassMethods
10
+ def between(lower_value, upper_value)
11
+ gt(lower_value).merge(lt(upper_value))
12
+ end
13
+
14
+ def in_range(lower_value, upper_value)
15
+ gte(lower_value).merge(lte(upper_value))
16
+ end
17
+
18
+ def eq(*_scope_values)
19
+ _scope_values.flat_compact_uniq!
20
+ cast_type = type
21
+ model_class.where("(#{parent_definition.field_name} ->> '#{field_name}')::#{cast_type} IN(?)", _scope_values)
22
+ end
23
+
24
+ def gt(value)
25
+ cast_type = type
26
+ operator = '>'
27
+ model_class.where("(#{parent_definition.field_name} ->> '#{field_name}')::#{cast_type} #{operator} ?", value)
28
+ end
29
+
30
+ def gte(value)
31
+ cast_type = type
32
+ operator = '>='
33
+ model_class.where("(#{parent_definition.field_name} ->> '#{field_name}')::#{cast_type} #{operator} ?", value)
34
+ end
35
+
36
+ def lt(value)
37
+ cast_type = type
38
+ operator = '<'
39
+ model_class.where("(#{parent_definition.field_name} ->> '#{field_name}')::#{cast_type} #{operator} ?", value)
40
+ end
41
+
42
+ def lte(value)
43
+ cast_type = type
44
+ operator = '<='
45
+ model_class.where("(#{parent_definition.field_name} ->> '#{field_name}')::#{cast_type} #{operator} ?", value)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ module Trax
2
+ module Model
3
+ module ExtensionsFor
4
+ module StructFields
5
+ module String
6
+ extend ::ActiveSupport::Concern
7
+ include ::Trax::Model::ExtensionsFor::Base
8
+
9
+ module ClassMethods
10
+ def eq(*_scope_values)
11
+ _scope_values.flat_compact_uniq!
12
+ model_class.where("(#{parent_definition.field_name} ->> '#{field_name}') IN(?)", _scope_values)
13
+ end
14
+
15
+ def eq_lower(*_scope_values)
16
+ _scope_values.flat_compact_uniq!
17
+ model_class.where("lower(#{parent_definition.field_name} ->> '#{field_name}') IN(?)", _scope_values.map(&:downcase))
18
+ end
19
+
20
+ def matches(*_scope_values)
21
+ _scope_values.flat_compact_uniq!
22
+ model_class.where("(#{parent_definition.field_name} ->> '#{field_name}') ilike ANY(array[?])", _scope_values.map(&:to_matchable))
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end