trax_model 0.0.92 → 0.0.93
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -1
- data/README.md +227 -86
- data/lib/trax.rb +0 -1
- data/lib/trax/model.rb +23 -29
- data/lib/trax/model/attributes.rb +3 -1
- data/lib/trax/model/attributes/attribute.rb +11 -0
- data/lib/trax/model/attributes/definitions.rb +16 -0
- data/lib/trax/model/attributes/errors.rb +8 -0
- data/lib/trax/model/attributes/fields.rb +74 -0
- data/lib/trax/model/attributes/mixin.rb +48 -19
- data/lib/trax/model/attributes/type.rb +4 -0
- data/lib/trax/model/attributes/types/array.rb +8 -25
- data/lib/trax/model/attributes/types/boolean.rb +51 -0
- data/lib/trax/model/attributes/types/enum.rb +53 -12
- data/lib/trax/model/attributes/types/json.rb +36 -33
- data/lib/trax/model/attributes/types/string.rb +50 -0
- data/lib/trax/model/attributes/types/uuid_array.rb +17 -28
- data/lib/trax/model/attributes/value.rb +16 -0
- data/lib/trax/model/errors.rb +7 -0
- data/lib/trax/model/mixins.rb +11 -0
- data/lib/trax/model/mixins/field_scopes.rb +60 -0
- data/lib/trax/model/mixins/id_scopes.rb +36 -0
- data/lib/trax/model/mixins/sort_by_scopes.rb +25 -0
- data/lib/trax/model/railtie.rb +1 -0
- data/lib/trax/model/scopes.rb +16 -0
- data/lib/trax/model/struct.rb +168 -14
- data/lib/trax/model/unique_id.rb +14 -21
- data/lib/trax/model/uuid.rb +1 -1
- data/lib/trax/validators/enum_attribute_validator.rb +9 -0
- data/lib/trax/validators/future_validator.rb +1 -1
- data/lib/trax/validators/json_attribute_validator.rb +3 -3
- data/lib/trax/validators/string_attribute_validator.rb +17 -0
- data/lib/trax_model/version.rb +1 -1
- data/spec/db/database.yml +16 -0
- data/spec/db/schema/default_tables.rb +68 -0
- data/spec/db/schema/pg_tables.rb +27 -0
- data/spec/spec_helper.rb +20 -3
- data/spec/support/models.rb +123 -0
- data/spec/support/pg/models.rb +103 -0
- data/spec/trax/model/attributes/fields_spec.rb +88 -0
- data/spec/trax/model/attributes/types/enum_spec.rb +51 -0
- data/spec/trax/model/attributes/types/json_spec.rb +107 -0
- data/spec/trax/model/attributes_spec.rb +13 -0
- data/spec/trax/model/errors_spec.rb +1 -2
- data/spec/trax/model/mixins/field_scopes_spec.rb +7 -0
- data/spec/trax/model/struct_spec.rb +1 -1
- data/spec/trax/model/unique_id_spec.rb +1 -3
- data/spec/trax/validators/url_validator_spec.rb +1 -1
- data/trax_model.gemspec +4 -4
- metadata +57 -19
- data/lib/trax/model/config.rb +0 -16
- data/lib/trax/model/validators.rb +0 -15
- data/lib/trax/validators/enum_validator.rb +0 -16
- data/spec/support/schema.rb +0 -151
- data/spec/trax/model/config_spec.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f90a589c224b12e3d814f1d0000f97ae92abf3a0
|
4
|
+
data.tar.gz: 3805c04cfb881d04c38bbeddc52ff028250cd7b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df15b25f69f3dd4ab7a5bf1ae9b67ff16a9868a64812e6ccfad1190758a0af053aa77ce5e0edf9c226fb5246315b675242202714ca08138a38a27863e2808972
|
7
|
+
data.tar.gz: 483dace95c58f209f3474258263b4b18d5f6f4cf8ad11c00ca4290a061f0217856aeab45c992ca819447136bd8aa558e302229bc3aa127b755efb8c14a79e575
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,200 @@
|
|
1
1
|
# Trax Model
|
2
|
+
A higher level, even more opinionated active record model. Some of the features are postgres specific, but library itself should work with anything. Just include ::Trax::Model module and you're off to the races. The library currently contains two major components, a declarative, explicit attribute definitions dsl, and mixins. It also has additional STI support, but don't use the MTI stuff that's getting ripped out.
|
2
3
|
|
3
|
-
|
4
|
+
## Attributes
|
4
5
|
|
5
|
-
|
6
|
+
An declarative, more explicit attributes dsl for your models. Biggest feature at the moment is
|
7
|
+
support for struct (json), fields, as well as enum (integer) fields.
|
8
|
+
|
9
|
+
``` ruby
|
10
|
+
class Post
|
11
|
+
define_attributes do
|
12
|
+
enum :category, :default => :tutorials do
|
13
|
+
define :tutorials, 1
|
14
|
+
define :rants, 2
|
15
|
+
define :news, 3
|
16
|
+
end
|
17
|
+
|
18
|
+
struct :custom_fields do
|
19
|
+
boolean :is_published
|
20
|
+
|
21
|
+
enum :subtype do
|
22
|
+
define :video, 1
|
23
|
+
define :text, 2
|
24
|
+
define :audio, 3
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
### Struct Field (json/jsonb) ###
|
32
|
+
|
33
|
+
Finally, JSON fields that are usable. Usable as in, if you wanted to use a json field
|
34
|
+
for anything before, you probably soon after trying to use it, ran into at least one of the following problems:
|
35
|
+
|
36
|
+
1. Cant validate it's structure. You almost always want to define the structure of the thing you are allowing into your database. Otherwise its useless
|
37
|
+
2. Cant validate the components within it's structure. (even more difficult)
|
38
|
+
3. Setting from user input/how the database casts it is messy to implement and prone to error
|
39
|
+
|
40
|
+
So you realize, hey what a waste of time, Ill just create a new model because thats by far a better solution than doing all the above. However,
|
41
|
+
there are alot of cases where this will end up making your application messier via unnecessary relations.
|
42
|
+
|
43
|
+
**The solution**
|
44
|
+
``` ruby
|
45
|
+
struct :custom_fields do
|
46
|
+
string :title
|
47
|
+
boolean :is_published
|
48
|
+
|
49
|
+
enum :subtype, :define_scopes => true do
|
50
|
+
define :video, 1
|
51
|
+
define :text, 2
|
52
|
+
define :audio, 3
|
53
|
+
end
|
54
|
+
|
55
|
+
validates :title, :presence => true
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
Getting/setting values works via hash, or via method calls, both work the same.
|
60
|
+
|
61
|
+
``` ruby
|
62
|
+
#access should be indifferent so you can handle user input
|
63
|
+
::Post.new(:custom_fields => { :subtype => :video, :is_published => false })
|
64
|
+
|
65
|
+
#or
|
66
|
+
post = ::Post.first
|
67
|
+
post.custom_fields.subtype = :audio
|
68
|
+
post.save
|
69
|
+
```
|
70
|
+
|
71
|
+
Since struct is an actual value object, it has its own validation state. So you could call:
|
72
|
+
``` ruby
|
73
|
+
post.custom_fields.valid?
|
74
|
+
post.custom_field.errors
|
75
|
+
```
|
76
|
+
|
77
|
+
However, validation errors get pushed up to the root object as well, to make it easy to deal with.
|
78
|
+
|
79
|
+
``` ruby
|
80
|
+
Post.by_custom_fields_subtype(:video, :audio)
|
81
|
+
```
|
82
|
+
|
83
|
+
Yes thats right, you can search by the nested enum field via a search scope. It's a pretty dumb search scope (only supports enums ATM, no greater than or less than or anything that requires casting at the moment, and I really encourage structured i.e. enums to use when using struct to search).
|
84
|
+
|
85
|
+
**Warning** Use sparingly if you are doing heavy/many searches in json fields. I have no idea what performance impact to expect from lack of actual benchmarking atm and not a ton of information on pg json field search benchmarks in general, but common sense would say that if you are doing alot of searching on a ton of different values within the json data, particularly if the structures are huge, its probably going to be an expensive query.
|
86
|
+
|
87
|
+
Basically what Im saying is, if you allow a single json field to have say a 30mb json object in your db, filled with any number of possible keys and values, whenever you search that table (indexing aside), you're going to have a problem since postgres needs to look through all the col/rows in that table, + that giant field to look for matches to your query. We can reason without much knowledge of PG internals, that this is probably going to be slow.
|
88
|
+
|
89
|
+
Remember, just because you can do something, doesn't mean you should.
|
90
|
+
|
91
|
+
**With that said, giving your json fields structure, will give you better control over what you allow in the field, thereby making the search more usable. You can ensure that only the keys specified are allowed on that json field (much like a database table), and in the case of enums/boolean even limit the possible values of those keys, while providing meaning since it acts like a normal enum field.**
|
92
|
+
|
93
|
+
**Requirements to use struct field**
|
94
|
+
Fairly postgres specific, and intended to be used with the json field type. It may work with other implementations, but
|
95
|
+
this library is built to be opinionated and not handle every circumstance. -- Also use a jsonb field (pg 9.4 +)
|
96
|
+
if you want the search scope magic.
|
97
|
+
|
98
|
+
##Enum Field (integer) ##
|
99
|
+
|
100
|
+
You may be thiking, whats wrong with rails's built in enum? Answer: Everything. Ill detail somewhere else later, for now,
|
101
|
+
just know that the enum field type wont pollute your model with a million methods like rails enum.
|
102
|
+
It also supports setting the enum value by the name of the key, or by its integer value.
|
103
|
+
|
104
|
+
Syntax:
|
105
|
+
|
106
|
+
``` ruby
|
107
|
+
define_attributes do
|
108
|
+
enum :category, :default => :tutorials do
|
109
|
+
define :tutorials, 1
|
110
|
+
define :rants, 2
|
111
|
+
define :news, 3
|
112
|
+
end
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
Only one scope method will be defined (unlike rails which defines a scope for every value
|
117
|
+
within your enum, as well as a thousand instance methods. And if you use the same value
|
118
|
+
in a different enum field on the same model, you're not going to have a good time.
|
119
|
+
|
120
|
+
Assuming a subtype enum as above, you will have the following method which accepts
|
121
|
+
multiple enum args as input.
|
122
|
+
|
123
|
+
``` ruby
|
124
|
+
Post.by_subtype(:video, :text)
|
125
|
+
=> Post.where(:subtype => [1, 2])
|
126
|
+
```
|
127
|
+
|
128
|
+
## Mixins
|
129
|
+
|
130
|
+
Mixins are one of the core features of Trax model. A mixin is like a concern,
|
131
|
+
(in fact, mixins extend concern, so they have that behavior as well), but
|
132
|
+
with a more rigid pattern, with configurability built in. You can pass in options
|
133
|
+
to your mixin, which will allow you to use those options to define methods and what not
|
134
|
+
based on the options passed to the mixed_in method. Example:
|
135
|
+
|
136
|
+
``` ruby
|
137
|
+
module Slugify
|
138
|
+
extend ::Trax::Model::Mixin
|
139
|
+
|
140
|
+
mixed_in do |*field_names|
|
141
|
+
field_names.each do |field_name|
|
142
|
+
define_slug_method_for_field(field_name)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def some_instance_method
|
147
|
+
puts "Because I extend ActiveSupport::Concern"
|
148
|
+
puts "I am included into post instance methods"
|
149
|
+
end
|
150
|
+
|
151
|
+
module ClassMethods
|
152
|
+
def find_by_slug(field, *args)
|
153
|
+
where(:field => args.map(&:paramaterize))
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
def define_slug_method_for_field(field_name)
|
158
|
+
define_method("#{field_name}=") do |value|
|
159
|
+
super(value.paramaterize)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
You would call the mixin via:
|
167
|
+
|
168
|
+
``` ruby
|
169
|
+
class Post
|
170
|
+
mixins :slugify => [ :title, :category ]
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
or
|
175
|
+
``` ruby
|
176
|
+
class Post
|
177
|
+
mixin :slugify, :title, :category
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
``` ruby
|
182
|
+
Post.find_by_slug(:title, "Some Title")
|
183
|
+
Post.find_by_slug(:category, "Some ")
|
184
|
+
```
|
185
|
+
|
186
|
+
The mixins dsl should look familiar to you since it acts much like "validates". However,
|
187
|
+
unlike validators, there is one registry with one list of keys. So the first paramater of
|
188
|
+
the mixin call dictate what mixin gets invoked, and if you overwrite a mixin with same name,
|
189
|
+
it will call the last one defined.
|
190
|
+
|
191
|
+
# Packaged Trax Model Mixins
|
192
|
+
|
193
|
+
### UniqueId
|
194
|
+
|
195
|
+
``` ruby
|
196
|
+
mixins :unique_id => { :uuid_prefix => "0a" }
|
197
|
+
```
|
6
198
|
|
7
199
|
Supports uuid prefixes, and recommends next uuid prefix based on all uuid prefixes defined
|
8
200
|
in system -- Makes your uuids more discoverable and allows you to identify the model
|
@@ -35,12 +227,15 @@ the first 2 generated characters of the uuid function with a fixed character str
|
|
35
227
|
may affect the stats slightly, however Im not even sure if thats in a negative manner,
|
36
228
|
based on the fact that it splits the likeleyhood of a collision per record type
|
37
229
|
|
38
|
-
|
230
|
+
Usage
|
39
231
|
|
40
232
|
``` ruby
|
41
233
|
class Product < ActiveRecord::Base
|
42
234
|
include ::Trax::Model
|
43
|
-
|
235
|
+
|
236
|
+
mixins :unique_id => {
|
237
|
+
:uuid_prefix => "0a"
|
238
|
+
}
|
44
239
|
end
|
45
240
|
```
|
46
241
|
|
@@ -50,14 +245,6 @@ Product.new
|
|
50
245
|
=> #<Product id: nil, name: nil, category_id: nil, user_id: nil, price: nil, in_stock_quantity: nil, on_order_quantity: nil, active: nil, uuid: "0a97ad3e-1673-41f3-b356-d62dd53629d8", created_at: nil, updated_at: nil>
|
51
246
|
```
|
52
247
|
|
53
|
-
### Get next available prefix, useful when setting models up
|
54
|
-
|
55
|
-
``` ruby
|
56
|
-
bx rails c
|
57
|
-
::Trax::Model::Registry.next_prefix
|
58
|
-
=> "1a"
|
59
|
-
```
|
60
|
-
|
61
248
|
### Or, register prefixes using dsl rather than in each individual class
|
62
249
|
|
63
250
|
``` ruby
|
@@ -68,8 +255,6 @@ Trax::Model::UUID.register do
|
|
68
255
|
end
|
69
256
|
```
|
70
257
|
|
71
|
-
But wait theires more!
|
72
|
-
|
73
258
|
### UUID utility methods
|
74
259
|
|
75
260
|
``` ruby
|
@@ -80,88 +265,44 @@ product_uuid.record_type
|
|
80
265
|
=> Product
|
81
266
|
product_uuid.record
|
82
267
|
```
|
268
|
+
Will return the product instance, Which opens up quite a few possibilites via the newfound discoverability of your uuids...
|
83
269
|
|
84
|
-
|
85
|
-
|
86
|
-
Which opens up quite a few possibilites via the newfound discoverability of your uuids...
|
87
|
-
|
88
|
-
# MTI (Multiple Table Inheritance)
|
89
|
-
|
90
|
-
### Note: you must use Trax UUIDS w/ prefixes to use this feature (as we map each entity to its specific table, via the prefixed uuid.
|
91
|
-
|
92
|
-
Going to be a very brief documentation but:
|
93
|
-
|
94
|
-
### Set up MTI structure like this:
|
95
|
-
```
|
96
|
-
models/post.rb (your entity model)
|
97
|
-
models/post_types/abstract.rb (abstract, inherit from this)
|
98
|
-
models/post_types/entity.rb
|
99
|
-
models/post_types/video.rb
|
100
|
-
models/post_types/text.rb
|
101
|
-
models/post_types/audio.rb
|
102
|
-
```
|
103
|
-
|
104
|
-
Post is your entity class, entity is essentially a flat table which contains a list of
|
105
|
-
any common attributes, as well as the ids for each of your MTI data models. The beauty
|
106
|
-
of this, is that since Trax model uuids tell us what type the record is, we don't
|
107
|
-
need to use STI, or have a type column to determine the type of the record.
|
108
|
-
|
109
|
-
Basically, the entity model when loaded, will eager load the real model. If the real model is created, updated, or destroyed, a callback will ensure that the corresponding entity record is kept in sync.
|
110
|
-
|
270
|
+
## Field Scopes ##
|
111
271
|
``` ruby
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
#no database table to this class as its abstract.
|
121
|
-
module Blog
|
122
|
-
module Posts
|
123
|
-
class Abstract < ::ActiveRecord::Base
|
124
|
-
# following line sets abstract_class = true when including module
|
125
|
-
include ::Trax::Model::MTI::Abstract
|
126
|
-
|
127
|
-
entity_model :class_name => "Blog::Post"
|
128
|
-
|
129
|
-
### Define your base inherited logic / methods here ###
|
130
|
-
|
131
|
-
belongs_to :user_id
|
132
|
-
belongs_to :category_id
|
133
|
-
|
134
|
-
validates :user_id
|
135
|
-
validates :category_id
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
module Blog
|
141
|
-
module Posts
|
142
|
-
class Video < ::Blog::Posts::Abstract
|
143
|
-
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
272
|
+
mixins :field_scopes => {
|
273
|
+
:by_id => true,
|
274
|
+
:by_id_not => { :field => :id, :type => :not },
|
275
|
+
:by_name_matches => { :field => :name, :type => :matches }
|
276
|
+
}
|
147
277
|
```
|
148
278
|
|
149
|
-
|
279
|
+
Here's a quick protip to writing better rails code. Treat the where method as private at all times. Use scopes to define the fields that can be searched, and keep them composable and chainable. Most search scopes should simply equate to "where field contains any number of these values". It's (generalizing) roughly the same performance hit to search one field for 100 values as it is to search one field for one value provided that value is at the bottom of the table.
|
150
280
|
|
151
|
-
|
281
|
+
Based on those rules, 3 primary scope types right now.
|
152
282
|
|
153
|
-
1.
|
283
|
+
1. where
|
284
|
+
2. where_not
|
285
|
+
3. matching (contains | fuzzy)
|
154
286
|
|
155
|
-
|
287
|
+
I like having the by_ affix attached to search scopes in most cases, so if your field contains a by_ it will try and guess the field name based on the fact.
|
156
288
|
|
157
|
-
|
289
|
+
The preceeding example will do the folllowing:
|
158
290
|
|
159
|
-
|
160
|
-
|
161
|
-
|
291
|
+
``` ruby
|
292
|
+
scope :by_id, lambda{|*_values|
|
293
|
+
_values.flat_compact_uniq!
|
294
|
+
where(:id => _values)
|
295
|
+
}
|
296
|
+
scope :by_id_not, lambda{|*_values|
|
297
|
+
_values.flat_compact_uniq!
|
298
|
+
where.not(:id => _values)
|
299
|
+
}
|
300
|
+
scope :by_name_matches, lambda{|*_values|
|
301
|
+
_values.flat_compact_uniq!
|
302
|
+
matching(:name => _values)
|
303
|
+
}
|
304
|
+
```
|
162
305
|
|
163
|
-
1. No shared view between child types. I.E. thats what the MTI entity is for. (want to find all blog posts? You cant unless you select a type first, or are using this gem, or a postgres view or something else)
|
164
|
-
2. More difficult to setup since each child table needs its own table.
|
165
306
|
|
166
307
|
# STI
|
167
308
|
|
data/lib/trax.rb
CHANGED
data/lib/trax/model.rb
CHANGED
@@ -2,17 +2,22 @@ require 'active_record'
|
|
2
2
|
require 'default_value_for'
|
3
3
|
require 'hashie/dash'
|
4
4
|
require 'hashie/mash'
|
5
|
+
require 'hashie/trash'
|
6
|
+
require 'hashie/extensions/dash/indifferent_access'
|
5
7
|
require 'simple_enum'
|
6
8
|
require_relative './string'
|
7
9
|
require_relative './validators/boolean_validator'
|
8
10
|
require_relative './validators/email_validator'
|
9
|
-
require_relative './validators/enum_validator'
|
10
11
|
require_relative './validators/frozen_validator'
|
11
12
|
require_relative './validators/future_validator'
|
12
|
-
require_relative './validators/json_attribute_validator'
|
13
13
|
require_relative './validators/subdomain_validator'
|
14
14
|
require_relative './validators/url_validator'
|
15
15
|
|
16
|
+
#trax attribute specific validators
|
17
|
+
require_relative './validators/enum_attribute_validator'
|
18
|
+
require_relative './validators/json_attribute_validator'
|
19
|
+
require_relative './validators/string_attribute_validator'
|
20
|
+
|
16
21
|
module Trax
|
17
22
|
module Model
|
18
23
|
extend ::ActiveSupport::Concern
|
@@ -29,6 +34,7 @@ module Trax
|
|
29
34
|
autoload :UniqueId
|
30
35
|
autoload :Matchable
|
31
36
|
autoload :Mixin
|
37
|
+
autoload :Mixins
|
32
38
|
autoload :MTI
|
33
39
|
autoload :Restorable
|
34
40
|
autoload :Railtie
|
@@ -38,12 +44,20 @@ module Trax
|
|
38
44
|
|
39
45
|
include ::Trax::Model::Matchable
|
40
46
|
include ::ActiveModel::Dirty
|
47
|
+
include ::Trax::Core::InheritanceHooks
|
41
48
|
|
42
49
|
define_configuration_options! do
|
43
50
|
option :auto_include, :default => false
|
44
51
|
option :auto_include_mixins, :default => []
|
45
52
|
end
|
46
53
|
|
54
|
+
#like reverse merge, only assigns attributes which have not yet been assigned
|
55
|
+
def reverse_assign_attributes(attributes_hash)
|
56
|
+
attributes_to_assign = attributes_hash.keys.reject{|_attribute_name| attribute_present?(_attribute_name) }
|
57
|
+
|
58
|
+
assign_attributes(attributes_hash.slice(attributes_to_assign))
|
59
|
+
end
|
60
|
+
|
47
61
|
class << self
|
48
62
|
attr_accessor :mixin_registry
|
49
63
|
end
|
@@ -67,10 +81,17 @@ module Trax
|
|
67
81
|
::Trax::Model::Freezable
|
68
82
|
::Trax::Model::Restorable
|
69
83
|
::Trax::Model::UniqueId
|
84
|
+
::Trax::Model::Mixins::FieldScopes
|
85
|
+
::Trax::Model::Mixins::IdScopes
|
86
|
+
::Trax::Model::Mixins::SortByScopes
|
70
87
|
end
|
71
88
|
|
72
89
|
eager_autoload_mixins!
|
73
90
|
|
91
|
+
def self.find_by_uuid(uuid)
|
92
|
+
::Trax::Model::UUID.new(uuid).record
|
93
|
+
end
|
94
|
+
|
74
95
|
included do
|
75
96
|
class_attribute :registered_mixins
|
76
97
|
|
@@ -87,33 +108,6 @@ module Trax
|
|
87
108
|
instance_variable_set(:@_after_inherited_block, block)
|
88
109
|
end
|
89
110
|
|
90
|
-
#the tracepoint stuff is to ensure that we call the after_inherited block not
|
91
|
-
#right after the class is defined, but rather, after the class is defined and
|
92
|
-
#evaluated. i.e. this allows us to do stuff like set class attributes
|
93
|
-
# class_attribute :messages_class
|
94
|
-
#
|
95
|
-
# after_inherited do
|
96
|
-
# has_many :computed_subtype_messages, :class_name => message_class
|
97
|
-
# end
|
98
|
-
# - Then in subklass
|
99
|
-
# self.messages_class = "MySubtypeSpecifcModel"
|
100
|
-
|
101
|
-
def inherited(subklass)
|
102
|
-
super(subklass)
|
103
|
-
|
104
|
-
if self.instance_variable_defined?(:@_after_inherited_block)
|
105
|
-
trace = ::TracePoint.new(:end) do |tracepoint|
|
106
|
-
if tracepoint.self == subklass
|
107
|
-
trace.disable
|
108
|
-
|
109
|
-
subklass.instance_eval(&self.instance_variable_get(:@_after_inherited_block))
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
trace.enable
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
111
|
def mixin(key, options = {})
|
118
112
|
raise ::Trax::Model::Errors::MixinNotRegistered.new(
|
119
113
|
model: self.name,
|