trax_model 0.0.91 → 0.0.92

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/lib/trax/model.rb +71 -12
  4. data/lib/trax/model/attributes.rb +40 -0
  5. data/lib/trax/model/attributes/definitions.rb +19 -0
  6. data/lib/trax/model/attributes/errors.rb +15 -0
  7. data/lib/trax/model/attributes/mixin.rb +43 -0
  8. data/lib/trax/model/attributes/type.rb +11 -0
  9. data/lib/trax/model/attributes/types.rb +11 -0
  10. data/lib/trax/model/attributes/types/array.rb +92 -0
  11. data/lib/trax/model/attributes/types/enum.rb +32 -0
  12. data/lib/trax/model/attributes/types/json.rb +76 -0
  13. data/lib/trax/model/attributes/types/uuid_array.rb +83 -0
  14. data/lib/trax/model/attributes/value.rb +9 -0
  15. data/lib/trax/model/config.rb +1 -9
  16. data/lib/trax/model/enum.rb +3 -3
  17. data/lib/trax/model/errors.rb +30 -19
  18. data/lib/trax/model/freezable.rb +1 -1
  19. data/lib/trax/model/mixin.rb +16 -4
  20. data/lib/trax/model/railtie.rb +17 -0
  21. data/lib/trax/model/registry.rb +3 -3
  22. data/lib/trax/model/restorable.rb +26 -17
  23. data/lib/trax/model/struct.rb +31 -0
  24. data/lib/trax/model/unique_id.rb +56 -14
  25. data/lib/trax/model/uuid_array.rb +59 -0
  26. data/lib/trax/model/validators.rb +2 -0
  27. data/lib/trax/validators/boolean_validator.rb +14 -0
  28. data/lib/trax/validators/email_validator.rb +6 -2
  29. data/lib/trax/validators/enum_validator.rb +16 -0
  30. data/lib/trax/validators/json_attribute_validator.rb +19 -0
  31. data/lib/trax_model/version.rb +1 -1
  32. data/spec/support/schema.rb +26 -10
  33. data/spec/trax/model/struct_spec.rb +16 -0
  34. data/spec/trax/model_spec.rb +2 -4
  35. data/trax_model.gemspec +1 -0
  36. metadata +35 -2
@@ -0,0 +1,83 @@
1
+ require 'hashie/extensions/ignore_undeclared'
2
+
3
+ module Trax
4
+ module Model
5
+ module Attributes
6
+ module Types
7
+ class UuidArray < ::Trax::Model::Attributes::Type
8
+ class Value < ::Trax::Model::Attributes::Value
9
+ def initialize(*args)
10
+ @array = ::Trax::Model::UUIDArray.new(*args)
11
+ end
12
+
13
+ def __getobj__
14
+ @array
15
+ end
16
+
17
+ def inspect
18
+ @array.to_a.flatten.inspect
19
+ end
20
+ end
21
+
22
+ class TypeCaster < ActiveRecord::Type::Value
23
+ include ::ActiveRecord::Type::Mutable
24
+
25
+ def initialize(*args, target_klass:)
26
+ super(*args)
27
+
28
+ @target_klass = target_klass
29
+ end
30
+
31
+ def type
32
+ :uuid_array
33
+ end
34
+
35
+ def type_cast_from_user(value)
36
+ value.is_a?(@target_klass) ? @target_klass : @target_klass.new(value) || @target_klass.new
37
+ end
38
+
39
+ def type_cast_from_database(value)
40
+ value.present? ? @target_klass.new(*value) : @target_klass.new(nil)
41
+ end
42
+
43
+ def type_cast_for_database(value)
44
+ if value.present?
45
+ value.to_json
46
+ else
47
+ nil
48
+ end
49
+ end
50
+ 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
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,9 @@
1
+ module Trax
2
+ module Model
3
+ module Attributes
4
+ class Value < SimpleDelegator
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,20 +1,12 @@
1
1
  module Trax
2
2
  module Model
3
3
  class Config < ::Hashie::Dash
4
- ERROR_MESSAGES = {
5
- :invalid_uuid_prefix => [
6
- "UUID prefix must be 2 characters long",
7
- "and be 0-9 or a-f",
8
- "for hexadecimal id compatibility"
9
- ].join("\n")
10
- }.freeze
11
-
12
4
  property :uuid_prefix, :default => nil
13
5
  property :uuid_column, :default => :id
14
6
 
15
7
  def uuid_prefix=(prefix)
16
8
  if prefix.length != 2 || prefix.chars.any?{|char| char !~ /[0-9a-f]/ }
17
- raise ::Trax::Model::Errors::InvalidPrefixForUUID.new(prefix)
9
+ raise ::Trax::Model::Errors::InvalidPrefixForUUID.new(:prefix => prefix)
18
10
  end
19
11
 
20
12
  self[:uuid_prefix] = ::Trax::Model::UUIDPrefix.new(prefix)
@@ -20,7 +20,7 @@
20
20
  module Trax
21
21
  module Model
22
22
  module Enum
23
- include ::Trax::Model::Mixin
23
+ extend ::Trax::Model::Mixin
24
24
 
25
25
  module ClassMethods
26
26
  def define_scopes_for_trax_enum(enum_name)
@@ -50,13 +50,13 @@ module Trax
50
50
  validation_options = { :in => enum_values, :message => options.extract!(:message)[:message] }
51
51
 
52
52
  self.validates_inclusion_of(enum_name, validation_options) unless options.key?(:validate) && !options[:validate]
53
+
53
54
  options.delete(:validate) if options.key?(:validate)
54
55
 
55
56
  define_scopes_for_trax_enum(enum_name)
56
57
 
57
- self.default_value_for(enum_name) { default_value } if default_value
58
-
59
58
  super(enum_name, enum_mapping, options)
59
+ self.default_value_for(enum_name) { default_value } if default_value
60
60
  end
61
61
  end
62
62
  end
@@ -1,32 +1,43 @@
1
+ require 'trax/core/errors'
2
+
1
3
  module Trax
2
4
  module Model
3
5
  module Errors
4
- class Base < StandardError
5
- def initialize(*args)
6
- message = (self.class::MESSAGE + args).join("\n")
7
- super(message)
8
- end
6
+ class MixinNotRegistered < ::Trax::Core::Errors::Base
7
+ argument :mixin
8
+ argument :model
9
+
10
+ message {
11
+ "#{model} tried to load mixin: #{mixin}, whichdoes not exist in " \
12
+ "registry. Registered mixins were #{::Trax::Model.mixin_registry.keys.join(', ')} \n"
13
+ }
9
14
  end
10
15
 
11
- class InvalidPrefix < Trax::Model::Errors::Base
12
- MESSAGE = [
13
- "UUID prefix must be 2 characters long",
14
- "and be 0-9 or a-f",
16
+ class InvalidPrefix < ::Trax::Core::Errors::Base
17
+ argument :prefix, :required => true
18
+
19
+ message {
20
+ "Prefix #{prefix}"
21
+ "UUID prefix must be 2 characters long" \
22
+ "and be 0-9 or a-f" \
15
23
  "for hexadecimal id compatibility"
16
- ]
24
+ }
17
25
  end
18
26
 
19
- class DuplicatePrefixRegistered < Trax::Model::Errors::Base
20
- MESSAGE = [
21
- "UUID prefix must be unique, the",
22
- "following prefix was already registered"
23
- ]
27
+ class DuplicatePrefixRegistered < Trax::Core::Errors::Base
28
+ argument :prefix, :required => true
29
+ argument :model, :required => true
30
+
31
+ message {
32
+ "UUID prefix must be unique,\n" \
33
+ "#{prefix} was already registered by #{model}!"
34
+ }
24
35
  end
25
36
 
26
- class STIAttributeNotFound < ::Trax::Model::Errors::Base
27
- MESSAGE = [
28
- "STI Attribute was not found for attribute"
29
- ]
37
+ class STIAttributeNotFound < ::Trax::Core::Errors::Base
38
+ argument :attribute_name
39
+
40
+ message { "STI Attribute was not found for #{attribute_name}" }
30
41
  end
31
42
  end
32
43
  end
@@ -1,7 +1,7 @@
1
1
  module Trax
2
2
  module Model
3
3
  module Freezable
4
- include ::Trax::Model::Mixin
4
+ extend ::Trax::Model::Mixin
5
5
 
6
6
  included do
7
7
  class_attribute :freezable_fields
@@ -1,12 +1,24 @@
1
1
  module Trax
2
2
  module Model
3
3
  module Mixin
4
- extend ::ActiveSupport::Concern
4
+ def self.extended(base)
5
+ base.extend(::ActiveSupport::Concern)
5
6
 
6
- included do
7
- self.extend(::ActiveSupport::Concern)
7
+ super(base)
8
8
 
9
- ::Trax::Model.register_mixin(self)
9
+ ::Trax::Model.register_mixin(base)
10
+ end
11
+
12
+ def after_included(&block)
13
+ self.instance_variable_set(:@_after_included_block, block)
14
+ end
15
+
16
+ def mixed_in(&block)
17
+ after_included(&block)
18
+ end
19
+
20
+ def mixed(&block)
21
+ after_included(&block)
10
22
  end
11
23
  end
12
24
  end
@@ -0,0 +1,17 @@
1
+ module Trax
2
+ module Model
3
+ class Railtie < ::Rails::Railtie
4
+ ::ActiveSupport.on_load(:active_record) do
5
+ def self.inherited(subklass)
6
+ subklass.include(::Trax::Model) if ::Trax::Model.config.auto_include
7
+
8
+ super(subklass)
9
+
10
+ ::Trax::Model.config.auto_include_mixins.each do |mixin|
11
+ subklass.mixin(mixin)
12
+ end if ::Trax::Model.config.auto_include_mixins.any?
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -3,7 +3,7 @@ module Trax
3
3
  class Registry
4
4
  class_attribute :models
5
5
 
6
- self.models ||= ::Hashie::Mash.new
6
+ self.models = ::Hashie::Mash.new
7
7
 
8
8
  class << self
9
9
  delegate :key?, :to => :models
@@ -52,8 +52,8 @@ module Trax
52
52
  end
53
53
 
54
54
  def self.uuid_map
55
- models.values.reject{|model| model.try(:uuid_prefix) == nil }.inject(::Hashie::Mash.new) do |result, model|
56
- result[model.uuid_prefix] = model
55
+ models.values.reject{|model| model.try(:unique_id_config).try(:uuid_prefix) == nil }.inject(::Hashie::Mash.new) do |result, model|
56
+ result[model.unique_id_config.uuid_prefix] = model
57
57
  result
58
58
  end
59
59
  end
@@ -1,50 +1,59 @@
1
1
  module Trax
2
2
  module Model
3
3
  module Restorable
4
- include ::Trax::Model::Mixin
4
+ extend ::Trax::Model::Mixin
5
5
 
6
- included do
7
- class_attribute :_restorable_config
8
-
9
- alias_method :destroy!, :destroy
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
10
12
 
11
- self._restorable_config = {
12
- :field => :is_deleted,
13
- :timestamp_field => :deleted_at,
14
- :hide_deleted => true
15
- }
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
16
20
  end
17
21
 
18
22
  module ClassMethods
19
23
  def setup_restorable!
20
24
  self.class_eval do
21
- if(self._restorable_config[:hide_deleted])
25
+ if(self.restorable_config.hide_deleted)
22
26
  default_scope { by_not_deleted }
23
27
  end
24
28
 
29
+ if(self.restorable_config.alias_destroy)
30
+ alias_method :destroy!, :destroy
31
+ end
32
+
25
33
  ### Clear default deleted scope ###
26
34
  scope :by_is_deleted, lambda { |*|
27
- unscope(:where => self._restorable_config[:field]).where(self._restorable_config[:field] => true)
35
+ unscope(:where => self.restorable_config.field).where(self.restorable_config.field => true)
28
36
  }
29
37
  scope :by_not_deleted, lambda { |*|
30
- where(self._restorable_config[:field] => false)
38
+ where(self.restorable_config.field => false)
31
39
  }
32
40
 
33
- default_value_for(self._restorable_config[:field]) { false }
41
+ default_value_for(self.restorable_config.field) { false }
34
42
  end
35
43
  end
36
44
  end
37
45
 
38
46
  def destroy
39
- self.update_attributes(self._restorable_config[:field] => true, self._restorable_config[:timestamp_field] => ::DateTime.now)
47
+ self.update_attributes(self.class.restorable_config.field => true, self.class.restorable_config.timestamp_field => ::DateTime.now)
40
48
  end
41
49
 
42
50
  def restore
43
- self.update_attributes(self._restorable_config[:field] => false, self._restorable_config[:timestamp_field] => ::DateTime.now)
51
+ self.update_attributes(self.class.restorable_config.field => false, self.class.restorable_config.timestamp_field => ::DateTime.now)
44
52
  end
45
53
 
46
54
  def self.apply_mixin(target, options)
47
- target._restorable_config.merge!(options)
55
+ target.restorable_config.merge!(options)
56
+
48
57
  target.setup_restorable!
49
58
  end
50
59
  end
@@ -0,0 +1,31 @@
1
+ module Trax
2
+ module Model
3
+ class Struct < ::Hashie::Dash
4
+ include ::Hashie::Extensions::Dash::IndifferentAccess
5
+ include ::Hashie::Extensions::Coercion
6
+ include ::Hashie::Extensions::IgnoreUndeclared
7
+ include ::ActiveModel::Validations
8
+
9
+ class_attribute :property_types
10
+
11
+ def self.inherited(subklass)
12
+ super(subklass)
13
+
14
+ subklass.property_types = {}.dup.tap do |hash|
15
+ hash[:structs] = []
16
+ hash
17
+ end
18
+ end
19
+
20
+ def self.struct_property(name, *args, **options, &block)
21
+ struct_klass_name = "#{name}_structs".classify
22
+ struct_klass = const_set(struct_klass_name, ::Class.new(::Trax::Model::Struct))
23
+ struct_klass.instance_eval(&block)
24
+ options[:default] = {} unless options.key?(:default)
25
+ property(name, *args, **options)
26
+ coerce_key(name, struct_klass)
27
+ property_types[:structs].push(name)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,17 +1,56 @@
1
1
  module Trax
2
2
  module Model
3
3
  module UniqueId
4
- include ::Trax::Model::Mixin
4
+ extend ::Trax::Model::Mixin
5
+
6
+ define_configuration_options! do
7
+ option :uuid_column, :default => :id
8
+ option :uuid_map, :default => {}
9
+ end
5
10
 
6
11
  included do
7
- #grab prefix from uuid registry if using that
8
- if ::Trax::Model::UUID.klass_prefix_map.has_key?(self.name)
9
- self.trax_defaults.uuid_prefix = ::Trax::Model::UUIDPrefix.new(klass_prefix_map[self.name])
12
+ define_configuration_options!(:unique_id) do
13
+ option :uuid_prefix,
14
+ :setter => lambda{ |prefix|
15
+ if(::Trax::Model::UniqueId.config.uuid_map.values.include?(prefix) && ::Trax::Model::UniqueId.config.uuid_map[self.source.name] != prefix)
16
+ raise ::Trax::Model::Errors::DuplicatePrefixRegistered.new(:prefix => prefix, :model => self.source.name)
17
+ end
18
+
19
+ ::Trax::Model::UniqueId.config.uuid_map[self.source.name] = prefix
20
+ ::Trax::Model::UUIDPrefix.new(prefix)
21
+ },
22
+ :validates => {
23
+ :exclusion => {
24
+ :in => ::Trax::Model::Registry.uuid_map.values
25
+ },
26
+ :inclusion => {
27
+ :in => ::Trax::Model::UUIDPrefix.all,
28
+ :message => "%{value} not a valid uuid prefix!\nRun Trax::Model::UUIDPrefix.all for valid prefix list"
29
+ },
30
+ :allow_nil => true
31
+ }
32
+
33
+ option :uuid_column, :default => ::Trax::Model::UniqueId.config.uuid_column
34
+ end
35
+
36
+ #grab prefix from uuid registry if uuids are defined in an initializer
37
+ if ::Trax::Model.mixin_registry.key?(:unique_id) && ::Trax::Model::UUID.klass_prefix_map.key?(self.name)
38
+ self.unique_id_config.uuid_prefix = ::Trax::Model::UUID.klass_prefix_map[self.name]
39
+ end
40
+ end
41
+
42
+ after_included do |options|
43
+ self.unique_id_config.merge!(options)
44
+
45
+ if(self.unique_id_config.uuid_prefix)
46
+ default_value_for(:"#{self.unique_id_config.uuid_column}") {
47
+ ::Trax::Model::UUID.generate(self.unique_id_config.uuid_prefix)
48
+ }
10
49
  end
11
50
  end
12
51
 
13
52
  def uuid
14
- uuid_column = self.class.trax_defaults.uuid_column
53
+ uuid_column = self.class.unique_id_config.uuid_column
15
54
  uuid_value = (uuid_column == "uuid") ? super : __send__(uuid_column)
16
55
 
17
56
  ::Trax::Model::UUID.new(uuid_value)
@@ -33,17 +72,20 @@ module Trax
33
72
  end
34
73
 
35
74
  module ClassMethods
36
- delegate :uuid_prefix, :to => :trax_defaults
37
- delegate :uuid_column, :to => :trax_defaults
38
-
39
- def defaults(*args)
40
- super(*args)
41
-
42
- self.default_value_for(:"#{self.trax_defaults.uuid_column}") {
43
- ::Trax::Model::UUID.generate(self.trax_defaults.uuid_prefix)
44
- }
75
+ def uuid_prefix
76
+ self.unique_id_config.uuid_prefix
45
77
  end
46
78
  end
79
+
80
+ # def self.apply_mixin(target, options)
81
+ # target.unique_id_config.merge!(options)
82
+ #
83
+ # if(target.unique_id_config.uuid_prefix)
84
+ # target.default_value_for(:"#{target.unique_id_config.uuid_column}") {
85
+ # ::Trax::Model::UUID.generate(target.unique_id_config.uuid_prefix)
86
+ # }
87
+ # end
88
+ # end
47
89
  end
48
90
  end
49
91
  end