trax_model 0.0.99 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +0 -1
- data/lib/trax/model.rb +16 -1
- data/lib/trax/model/attributes/dsl.rb +0 -12
- data/lib/trax/model/attributes/types/enum.rb +2 -2
- data/lib/trax/model/attributes/types/string.rb +1 -1
- data/lib/trax/model/attributes/types/struct.rb +2 -4
- data/lib/trax/model/cache_key.rb +20 -0
- data/lib/trax/model/errors.rb +1 -1
- data/lib/trax/model/mixins.rb +2 -0
- data/lib/trax/model/mixins/cached_find_by.rb +39 -0
- data/lib/trax/model/mixins/cached_relations.rb +63 -0
- data/lib/trax_model/version.rb +1 -1
- data/spec/db/schema/default_tables.rb +40 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/models.rb +93 -16
- data/spec/support/pg/models.rb +1 -1
- data/spec/trax/model/attributes/types/struct_spec.rb +0 -6
- data/spec/trax/model/cache_key_spec.rb +14 -0
- data/spec/trax/model/mixins/cached_find_by_spec.rb +95 -0
- data/spec/trax/model/mixins/cached_relations_spec.rb +152 -0
- data/spec/trax/model_spec.rb +25 -1
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff781e2ed15ed9b8de74a0bc0524c36f1dff35f1
|
4
|
+
data.tar.gz: 310c34411bca3ed6e975a68f65dfe5dd7b0d3a4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 883b1cb879b34e218a1687aeb9a315154622b90b4b31de104278e7df4348eb630f8a1b3ebbfd865b202aa4cf93415092635c751a6ef1d30d2922627445e46d72
|
7
|
+
data.tar.gz: d64469203fda20f16669269aab3d462f3060f317ef684e2ad7c67cde6b5d1644b4cf0f47113c3a04c31083f15bc472795b15aa013e6de3ad4be5517bdebca3c7
|
data/Rakefile
CHANGED
data/lib/trax/model.rb
CHANGED
@@ -22,7 +22,9 @@ module Trax
|
|
22
22
|
extend ::ActiveSupport::Autoload
|
23
23
|
|
24
24
|
autoload :Attributes
|
25
|
+
autoload :CacheKey
|
25
26
|
autoload :Config
|
27
|
+
autoload :Concerns
|
26
28
|
autoload :CoreExtensions
|
27
29
|
autoload :ExtensionsFor
|
28
30
|
autoload :Errors
|
@@ -42,6 +44,7 @@ module Trax
|
|
42
44
|
define_configuration_options! do
|
43
45
|
option :auto_include, :default => false
|
44
46
|
option :auto_include_mixins, :default => []
|
47
|
+
option :cache, :default => ::ActiveSupport::Cache::MemoryStore.new
|
45
48
|
end
|
46
49
|
|
47
50
|
#like reverse merge, only assigns attributes which have not yet been assigned
|
@@ -64,11 +67,23 @@ module Trax
|
|
64
67
|
mixin_registry[mixin_key] = mixin_klass
|
65
68
|
end
|
66
69
|
|
70
|
+
def self.cache
|
71
|
+
::Trax::Model.config.cache
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.cache=(cache_store)
|
75
|
+
::Trax::Model.configure do |config|
|
76
|
+
config.cache = cache_store
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
67
80
|
def self.root
|
68
81
|
::Pathname.new(::File.path(__FILE__))
|
69
82
|
end
|
70
83
|
|
71
84
|
def self.eager_autoload_mixins!
|
85
|
+
::Trax::Model::Mixins::CachedFindBy
|
86
|
+
::Trax::Model::Mixins::CachedRelations
|
72
87
|
::Trax::Model::Mixins::FieldScopes
|
73
88
|
::Trax::Model::Mixins::Freezable
|
74
89
|
::Trax::Model::Mixins::IdScopes
|
@@ -104,7 +119,7 @@ module Trax
|
|
104
119
|
raise ::Trax::Model::Errors::MixinNotRegistered.new(
|
105
120
|
model: self.name,
|
106
121
|
mixin: key
|
107
|
-
)
|
122
|
+
) unless ::Trax::Model.mixin_registry.key?(key)
|
108
123
|
|
109
124
|
mixin_module = ::Trax::Model.mixin_registry[key]
|
110
125
|
self.registered_mixins[key] = mixin_module
|
@@ -13,18 +13,6 @@ module Trax
|
|
13
13
|
self.instance_variable_set("@_attribute_definitions_block", block)
|
14
14
|
end
|
15
15
|
|
16
|
-
#recursively search direct parent classes for attribute definitions, so we can fully support
|
17
|
-
#inheritance
|
18
|
-
def fetch_attribute_definitions_in_chain(_attribute_definitions_blocks = [], klass=nil)
|
19
|
-
_attribute_definitions_blocks.push(klass.instance_variable_get("@_attribute_definitions_block")) if klass && klass.instance_variable_defined?("@_attribute_definitions_block")
|
20
|
-
|
21
|
-
if klass && klass.superclass != ::ActiveRecord::Base
|
22
|
-
return fetch_attribute_definitions_in_chain(_attribute_definitions_blocks, klass.superclass)
|
23
|
-
else
|
24
|
-
return _attribute_definitions_blocks.compact
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
16
|
def fields_module
|
29
17
|
@fields_module ||= begin
|
30
18
|
module_name = "#{self.name}::Fields"
|
@@ -7,8 +7,8 @@ module Trax
|
|
7
7
|
def self.define_attribute(klass, attribute_name, **options, &block)
|
8
8
|
klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
|
9
9
|
|
10
|
-
attribute_klass = if options.key?(:
|
11
|
-
_klass_prototype = options[:
|
10
|
+
attribute_klass = if options.key?(:extends)
|
11
|
+
_klass_prototype = options[:extends].is_a?(::String) ? options[:extends].safe_constantize : options[:extends]
|
12
12
|
_klass = ::Trax::Core::NamedClass.new(klass_name, _klass_prototype, :parent_definition => klass, &block)
|
13
13
|
_klass
|
14
14
|
else
|
@@ -7,8 +7,8 @@ module Trax
|
|
7
7
|
class Struct < ::Trax::Model::Attributes::Type
|
8
8
|
def self.define_attribute(klass, attribute_name, **options, &block)
|
9
9
|
klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
|
10
|
-
attribute_klass = if options.key?(:
|
11
|
-
_klass_prototype = options[:
|
10
|
+
attribute_klass = if options.key?(:extends)
|
11
|
+
_klass_prototype = options[:extends].is_a?(::String) ? options[:extends].safe_constantize : options[:extends]
|
12
12
|
_klass = ::Trax::Core::NamedClass.new(klass_name, _klass_prototype, :parent_definition => klass, &block)
|
13
13
|
_klass.include(::Trax::Model::ExtensionsFor::Struct)
|
14
14
|
_klass
|
@@ -82,8 +82,6 @@ module Trax
|
|
82
82
|
end
|
83
83
|
|
84
84
|
model.delegate(getter_method, :to => attribute_name)
|
85
|
-
|
86
|
-
model.attribute_names << _property.to_s unless model.attribute_names.include?(_property.to_s)
|
87
85
|
end
|
88
86
|
end
|
89
87
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
class CacheKey < SimpleDelegator
|
4
|
+
CACHE_OPTION_KEYS = [ :expires_in ].freeze
|
5
|
+
|
6
|
+
attr_reader :options, :search_params
|
7
|
+
|
8
|
+
def initialize(*args, **params)
|
9
|
+
params.symbolize_keys!
|
10
|
+
@options = params.extract!(*CACHE_OPTION_KEYS)
|
11
|
+
@search_params = params
|
12
|
+
@obj = ::Set[*args.sort, params.sort].flatten.to_a
|
13
|
+
end
|
14
|
+
|
15
|
+
def __getobj__
|
16
|
+
@obj
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/trax/model/errors.rb
CHANGED
@@ -17,7 +17,7 @@ module Trax
|
|
17
17
|
argument :model
|
18
18
|
|
19
19
|
message {
|
20
|
-
"#{model} tried to load mixin: #{mixin},
|
20
|
+
"#{model} tried to load mixin: #{mixin}, which does not exist in " \
|
21
21
|
"registry. Registered mixins were #{::Trax::Model.mixin_registry.keys.join(', ')} \n"
|
22
22
|
}
|
23
23
|
end
|
data/lib/trax/model/mixins.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Mixins
|
4
|
+
module CachedFindBy
|
5
|
+
extend ::Trax::Model::Mixin
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def cached_find_by(**params)
|
9
|
+
cache_key = ::Trax::Model::CacheKey.new(self.name.underscore.pluralize, '.find_by', **params)
|
10
|
+
|
11
|
+
::Trax::Model.cache.fetch(cache_key, cache_key.options) do
|
12
|
+
self.find_by(cache_key.search_params)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def cached_where(**params)
|
17
|
+
cache_key = ::Trax::Model::CacheKey.new(self.name.underscore.pluralize, '.where', **params)
|
18
|
+
|
19
|
+
::Trax::Model.cache.fetch(cache_key, cache_key.options) do
|
20
|
+
self.where(cache_key.search_params).to_a
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear_cached_find_by(**params)
|
25
|
+
cache_key = ::Trax::Model::CacheKey.new(self.name.underscore.pluralize, '.find_by', **params)
|
26
|
+
|
27
|
+
::Trax::Model.cache.delete(cache_key)
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear_cached_where(**params)
|
31
|
+
cache_key = ::Trax::Model::CacheKey.new(self.name.underscore.pluralize, '.where', **params)
|
32
|
+
|
33
|
+
::Trax::Model.cache.delete(cache_key)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Trax
|
2
|
+
module Model
|
3
|
+
module Mixins
|
4
|
+
module CachedRelations
|
5
|
+
extend ::Trax::Model::Mixin
|
6
|
+
|
7
|
+
include ::Trax::Model::Mixins::CachedFindBy
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def cached_belongs_to(relation_name, **options)
|
11
|
+
define_method("cached_#{relation_name}") do
|
12
|
+
relation = self.class.reflect_on_association(relation_name)
|
13
|
+
foreign_key = (relation.foreign_key || "#{relation.name}_id").to_sym
|
14
|
+
params = { :id => self.__send__(foreign_key) }.merge(options)
|
15
|
+
relation.klass.cached_find_by(**params)
|
16
|
+
end
|
17
|
+
|
18
|
+
define_method("clear_cached_#{relation_name}") do
|
19
|
+
relation = self.class.reflect_on_association(relation_name)
|
20
|
+
foreign_key = (relation.foreign_key || :"#{relation.name}_id").to_sym
|
21
|
+
params = { :id => self.__send__(foreign_key) }.merge(options)
|
22
|
+
relation.klass.clear_cached_find_by(**params)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def cached_has_one(relation_name, **options)
|
27
|
+
define_method("cached_#{relation_name}") do
|
28
|
+
relation = self.class.reflect_on_association(relation_name)
|
29
|
+
foreign_key = (relation.foreign_key || "#{relation.name}_id").to_sym
|
30
|
+
params = { foreign_key => self.__send__(:id) }.merge(options)
|
31
|
+
params.merge!(relation.klass.instance_eval(&relation.scope).where_values_hash.symbolize_keys) if relation.scope
|
32
|
+
relation.klass.cached_find_by(**params)
|
33
|
+
end
|
34
|
+
|
35
|
+
define_method("clear_cached_#{relation_name}") do
|
36
|
+
relation = self.class.reflect_on_association(relation_name)
|
37
|
+
foreign_key = (relation.foreign_key || :"#{relation.name}_id").to_sym
|
38
|
+
params = { foreign_key => self.__send__(:id) }.merge(options)
|
39
|
+
params.merge!(relation.klass.instance_eval(&relation.scope).where_values_hash.symbolize_keys) if relation.scope
|
40
|
+
relation.klass.clear_cached_find_by(**params)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def cached_has_many(relation_name, **options)
|
45
|
+
define_method("cached_#{relation_name}") do
|
46
|
+
relation = self.class.reflect_on_association(relation_name)
|
47
|
+
foreign_key = :"#{relation.foreign_key}"
|
48
|
+
params = {foreign_key => self.__send__(:id)}.merge(options)
|
49
|
+
relation.klass.cached_where(**params)
|
50
|
+
end
|
51
|
+
|
52
|
+
define_method("clear_cached_#{relation_name}") do
|
53
|
+
relation = self.class.reflect_on_association(relation_name)
|
54
|
+
foreign_key = :"#{relation.foreign_key}"
|
55
|
+
params = {foreign_key => self.__send__(:id)}.merge(options)
|
56
|
+
relation.klass.clear_cached_where(**params)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/trax_model/version.rb
CHANGED
@@ -1,10 +1,29 @@
|
|
1
1
|
DEFAULT_TABLES = Proc.new do
|
2
|
+
create_table "subscribers", :force => true do |t|
|
3
|
+
t.string "name"
|
4
|
+
t.string "uuid"
|
5
|
+
t.datetime "created_at", :null => false
|
6
|
+
t.datetime "updated_at", :null => false
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table "manufacturers", :force => true do |t|
|
10
|
+
t.string "name"
|
11
|
+
t.string "url"
|
12
|
+
t.string "uuid"
|
13
|
+
t.integer "subscriber_id"
|
14
|
+
t.datetime "created_at", :null => false
|
15
|
+
t.datetime "updated_at", :null => false
|
16
|
+
end
|
17
|
+
|
2
18
|
create_table "vehicles", :force => true do |t|
|
19
|
+
t.string "name"
|
3
20
|
t.string "type"
|
4
21
|
t.integer "kind"
|
5
22
|
t.integer "make"
|
6
23
|
t.integer "model"
|
7
24
|
t.string "uuid"
|
25
|
+
t.integer "cost"
|
26
|
+
t.integer "manufacturer_id"
|
8
27
|
t.datetime "created_at", :null => false
|
9
28
|
t.datetime "updated_at", :null => false
|
10
29
|
end
|
@@ -41,6 +60,8 @@ DEFAULT_TABLES = Proc.new do
|
|
41
60
|
t.string "subdomain"
|
42
61
|
t.string "website"
|
43
62
|
t.integer "status"
|
63
|
+
t.integer "subscriber_id"
|
64
|
+
t.string "name"
|
44
65
|
t.datetime "created_at", :null => false
|
45
66
|
t.datetime "updated_at", :null => false
|
46
67
|
end
|
@@ -55,6 +76,25 @@ DEFAULT_TABLES = Proc.new do
|
|
55
76
|
create_table "people", :force => true do |t|
|
56
77
|
t.string "name"
|
57
78
|
t.string "uuid"
|
79
|
+
t.integer "vehicle_id"
|
80
|
+
t.datetime "created_at", :null => false
|
81
|
+
t.datetime "updated_at", :null => false
|
82
|
+
end
|
83
|
+
|
84
|
+
create_table "users", :force => true do |t|
|
85
|
+
t.string "name"
|
86
|
+
t.string "uuid"
|
87
|
+
t.integer "role"
|
88
|
+
t.integer "subscriber_id"
|
89
|
+
t.datetime "created_at", :null => false
|
90
|
+
t.datetime "updated_at", :null => false
|
91
|
+
end
|
92
|
+
|
93
|
+
create_table "animals", :force => true do |t|
|
94
|
+
t.string "name"
|
95
|
+
t.string "uuid"
|
96
|
+
t.string "type"
|
97
|
+
t.string "characteristics" # `string` is a workaround for lack of jsonb support in sqlite
|
58
98
|
t.datetime "created_at", :null => false
|
59
99
|
t.datetime "updated_at", :null => false
|
60
100
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/models.rb
CHANGED
@@ -14,15 +14,6 @@ end
|
|
14
14
|
if ENV["DB"] == "postgres"
|
15
15
|
require_relative 'pg/models'
|
16
16
|
end
|
17
|
-
#
|
18
|
-
# class Blueprint < ::Trax::Core::Blueprint
|
19
|
-
# class Vehicle < ::Trax::Core::Blueprint
|
20
|
-
# enum :kind do
|
21
|
-
# define :car, 1, :type => "Vehicle::Car"
|
22
|
-
# define :truck, 2, :type => "Vehicle::Truck"
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
# end
|
26
17
|
|
27
18
|
class Product < ::ActiveRecord::Base
|
28
19
|
include ::Trax::Model
|
@@ -35,7 +26,6 @@ class Product < ::ActiveRecord::Base
|
|
35
26
|
|
36
27
|
define_attributes do
|
37
28
|
string :name
|
38
|
-
# float :price
|
39
29
|
|
40
30
|
integer :in_stock_quantity
|
41
31
|
integer :out_of_stock_quantity
|
@@ -76,18 +66,65 @@ module Products
|
|
76
66
|
end
|
77
67
|
end
|
78
68
|
|
69
|
+
class Subscriber < ::ActiveRecord::Base
|
70
|
+
include ::Trax::Model
|
71
|
+
include ::Trax::Model::Attributes::Dsl
|
72
|
+
|
73
|
+
mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "4f" },
|
74
|
+
:cached_find_by => true,
|
75
|
+
:cached_relations => true
|
76
|
+
|
77
|
+
has_many :manufacturers, :class_name => "Manufacturer"
|
78
|
+
cached_has_many :manufacturers
|
79
|
+
|
80
|
+
has_one :admin_user, -> { by_role(:admin) }, :class_name => "User", :foreign_key => :subscriber_id
|
81
|
+
cached_has_one :admin_user
|
82
|
+
has_one :widget
|
83
|
+
cached_has_one :widget
|
84
|
+
end
|
85
|
+
|
86
|
+
class Manufacturer < ::ActiveRecord::Base
|
87
|
+
include ::Trax::Model
|
88
|
+
include ::Trax::Model::Attributes::Dsl
|
89
|
+
|
90
|
+
mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "3d" },
|
91
|
+
:cached_find_by => true,
|
92
|
+
:cached_relations => true
|
93
|
+
|
94
|
+
has_many :vehicles
|
95
|
+
def cached_vehicles
|
96
|
+
::Vehicle.cached_where(:manufacturer_id => self.id)
|
97
|
+
end
|
98
|
+
|
99
|
+
belongs_to :subscriber
|
100
|
+
cached_belongs_to :subscriber
|
101
|
+
end
|
102
|
+
|
79
103
|
class Vehicle < ::ActiveRecord::Base
|
80
104
|
include ::Trax::Model
|
81
105
|
include ::Trax::Model::Attributes::Dsl
|
82
106
|
|
83
107
|
mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "9c" },
|
84
|
-
:sti_enum => true
|
108
|
+
:sti_enum => true,
|
109
|
+
:cached_find_by => true
|
110
|
+
|
85
111
|
|
86
112
|
define_attributes do
|
87
113
|
enum :kind do
|
88
114
|
define :car, 1, :type => "Vehicle::Car"
|
89
115
|
define :truck, 2, :type => "Vehicle::Truck"
|
90
116
|
end
|
117
|
+
|
118
|
+
integer :cost
|
119
|
+
end
|
120
|
+
|
121
|
+
def subscriber_id
|
122
|
+
self.manufacturer.subscriber_id
|
123
|
+
end
|
124
|
+
|
125
|
+
belongs_to :manufacturer
|
126
|
+
def cached_manufacturer
|
127
|
+
::Manufacturer.cached_find_by(:id => self.manufacturer_id)
|
91
128
|
end
|
92
129
|
|
93
130
|
class Car < ::Vehicle
|
@@ -100,14 +137,15 @@ end
|
|
100
137
|
class Widget < ::ActiveRecord::Base
|
101
138
|
include ::Trax::Model
|
102
139
|
|
103
|
-
mixins :unique_id => {
|
104
|
-
|
105
|
-
:uuid_prefix => "2a"
|
106
|
-
}
|
140
|
+
mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "2a" },
|
141
|
+
:cached_relations => true
|
107
142
|
|
108
143
|
validates :subdomain, :subdomain => true, :allow_nil => true
|
109
144
|
validates :email_address, :email => true, :allow_nil => true
|
110
145
|
validates :website, :url => true, :allow_nil => true
|
146
|
+
|
147
|
+
belongs_to :subscriber
|
148
|
+
cached_belongs_to :subscriber
|
111
149
|
end
|
112
150
|
|
113
151
|
class Message < ::ActiveRecord::Base
|
@@ -141,7 +179,13 @@ end
|
|
141
179
|
class Person < ::ActiveRecord::Base
|
142
180
|
include ::Trax::Model
|
143
181
|
|
144
|
-
mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "5a" }
|
182
|
+
mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "5a" },
|
183
|
+
:cached_find_by => true
|
184
|
+
|
185
|
+
belongs_to :vehicle
|
186
|
+
def cached_vehicle
|
187
|
+
::Vehicle.cached_find_by(:id => self.vehicle_id)
|
188
|
+
end
|
145
189
|
end
|
146
190
|
|
147
191
|
class StoreCategory < ::Trax::Core::Types::Struct
|
@@ -153,3 +197,36 @@ class StoreCategory < ::Trax::Core::Types::Struct
|
|
153
197
|
string :keywords
|
154
198
|
end
|
155
199
|
end
|
200
|
+
|
201
|
+
class User < ::ActiveRecord::Base
|
202
|
+
include ::Trax::Model
|
203
|
+
include ::Trax::Model::Attributes::Dsl
|
204
|
+
|
205
|
+
mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "3e" },
|
206
|
+
:cached_find_by => true,
|
207
|
+
:cached_relations => true
|
208
|
+
|
209
|
+
define_attributes do
|
210
|
+
enum :role, :default => :staff do
|
211
|
+
define :staff, 1
|
212
|
+
define :admin, 2
|
213
|
+
define :billing, 3
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
class Animal < ::ActiveRecord::Base
|
219
|
+
include ::Trax::Model
|
220
|
+
include ::Trax::Model::Attributes::Dsl
|
221
|
+
end
|
222
|
+
|
223
|
+
class Mammal < ::Animal
|
224
|
+
include ::Trax::Model
|
225
|
+
include ::Trax::Model::Attributes::Dsl
|
226
|
+
|
227
|
+
define_attributes do
|
228
|
+
struct :characteristics, :model_accessors => true do
|
229
|
+
string :fun_facts
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
data/spec/support/pg/models.rb
CHANGED
@@ -119,7 +119,7 @@ module Ecommerce
|
|
119
119
|
string :name, :default => "Some Shoe Name"
|
120
120
|
boolean :active, :default => true
|
121
121
|
|
122
|
-
struct :custom_fields, :
|
122
|
+
struct :custom_fields, :extends => ::Ecommerce::Product::Fields::CustomFields do
|
123
123
|
string :primary_utility, :default => "Skateboarding"
|
124
124
|
string :sole_material
|
125
125
|
boolean :has_shoelaces
|
@@ -16,12 +16,6 @@ describe ::Trax::Model::Attributes::Types::Struct, :postgres => true do
|
|
16
16
|
end
|
17
17
|
}
|
18
18
|
|
19
|
-
it {
|
20
|
-
::Ecommerce::ShippingAttributes.fields[:specifics].properties.each do |_property|
|
21
|
-
expect(subject.class.attribute_names).to include(_property.to_s)
|
22
|
-
end
|
23
|
-
}
|
24
|
-
|
25
19
|
context "initializing model and setting delegated attributes directly", :delegated_attributes => { :cost => 5, :tax => 5, :delivery_time => "5 days" } do
|
26
20
|
self::DELEGATED_ATTRIBUTES = { :cost => 5, :tax => 5, :delivery_time => "5 days" }
|
27
21
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Trax::Model::CacheKey do
|
4
|
+
subject{ described_class }
|
5
|
+
|
6
|
+
context "cache key" do
|
7
|
+
let(:fake_id) { 1 }
|
8
|
+
let(:expires) { 2.hours }
|
9
|
+
let(:cache_options) { {:expires_in => expires } }
|
10
|
+
subject { ::Trax::Model::CacheKey.new("posts", :id => fake_id, :expires_in => expires) }
|
11
|
+
it { expect(subject.first).to eq "posts" }
|
12
|
+
it { expect(subject.options).to eq cache_options }
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Trax::Model::Mixins::CachedFindBy do
|
4
|
+
before(:all) do
|
5
|
+
::ActiveRecord::Base.logger = ::Logger.new(::STDOUT)
|
6
|
+
end
|
7
|
+
|
8
|
+
before { ::Vehicle.destroy_all }
|
9
|
+
before { ::Manufacturer.destroy_all }
|
10
|
+
before { ::Subscriber.destroy_all }
|
11
|
+
before { ::Person.destroy_all }
|
12
|
+
before { ::Trax::Model.cache.clear }
|
13
|
+
|
14
|
+
let(:subscriber) { ::Subscriber.create(:name => "automatch_usa") }
|
15
|
+
let(:subscriber_2) { ::Subscriber.create(:name => "larryhmiller") }
|
16
|
+
|
17
|
+
let(:ford) { ::Manufacturer.create(:name => "ford", :url => "ford.com", :subscriber_id => subscriber.id) }
|
18
|
+
let(:toyota) { ::Manufacturer.create(:name => "toyota", :url => "toyota.com", :subscriber_id => subscriber_2.id) }
|
19
|
+
let(:vehicle_1) { ::Vehicle.create(:kind => :car, :manufacturer => ford, :cost => 20_000, :name => "pinto") }
|
20
|
+
let(:vehicle_2) { ::Vehicle.create(:kind => :truck, :manufacturer => ford, :cost => 20_000, :name => "f150")}
|
21
|
+
let(:vehicle_3) { ::Vehicle.create(:kind => :truck, :manufacturer => toyota, :cost => 15_000) }
|
22
|
+
let(:person_1) { ::Person.create(:name => "jason", :vehicle_id => vehicle_1.id) }
|
23
|
+
let(:person_2) { ::Person.create(:name => "steve", :vehicle_id => vehicle_1.id) }
|
24
|
+
|
25
|
+
subject { ford }
|
26
|
+
|
27
|
+
context ".cached_find_by" do
|
28
|
+
before do
|
29
|
+
person_1
|
30
|
+
person_2
|
31
|
+
subject
|
32
|
+
end
|
33
|
+
|
34
|
+
it "cache key exists" do
|
35
|
+
::Manufacturer.cached_find_by(:id => vehicle_1.manufacturer_id)
|
36
|
+
|
37
|
+
cache_key = ::Trax::Model::CacheKey.new('manufacturers', '.find_by', {:id => vehicle_1.manufacturer_id})
|
38
|
+
expect(::Trax::Model.cache.exist?(cache_key)).to eq true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "caches relation" do
|
42
|
+
vehicle_1
|
43
|
+
expect(vehicle_1.cached_manufacturer.name).to eq subject.name
|
44
|
+
|
45
|
+
man = ::Manufacturer.find(vehicle_1.cached_manufacturer.id)
|
46
|
+
man.update_attributes(:name => "whatever")
|
47
|
+
vehicle_1.reload
|
48
|
+
expect(vehicle_1.cached_manufacturer.name).to eq "ford"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "cache can be cleared by params" do
|
52
|
+
vehicle_1
|
53
|
+
expect(vehicle_1.cached_manufacturer.name).to eq subject.name
|
54
|
+
|
55
|
+
man = ::Manufacturer.find(vehicle_1.cached_manufacturer.id)
|
56
|
+
man.update_attributes(:name => "whatever")
|
57
|
+
vehicle_1.reload
|
58
|
+
expect(vehicle_1.cached_manufacturer.name).to eq "ford"
|
59
|
+
::Manufacturer.clear_cached_find_by(:id => man.id)
|
60
|
+
expect(vehicle_1.cached_manufacturer.name).to eq "whatever"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context ".cached_where" do
|
65
|
+
subject { ::Vehicle.cached_where(:manufacturer_id => ford.id) }
|
66
|
+
|
67
|
+
before do
|
68
|
+
ford
|
69
|
+
vehicle_1
|
70
|
+
vehicle_2
|
71
|
+
subject
|
72
|
+
end
|
73
|
+
|
74
|
+
it "cache key exists" do
|
75
|
+
cache_key = ::Trax::Model::CacheKey.new('vehicles', '.where', {:manufacturer_id => ford.id})
|
76
|
+
expect(::Trax::Model.cache.exist?(cache_key)).to eq true
|
77
|
+
end
|
78
|
+
|
79
|
+
it "caches result" do
|
80
|
+
expect(subject[0].name).to eq vehicle_1.name
|
81
|
+
vehicle_1.update_attributes(:name => "whatever")
|
82
|
+
vehicle_1.reload
|
83
|
+
expect(subject[0].name).to eq "pinto"
|
84
|
+
end
|
85
|
+
|
86
|
+
context ".clear_cached_where" do
|
87
|
+
it do
|
88
|
+
cache_key = ::Trax::Model::CacheKey.new('vehicles', '.where', {:manufacturer_id => ford.id})
|
89
|
+
expect(::Trax::Model.cache.exist?(cache_key)).to eq true
|
90
|
+
::Vehicle.clear_cached_where(cache_key.search_params)
|
91
|
+
expect(::Trax::Model.cache.exist?(cache_key)).to eq false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Trax::Model::Mixins::CachedRelations do
|
4
|
+
before(:all) do
|
5
|
+
::ActiveRecord::Base.logger = ::Logger.new(::STDOUT)
|
6
|
+
end
|
7
|
+
|
8
|
+
before { ::Vehicle.destroy_all }
|
9
|
+
before { ::Manufacturer.destroy_all }
|
10
|
+
before { ::Subscriber.destroy_all }
|
11
|
+
before { ::Person.destroy_all }
|
12
|
+
before { ::Trax::Model.cache.clear }
|
13
|
+
|
14
|
+
let(:subscriber) { ::Subscriber.create(:name => "automatch_usa") }
|
15
|
+
|
16
|
+
let(:ford) { ::Manufacturer.create(:name => "ford", :url => "ford.com", :subscriber_id => subscriber.id) }
|
17
|
+
let(:toyota) { ::Manufacturer.create(:name => "toyota", :url => "toyota.com", :subscriber_id => subscriber.id) }
|
18
|
+
let(:vehicle_1) { ::Vehicle.create(:kind => :car, :manufacturer => ford, :cost => 20_000, :name => "pinto") }
|
19
|
+
let(:vehicle_2) { ::Vehicle.create(:kind => :truck, :manufacturer => ford, :cost => 20_000, :name => "f150") }
|
20
|
+
let(:vehicle_3) { ::Vehicle.create(:kind => :truck, :manufacturer => toyota, :cost => 15_000) }
|
21
|
+
|
22
|
+
subject{ ford }
|
23
|
+
|
24
|
+
context ".cached_belongs_to" do
|
25
|
+
before do
|
26
|
+
subject.cached_subscriber
|
27
|
+
end
|
28
|
+
|
29
|
+
it "cache key exists" do
|
30
|
+
cache_key = ::Trax::Model::CacheKey.new('subscribers', '.find_by', {:id => subject.subscriber_id} )
|
31
|
+
expect(::Trax::Model.cache.exist?(cache_key)).to eq true
|
32
|
+
end
|
33
|
+
|
34
|
+
it "caches relation" do
|
35
|
+
expect(subject.cached_subscriber.name).to eq subscriber.name
|
36
|
+
_subscriber = ::Subscriber.find(subscriber.id)
|
37
|
+
_subscriber.update_attributes(:name => 'whatever')
|
38
|
+
expect(subject.cached_subscriber.name).to eq subscriber.name
|
39
|
+
end
|
40
|
+
|
41
|
+
it "clears cached belongs to relation" do
|
42
|
+
expect(subject.cached_subscriber.name).to eq subscriber.name
|
43
|
+
_subscriber = ::Subscriber.find(subscriber.id)
|
44
|
+
_updated_subscriber_name = 'whatever'
|
45
|
+
_subscriber.update_attributes(:name => _updated_subscriber_name)
|
46
|
+
expect(subject.cached_subscriber.name).to eq subscriber.name
|
47
|
+
subject.clear_cached_subscriber
|
48
|
+
expect(subject.cached_subscriber.name).to eq _updated_subscriber_name
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context ".cached_has_one" do
|
53
|
+
subject! { subscriber }
|
54
|
+
let!(:widget) { ::Widget.create(:name => "anything", :subscriber_id => subscriber.id) }
|
55
|
+
let!(:cached_widget) { subject.cached_widget }
|
56
|
+
|
57
|
+
it "cache key exists" do
|
58
|
+
cache_key = ::Trax::Model::CacheKey.new('widgets', '.find_by', {:subscriber_id => subject.id} )
|
59
|
+
expect(::Trax::Model.cache.exist?(cache_key)).to eq true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "caches relation" do
|
63
|
+
widget_name = widget.name
|
64
|
+
expect(cached_widget.name).to eq widget_name
|
65
|
+
_widget = ::Widget.find(cached_widget.id)
|
66
|
+
_widget.update_attributes(:name => "bob")
|
67
|
+
_widget.reload
|
68
|
+
expect(subject.cached_widget.name).to eq widget_name
|
69
|
+
end
|
70
|
+
|
71
|
+
it "clears cached has one relation" do
|
72
|
+
widget_name = widget.name
|
73
|
+
updated_widget_name = 'new_widget_name'
|
74
|
+
expect(cached_widget.name).to eq widget_name
|
75
|
+
_widget = ::Widget.find(cached_widget.id)
|
76
|
+
_widget.update_attributes(:name => updated_widget_name)
|
77
|
+
_widget.reload
|
78
|
+
expect(subject.cached_widget.name).to eq widget_name
|
79
|
+
subject.clear_cached_widget
|
80
|
+
expect(subject.cached_widget.name).to eq updated_widget_name
|
81
|
+
end
|
82
|
+
|
83
|
+
context "scoped by proc" do
|
84
|
+
subject! { subscriber }
|
85
|
+
let!(:admin_user) { ::User.create(:name => "milton", :role => :admin, :subscriber_id => subscriber.id) }
|
86
|
+
let!(:cached_admin_user) { subject.cached_admin_user }
|
87
|
+
|
88
|
+
it "cache key exists" do
|
89
|
+
cache_key = ::Trax::Model::CacheKey.new('users', '.find_by', {:subscriber_id => subject.id, :role => ::User::Fields::Role.new(:admin).to_i} )
|
90
|
+
expect(::Trax::Model.cache.exist?(cache_key)).to eq true
|
91
|
+
end
|
92
|
+
|
93
|
+
it "caches relation" do
|
94
|
+
admin_user_name = admin_user.name
|
95
|
+
expect(cached_admin_user.name).to eq admin_user_name
|
96
|
+
_user = ::User.find(admin_user.id)
|
97
|
+
_user.update_attributes(:name => "bob")
|
98
|
+
_user.reload
|
99
|
+
expect(subject.cached_admin_user.name).to eq admin_user_name
|
100
|
+
end
|
101
|
+
|
102
|
+
it "clears cached has one relation" do
|
103
|
+
admin_user_name = admin_user.name
|
104
|
+
updated_admin_user_name = "bob"
|
105
|
+
expect(cached_admin_user.name).to eq admin_user_name
|
106
|
+
_user = ::User.find(admin_user.id)
|
107
|
+
_user.update_attributes(:name => updated_admin_user_name)
|
108
|
+
_user.reload
|
109
|
+
expect(subject.cached_admin_user.name).to eq admin_user_name
|
110
|
+
subject.clear_cached_admin_user
|
111
|
+
expect(subject.cached_admin_user.name).to eq updated_admin_user_name
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context ".cached_has_many" do
|
117
|
+
before do
|
118
|
+
ford
|
119
|
+
toyota
|
120
|
+
end
|
121
|
+
|
122
|
+
subject!{ subscriber.cached_manufacturers }
|
123
|
+
|
124
|
+
it "cache key exists" do
|
125
|
+
cache_key = ::Trax::Model::CacheKey.new('manufacturers', '.where', {:subscriber_id => subscriber.id} )
|
126
|
+
expect(::Trax::Model.cache.exist?(cache_key)).to eq true
|
127
|
+
end
|
128
|
+
|
129
|
+
it "caches relation" do
|
130
|
+
expect(subject).to eq subscriber.manufacturers
|
131
|
+
first_manufacturer_name = subject.first.name
|
132
|
+
|
133
|
+
expect(subject.first.name).to eq subscriber.manufacturers.first.name
|
134
|
+
_manufacturer = ::Manufacturer.find(subject.first.id)
|
135
|
+
_manufacturer.update_attributes(:name => 'whatever')
|
136
|
+
expect(subject.first.name).to eq first_manufacturer_name
|
137
|
+
end
|
138
|
+
|
139
|
+
it "clears cached has many relation" do
|
140
|
+
expect(subject).to eq subscriber.manufacturers
|
141
|
+
first_manufacturer_name = subject.first.name
|
142
|
+
updated_manufacturer_name = 'whatever'
|
143
|
+
expect(subject.first.name).to eq subscriber.manufacturers.first.name
|
144
|
+
_manufacturer = ::Manufacturer.find(subject.first.id)
|
145
|
+
_manufacturer.update_attributes(:name => updated_manufacturer_name)
|
146
|
+
_manufacturer.reload
|
147
|
+
expect(subject.first.name).to eq first_manufacturer_name
|
148
|
+
subscriber.clear_cached_manufacturers
|
149
|
+
expect(subscriber.cached_manufacturers.detect{|m| m.id == _manufacturer.id}.name).to eq updated_manufacturer_name
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/spec/trax/model_spec.rb
CHANGED
@@ -3,6 +3,30 @@ require 'spec_helper'
|
|
3
3
|
describe ::Trax::Model do
|
4
4
|
subject { ::Product }
|
5
5
|
|
6
|
-
its(:trax_registry_key) {
|
6
|
+
its(:trax_registry_key) { is_expected.to eq "product" }
|
7
7
|
it { subject.unique_id_config.uuid_prefix }
|
8
|
+
|
9
|
+
context "with model accessors for struct attribute" do
|
10
|
+
let(:name) { "platypus" }
|
11
|
+
subject { ::Animal.new(:name => name) }
|
12
|
+
|
13
|
+
it { expect(::Animal.fields.constants).to_not include(:Characteristics) }
|
14
|
+
|
15
|
+
context "inheriting model" do
|
16
|
+
let(:fun_facts) { "is most like edible" }
|
17
|
+
let(:characteristics) { {:fun_facts => fun_facts} }
|
18
|
+
subject { ::Mammal.new(:name => name, :characteristics => characteristics) }
|
19
|
+
|
20
|
+
it { expect(::Mammal.fields.constants).to include(:Characteristics) }
|
21
|
+
|
22
|
+
its(:"characteristics.fun_facts") { is_expected.to eq(fun_facts) }
|
23
|
+
it { expect(subject.characteristics.respond_to?(:fun_facts)).to eq(true) }
|
24
|
+
|
25
|
+
context "model accessors" do
|
26
|
+
its(:name) { is_expected.to eq(name) }
|
27
|
+
its(:fun_facts) { is_expected.to eq(fun_facts) }
|
28
|
+
it { expect(subject.respond_to?(:fun_facts)).to eq(true) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
8
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trax_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Ayre
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trax_core
|
@@ -311,6 +311,7 @@ files:
|
|
311
311
|
- lib/trax/model/attributes/types/struct.rb
|
312
312
|
- lib/trax/model/attributes/types/uuid_array.rb
|
313
313
|
- lib/trax/model/attributes/value.rb
|
314
|
+
- lib/trax/model/cache_key.rb
|
314
315
|
- lib/trax/model/core_extensions.rb
|
315
316
|
- lib/trax/model/core_extensions/string.rb
|
316
317
|
- lib/trax/model/errors.rb
|
@@ -334,6 +335,8 @@ files:
|
|
334
335
|
- lib/trax/model/matchable.rb
|
335
336
|
- lib/trax/model/mixin.rb
|
336
337
|
- lib/trax/model/mixins.rb
|
338
|
+
- lib/trax/model/mixins/cached_find_by.rb
|
339
|
+
- lib/trax/model/mixins/cached_relations.rb
|
337
340
|
- lib/trax/model/mixins/field_scopes.rb
|
338
341
|
- lib/trax/model/mixins/freezable.rb
|
339
342
|
- lib/trax/model/mixins/id_scopes.rb
|
@@ -369,6 +372,7 @@ files:
|
|
369
372
|
- spec/trax/model/attributes/types/set_spec.rb
|
370
373
|
- spec/trax/model/attributes/types/struct_spec.rb
|
371
374
|
- spec/trax/model/attributes_spec.rb
|
375
|
+
- spec/trax/model/cache_key_spec.rb
|
372
376
|
- spec/trax/model/errors_spec.rb
|
373
377
|
- spec/trax/model/extensions_for/boolean_spec.rb
|
374
378
|
- spec/trax/model/extensions_for/numeric_spec.rb
|
@@ -376,6 +380,8 @@ files:
|
|
376
380
|
- spec/trax/model/extensions_for/struct_spec.rb
|
377
381
|
- spec/trax/model/extensions_for_spec.rb
|
378
382
|
- spec/trax/model/matchable_spec.rb
|
383
|
+
- spec/trax/model/mixins/cached_find_by_spec.rb
|
384
|
+
- spec/trax/model/mixins/cached_relations_spec.rb
|
379
385
|
- spec/trax/model/mixins/field_scopes_spec.rb
|
380
386
|
- spec/trax/model/mixins/freezable_spec.rb
|
381
387
|
- spec/trax/model/mixins/restorable_spec.rb
|
@@ -428,6 +434,7 @@ test_files:
|
|
428
434
|
- spec/trax/model/attributes/types/set_spec.rb
|
429
435
|
- spec/trax/model/attributes/types/struct_spec.rb
|
430
436
|
- spec/trax/model/attributes_spec.rb
|
437
|
+
- spec/trax/model/cache_key_spec.rb
|
431
438
|
- spec/trax/model/errors_spec.rb
|
432
439
|
- spec/trax/model/extensions_for/boolean_spec.rb
|
433
440
|
- spec/trax/model/extensions_for/numeric_spec.rb
|
@@ -435,6 +442,8 @@ test_files:
|
|
435
442
|
- spec/trax/model/extensions_for/struct_spec.rb
|
436
443
|
- spec/trax/model/extensions_for_spec.rb
|
437
444
|
- spec/trax/model/matchable_spec.rb
|
445
|
+
- spec/trax/model/mixins/cached_find_by_spec.rb
|
446
|
+
- spec/trax/model/mixins/cached_relations_spec.rb
|
438
447
|
- spec/trax/model/mixins/field_scopes_spec.rb
|
439
448
|
- spec/trax/model/mixins/freezable_spec.rb
|
440
449
|
- spec/trax/model/mixins/restorable_spec.rb
|