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 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