trax_model 0.0.99 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aff3b6b4389df830349e17d6b8ed2a378effd6af
4
- data.tar.gz: c2e220e45e53c87812eabf18c772db0067bd9f90
3
+ metadata.gz: ff781e2ed15ed9b8de74a0bc0524c36f1dff35f1
4
+ data.tar.gz: 310c34411bca3ed6e975a68f65dfe5dd7b0d3a4c
5
5
  SHA512:
6
- metadata.gz: 6c808f53684e7ed77a9509597cd2d8c4b11d52313f6e7246de544aa54db24d83695a7780331afa6626c32f8552e6c8a786132e0e7226347b56c43247d70c5341
7
- data.tar.gz: 91bcf1d6bf4f70155ac262f58f82b38fc15aff0dca066cccc901d93028c1b24cb739c0f582e6575354e264d21afecb10c92eb4368ae90c9382d0cf70a7b47fb8
6
+ metadata.gz: 883b1cb879b34e218a1687aeb9a315154622b90b4b31de104278e7df4348eb630f8a1b3ebbfd865b202aa4cf93415092635c751a6ef1d30d2922627445e46d72
7
+ data.tar.gz: d64469203fda20f16669269aab3d462f3060f317ef684e2ad7c67cde6b5d1644b4cf0f47113c3a04c31083f15bc472795b15aa013e6de3ad4be5517bdebca3c7
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
1
  require "bundler/gem_tasks"
2
-
@@ -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
- ) unless ::Trax::Model.mixin_registry.key?(key)
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?(:extend)
11
- _klass_prototype = options[:extend].is_a?(::String) ? options[:extend].safe_constantize : options[:extend]
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
@@ -17,7 +17,7 @@ module Trax
17
17
 
18
18
  class Value < ::Trax::Model::Attributes::Value
19
19
  include ::Trax::Model::ExtensionsFor::String
20
-
20
+
21
21
  def self.type; :string end;
22
22
  end
23
23
 
@@ -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?(:extend)
11
- _klass_prototype = options[:extend].is_a?(::String) ? options[:extend].safe_constantize : options[:extend]
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
@@ -17,7 +17,7 @@ module Trax
17
17
  argument :model
18
18
 
19
19
  message {
20
- "#{model} tried to load mixin: #{mixin}, whichdoes not exist in " \
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
@@ -3,6 +3,8 @@ module Trax
3
3
  module Mixins
4
4
  extend ::ActiveSupport::Autoload
5
5
 
6
+ autoload :CachedFindBy
7
+ autoload :CachedRelations
6
8
  autoload :FieldScopes
7
9
  autoload :Freezable
8
10
  autoload :IdScopes
@@ -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
@@ -1,3 +1,3 @@
1
1
  module TraxModel
2
- VERSION = '0.0.99'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -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
@@ -5,6 +5,8 @@ require 'simplecov'
5
5
  require 'trax_model'
6
6
  require 'rails'
7
7
  require 'active_record'
8
+ ::Rails.cache = ::ActiveSupport::Cache::MemoryStore.new
9
+ ::Trax::Model.cache = ::Rails.cache
8
10
 
9
11
  SimpleCov.start do
10
12
  add_filter '/spec/'
@@ -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
- :uuid_column => "uuid",
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
@@ -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, :extend => ::Ecommerce::Product::Fields::CustomFields do
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
@@ -3,6 +3,30 @@ require 'spec_helper'
3
3
  describe ::Trax::Model do
4
4
  subject { ::Product }
5
5
 
6
- its(:trax_registry_key) { should eq "product" }
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.99
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-02 00:00:00.000000000 Z
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