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
@@ -1,21 +0,0 @@
1
- module Trax
2
- module Model
3
- module MTI
4
- module Namespace
5
- extend ::Trax::Core::EagerAutoloadNamespace
6
-
7
- class << self
8
- attr_reader :base_mti_model
9
- end
10
-
11
- def self.base_model(model)
12
- @base_mti_model = model
13
- end
14
-
15
- def self.all
16
- @all ||= base_mti_model.subclasses
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,61 +0,0 @@
1
- module Trax
2
- module Model
3
- module Restorable
4
- extend ::Trax::Model::Mixin
5
-
6
- define_configuration_options! do
7
- option :field, :default => :is_deleted
8
- option :timestamp_field, :default => :deleted_at
9
- option :hide_deleted, :default => true
10
- option :alias_destroy, :default => true
11
- end
12
-
13
- included do
14
- define_configuration_options!(:restorable) do
15
- option :field, :default => ::Trax::Model::Restorable.config.field
16
- option :timestamp_field, :default => ::Trax::Model::Restorable.config.timestamp_field
17
- option :hide_deleted, :default => ::Trax::Model::Restorable.config.hide_deleted
18
- option :alias_destroy, :default => ::Trax::Model::Restorable.config.alias_destroy
19
- end
20
- end
21
-
22
- module ClassMethods
23
- def setup_restorable!
24
- self.class_eval do
25
- if(self.restorable_config.hide_deleted)
26
- default_scope { by_not_deleted }
27
- end
28
-
29
- if(self.restorable_config.alias_destroy)
30
- alias_method :destroy!, :destroy
31
- end
32
-
33
- ### Clear default deleted scope ###
34
- scope :by_is_deleted, lambda { |*|
35
- unscope(:where => self.restorable_config.field).where(self.restorable_config.field => true)
36
- }
37
- scope :by_not_deleted, lambda { |*|
38
- where(self.restorable_config.field => false)
39
- }
40
-
41
- default_value_for(self.restorable_config.field) { false }
42
- end
43
- end
44
- end
45
-
46
- def destroy
47
- self.update_attributes(self.class.restorable_config.field => true, self.class.restorable_config.timestamp_field => ::DateTime.now)
48
- end
49
-
50
- def restore
51
- self.update_attributes(self.class.restorable_config.field => false, self.class.restorable_config.timestamp_field => ::DateTime.now)
52
- end
53
-
54
- def self.apply_mixin(target, options)
55
- target.restorable_config.merge!(options)
56
-
57
- target.setup_restorable!
58
- end
59
- end
60
- end
61
- end
@@ -1,16 +0,0 @@
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
@@ -1,94 +0,0 @@
1
- module Trax
2
- module Model
3
- module STI
4
- module Attributes
5
- extend ::ActiveSupport::Concern
6
-
7
- #not configurable atm as you can see below
8
-
9
- included do
10
- class_attribute :_attribute_set_column,
11
- :_attribute_set_class_name,
12
- :_attribute_set_relation_name,
13
- :_sti_attributes
14
-
15
- self._sti_attributes = []
16
-
17
- self._attribute_set_relation_name = :attribute_set
18
- self._attribute_set_column = :attribute_set_id
19
- self._attribute_set_class_name = "#{name}AttributeSet"
20
-
21
- self.belongs_to(self._attribute_set_relation_name, :class_name => self._attribute_set_class_name, :validate => true)
22
- self.validates(self._attribute_set_relation_name, :presence => true)
23
-
24
- self.before_create(:attribute_set)
25
- end
26
-
27
- def attributes
28
- super.merge!(sti_attributes)
29
- end
30
-
31
- def attribute_set
32
- build_attribute_set if super.nil?
33
-
34
- super
35
- end
36
-
37
- def sti_attributes
38
- sti_attribute_hash = self.class._sti_attributes.inject({}) do |result, attribute|
39
- result["#{attribute}"] = __send__(attribute)
40
- result
41
- end
42
-
43
- sti_attribute_hash
44
- end
45
-
46
- def super!(*args)
47
- method_caller = caller_locations(1,1)[0].label
48
- attribute_set.send(method_caller, *args)
49
- end
50
-
51
- module ClassMethods
52
- def sti_attribute(*args)
53
- options = args.extract_options!
54
-
55
- args.each do |attribute_name|
56
- return unless self._attribute_set_class_name.constantize.table_exists?
57
- raise ::Trax::Model::Errors::STIAttributeNotFound unless self._attribute_set_class_name.constantize.column_names.include?("#{attribute_name}")
58
-
59
- self._sti_attributes << attribute_name
60
-
61
- self.delegate(attribute_name, :to => :attribute_set)
62
- self.delegate("#{attribute_name}=", :to => :attribute_set) unless options.key?(:writer) && options[:writer] == false
63
- end
64
- end
65
-
66
- def validates_uniqueness_of!(*args)
67
- options = args.extract_options!
68
-
69
- args.each do |arg|
70
- validation_method_name = :"validate_#{arg.to_s}"
71
-
72
- self.send(:define_method, :"validate_#{arg.to_s}") do |*_args|
73
- where_scope_hash = {}
74
- field_value = self.__send__(arg)
75
- where_scope_hash[arg] = field_value
76
-
77
- where_scope = self._attribute_set_class_name.constantize.where(where_scope_hash).all
78
-
79
- options[:scope].each do |field|
80
- scoped_field_value = self.__send__(field)
81
- where_scope.merge(self.class.where({field => scoped_field_value}))
82
- end if options.has_key?(:scope)
83
-
84
- errors.add(arg, "has already been taken") if where_scope.limit(1).any?
85
- end
86
-
87
- self.validate(validation_method_name)
88
- end
89
- end
90
- end
91
- end
92
- end
93
- end
94
- end
@@ -1,11 +0,0 @@
1
- module Trax
2
- module Model
3
- module StringExtensions
4
- extend ::ActiveSupport::Concern
5
-
6
- def uuid
7
- ::Trax::Model::UUID === self ? ::Trax::Model::UUID.new(self) : nil
8
- end
9
- end
10
- end
11
- end
@@ -1,185 +0,0 @@
1
- module Trax
2
- module Model
3
- module StructExtensions
4
- extend ::ActiveSupport::Concern
5
- include ::ActiveModel::Validations
6
-
7
- included do
8
- attr_reader :record
9
- end
10
-
11
- def inspect
12
- self.to_hash.inspect
13
- end
14
-
15
- def to_json
16
- self.to_hash.to_json
17
- end
18
-
19
- def value
20
- self
21
- end
22
-
23
- module ClassMethods
24
- #bit of a hack for the sake of strong params for now
25
- def permitted_keys
26
- @permitted_keys ||= properties.map(&:to_sym)
27
- end
28
-
29
- def define_model_scopes_for(*attribute_names)
30
- attribute_names.each do |attribute_name|
31
- define_model_scope_for(attribute_name)
32
- end
33
- end
34
-
35
- def define_model_scope_for(attribute_name, **options)
36
- attribute_klass = fields[attribute_name]
37
-
38
- case fields[attribute_name].type
39
- when :boolean
40
- define_where_scopes_for_boolean_property(attribute_name, attribute_klass, **options)
41
- when :enum
42
- define_scopes_for_enum(attribute_name, attribute_klass, **options)
43
- when :array
44
- define_scopes_for_array(attribute_name, attribute_klass, **options)
45
- when :integer
46
- define_scopes_for_numeric(attribute_name, attribute_klass, **options)
47
- when :time
48
- define_scopes_for_time(attribute_name, attribute_klass, **options)
49
- else
50
- define_where_scopes_for_property(attribute_name, attribute_klass, **options)
51
- end
52
- end
53
-
54
- def define_scopes_for_array(attribute_name, property_klass, as:nil)
55
- return unless has_active_record_ancestry?(property_klass)
56
-
57
- model_class = model_class_for_property(property_klass)
58
- field_name = property_klass.parent_definition.name.demodulize.underscore
59
- attribute_name = property_klass.name.demodulize.underscore
60
- scope_name = as || :"by_#{field_name}_#{attribute_name}"
61
-
62
- model_class.scope(scope_name, lambda{ |*_scope_values|
63
- _scope_values.flat_compact_uniq!
64
- model_class.where("#{field_name} -> '#{attribute_name}' ?| array[:values]", :values => _scope_values)
65
- })
66
- end
67
-
68
- def define_scopes_for_numeric(attribute_name, property_klass, as:nil)
69
- return unless has_active_record_ancestry?(property_klass)
70
-
71
- model_class = model_class_for_property(property_klass)
72
- field_name = property_klass.parent_definition.name.demodulize.underscore
73
- attribute_name = property_klass.name.demodulize.underscore
74
- cast_type = property_klass.type
75
-
76
- { :gt => '>', :gte => '>=', :lt => '<', :lte => '<=', :eq => '='}.each_pair do |k, operator|
77
- scope_name = as ? :"#{as}_#{k}" : :"by_#{field_name}_#{attribute_name}_#{k}"
78
-
79
- model_class.scope(scope_name, lambda{ |*_scope_values|
80
- _scope_values.flat_compact_uniq!
81
- model_class.where("(#{field_name} ->> '#{attribute_name}')::#{cast_type} #{operator} ?", _scope_values)
82
- })
83
- end
84
- end
85
-
86
- def define_scopes_for_time(attribute_name, property_klass, as:nil)
87
- return unless has_active_record_ancestry?(property_klass)
88
-
89
- model_class = model_class_for_property(property_klass)
90
- field_name = property_klass.parent_definition.name.demodulize.underscore
91
- attribute_name = property_klass.name.demodulize.underscore
92
- cast_type = 'timestamp'
93
-
94
- { :gt => '>', :lt => '<'}.each_pair do |k, operator|
95
- scope_prefix = as ? as : :"by_#{field_name}_#{attribute_name}"
96
- scope_name = "#{scope_prefix}_#{k}"
97
- scope_alias = "#{scope_prefix}_#{{:gt => 'after', :lt => 'before' }[k]}"
98
-
99
- model_class.scope(scope_name, lambda{ |*_scope_values|
100
- _scope_values.flat_compact_uniq!
101
- model_class.where("(#{field_name} ->> '#{attribute_name}')::#{cast_type} #{operator} ?", _scope_values)
102
- })
103
- model_class.singleton_class.__send__("alias_method", scope_alias.to_sym, scope_name)
104
- end
105
- end
106
-
107
- #this only supports properties 1 level deep, but works beautifully
108
- #I.E. for this structure
109
- # define_attributes do
110
- # struct :custom_fields do
111
- # enum :color, :default => :blue do
112
- # define :blue, 1
113
- # define :red, 2
114
- # define :green, 3
115
- # end
116
- # end
117
- # end
118
- # ::Product.by_custom_fields_color(:blue, :red)
119
- # will return #{Product color=blue}, #{Product color=red}
120
- def define_scopes_for_enum(attribute_name, enum_klass, as:nil)
121
- return unless has_active_record_ancestry?(enum_klass)
122
-
123
- model_class = model_class_for_property(enum_klass)
124
- field_name = enum_klass.parent_definition.name.demodulize.underscore
125
- attribute_name = enum_klass.name.demodulize.underscore
126
- scope_name = as || :"by_#{field_name}_#{attribute_name}"
127
- model_class.scope(scope_name, lambda{ |*_scope_values|
128
- _integer_values = enum_klass.select_values(*_scope_values.flat_compact_uniq!)
129
- _integer_values.map!(&:to_s)
130
- model_class.where("#{field_name} -> '#{attribute_name}' IN(?)", _integer_values)
131
- })
132
- end
133
-
134
- def define_where_scopes_for_boolean_property(attribute_name, property_klass, as:nil)
135
- return unless has_active_record_ancestry?(property_klass)
136
-
137
- model_class = model_class_for_property(property_klass)
138
- field_name = property_klass.parent_definition.name.demodulize.underscore
139
- attribute_name = property_klass.name.demodulize.underscore
140
- scope_name = as || :"by_#{field_name}_#{attribute_name}"
141
- model_class.scope(scope_name, lambda{ |*_scope_values|
142
- _scope_values.map!(&:to_s).flat_compact_uniq!
143
- model_class.where("#{field_name} -> '#{attribute_name}' IN(?)", _scope_values)
144
- })
145
- end
146
-
147
- def define_where_scopes_for_property(attribute_name, property_klass, as:nil)
148
- return unless has_active_record_ancestry?(property_klass)
149
-
150
- model_class = model_class_for_property(property_klass)
151
- field_name = property_klass.parent_definition.name.demodulize.underscore
152
- attribute_name = property_klass.name.demodulize.underscore
153
- scope_name = as || :"by_#{field_name}_#{attribute_name}"
154
-
155
- model_class.scope(scope_name, lambda{ |*_scope_values|
156
- _scope_values.map!(&:to_s).flat_compact_uniq!
157
- model_class.where("#{field_name} ->> '#{attribute_name}' IN(?)", _scope_values)
158
- })
159
- end
160
-
161
- def has_active_record_ancestry?(property_klass)
162
- return false unless property_klass.respond_to?(:parent_definition)
163
-
164
- result = if property_klass.parent_definition.ancestors.include?(::ActiveRecord::Base)
165
- true
166
- else
167
- has_active_record_ancestry?(property_klass.parent_definition)
168
- end
169
-
170
- result
171
- end
172
-
173
- def model_class_for_property(property_klass)
174
- result = if property_klass.parent_definition.ancestors.include?(::ActiveRecord::Base)
175
- property_klass.parent_definition
176
- else
177
- model_class_for_property(property_klass.parent_definition)
178
- end
179
-
180
- result
181
- end
182
- end
183
- end
184
- end
185
- end
@@ -1,83 +0,0 @@
1
- module Trax
2
- module Model
3
- module UniqueId
4
- extend ::Trax::Model::Mixin
5
-
6
- define_configuration_options! do
7
- option :uuid_column, :default => :id
8
- option :uuid_map, :default => {}
9
- end
10
-
11
- after_included do |options|
12
- define_configuration_options!(:unique_id) do
13
- option :uuid_prefix,
14
- :validates => {
15
- :exclusion => {
16
- :in => ::Trax::Model::Registry.uuid_map.values
17
- },
18
- :inclusion => {
19
- :in => ::Trax::Model::UUIDPrefix.all,
20
- :message => "%{value} not a valid uuid prefix!\nRun Trax::Model::UUIDPrefix.all for valid prefix list"
21
- },
22
- :allow_nil => true
23
- }
24
-
25
- option :uuid_column, :default => ::Trax::Model::UniqueId.config.uuid_column
26
-
27
- klass do
28
- def uuid_prefix=(prefix)
29
- if(::Trax::Model::UniqueId.config.uuid_map.values.include?(prefix) && ::Trax::Model::UniqueId.config.uuid_map[self.source.name] != prefix)
30
- raise ::Trax::Model::Errors::DuplicatePrefixRegistered.new(:prefix => prefix, :model => self.source.name)
31
- end
32
-
33
- ::Trax::Model::UniqueId.config.uuid_map[self.source.name] = prefix
34
- super(::Trax::Model::UUIDPrefix.new(prefix))
35
- end
36
- end
37
-
38
- end
39
-
40
- #grab prefix from uuid registry if uuids are defined in an initializer
41
- if ::Trax::Model.mixin_registry.key?(:unique_id) && ::Trax::Model::UUID.klass_prefix_map.key?(self.name)
42
- self.unique_id_config.uuid_prefix = ::Trax::Model::UUID.klass_prefix_map[self.name]
43
- end
44
-
45
- self.unique_id_config.merge!(options)
46
-
47
- if(self.unique_id_config.uuid_prefix)
48
- default_value_for(:"#{self.unique_id_config.uuid_column}") {
49
- ::Trax::Model::UUID.generate(self.unique_id_config.uuid_prefix)
50
- }
51
- end
52
- end
53
-
54
- def uuid
55
- uuid_column = self.class.unique_id_config.uuid_column
56
- uuid_value = (uuid_column == "uuid") ? super : __send__(uuid_column)
57
-
58
- ::Trax::Model::UUID.new(uuid_value)
59
- end
60
-
61
- #i.e. Blog::Post
62
- def uuid_type
63
- uuid.record_type
64
- end
65
-
66
- #i.e. 'Blog::Post'
67
- def uuid_type_name
68
- uuid.record_type.name
69
- end
70
-
71
- #i.e, Blog::Post will = post
72
- def uuid_type_slug
73
- uuid.record_type.name.demodulize.underscore
74
- end
75
-
76
- module ClassMethods
77
- def uuid_prefix
78
- self.unique_id_config.uuid_prefix
79
- end
80
- end
81
- end
82
- end
83
- end