trax_model 0.0.2 → 0.0.3

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: aadee7f297ba78ffcf101d66d949fd921528e6aa
4
- data.tar.gz: 31fc37ab41fbef66ac534734b9c34d223cdc71b7
3
+ metadata.gz: 72afe4ed91e48868ae5965523a0ece32221a2a42
4
+ data.tar.gz: 29e28997f27539058bf5a7e131d13cef8696608e
5
5
  SHA512:
6
- metadata.gz: 135a818db486a91cace4e5787d3de8d7c1685247df963f861af5fc6aabbe0054e0def7f60f66d6bbca2c2cea6f2b690320d5da98fecdf292bfff72a11e913e4c
7
- data.tar.gz: d9df19307c0741ac126a5c9a574715f393e4a1947096a1cb17114466d649720ea5f208e8eba65a6dcfdeb815ba37d7474a2cb959df3343a09ec3406ff4bfc4dd
6
+ metadata.gz: f4e3d8332d31fbabdca1318bf3a504e775e9c1b080df5cd7de76150bdcf24b508786c5c5d2767da8628f933b32b9cb980fe54a9dcb659701a653c0ee3b57b919
7
+ data.tar.gz: 06619d0356c3b86ffd769c4410be2e59e40838ca6d0af1a60e8c88f40d113e0775dd95c3a6caf4499c29b8fa800bbc94efd0684e5434bdcc5e5e4e0a81f091a1
data/README.md CHANGED
@@ -1,6 +1,158 @@
1
- # TraxModel
1
+ # Trax Model
2
+
3
+ A composeable companion to active record models. Just include ::Trax::Model and you're off to the races.
4
+
5
+ ### UUIDS
6
+
7
+ Supports uuid prefixes, and recommends next uuid prefix based on all uuid prefixes defined
8
+ in system -- Makes your uuids more discoverable and allows you to identify the model
9
+ itself just by the uuid, or do even cooler stuff programatically
10
+
11
+ ### ITS IMPERATIVE THAT YOU DO NOT CHANGE YOUR UUID PREFIXES AFTER CREATING RECORDS
12
+
13
+ Sorry for yelling, but the point is, that will throw all the mapping stuff out of whack.
14
+ Don't do it. Treat it as a single point of truth for identifying the model in your system.
15
+
16
+ ### UUID Prefixes should be treated like an enum, values are ordered like
17
+
18
+ [0a, 0b, 0c...], from 0-9a-f, then back down in reverse, i.e.
19
+
20
+ [a0, a1, a2], the alpha first prefixes are a higher sort order in the list
21
+
22
+ ### Why?
23
+
24
+ This library is compatible with postgres's uuid, hexidecimal type. This enables
25
+ you to use the library to generate uuids on the fly, in your application code,
26
+ WITHOUT relying on primary key integer increment, which in case you haven't yet realized,
27
+ is the wild west once you are processing enough writes that you start seeing duplicate
28
+ primary key errors.
29
+
30
+ ### Can't there still be conflicts?
31
+
32
+ Yes but the odds are astronomically small. From what I understand, probably you are more
33
+ likely to get struck by lightning than see that error. With that said, I am overwriting
34
+ the first 2 generated characters of the uuid function with a fixed character string, which
35
+ may affect the stats slightly, however Im not even sure if thats in a negative manner,
36
+ based on the fact that it splits the likeleyhood of a collision per record type
37
+
38
+ I.E.
39
+
40
+ ``` ruby
41
+ class Product < ActiveRecord::Base
42
+ include ::Trax::Model
43
+ defaults :uuid_prefix => "0a"
44
+ end
45
+ ```
46
+
47
+ ``` ruby
48
+ Product.new
49
+
50
+ => #<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
+ ```
52
+
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
+ But wait theires more!
62
+
63
+ ### UUID utility methods
64
+
65
+ ``` ruby
66
+ product_uuid = Product.first.uuid
67
+ => "0a97ad3e-1673-41f3-b356-d62dd53629d8"
68
+
69
+ product_uuid.record_type
70
+ => Product
71
+ product_uuid.record
72
+ ```
73
+
74
+ will return the product instance
75
+
76
+ Which opens up quite a few possibilites via the newfound discoverability of your uuids...
77
+
78
+ # MTI (Multiple Table Inheritance)
79
+
80
+ ### 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.
81
+
82
+ Going to be a very brief documentation but:
83
+
84
+ ### Set up MTI structure like this:
85
+ ```
86
+ models/post.rb (your entity model)
87
+ models/post_types/abstract.rb (abstract, inherit from this)
88
+ models/post_types/entity.rb
89
+ models/post_types/video.rb
90
+ models/post_types/text.rb
91
+ models/post_types/audio.rb
92
+ ```
93
+
94
+ Post is your entity class, entity is essentially a flat table which contains a list of
95
+ any common attributes, as well as the ids for each of your MTI data models. The beauty
96
+ of this, is that since Trax model uuids tell us what type the record is, we don't
97
+ need to use STI, or have a type column to determine the type of the record.
98
+
99
+ 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.
100
+
101
+ ``` ruby
102
+ module Blog
103
+ class Post < ActiveRecord::Base
104
+ include ::Trax::Model::MTI::Entity
105
+
106
+ mti_namespace ::Blog::Posts
107
+ end
108
+ end
109
+
110
+ #no database table to this class as its abstract.
111
+ module Blog
112
+ module Posts
113
+ class Abstract < ::ActiveRecord::Base
114
+ # following line sets abstract_class = true when including module
115
+ include ::Trax::Model::MTI::Abstract
116
+
117
+ entity_model :class_name => "Blog::Post"
118
+
119
+ ### Define your base inherited logic / methods here ###
120
+
121
+ belongs_to :user_id
122
+ belongs_to :category_id
123
+
124
+ validates :user_id
125
+ validates :category_id
126
+ end
127
+ end
128
+ end
129
+
130
+ module Blog
131
+ module Posts
132
+ class Video < ::Blog::Posts::Abstract
133
+
134
+ end
135
+ end
136
+ end
137
+ ```
138
+
139
+ ### MTI VS STI
140
+
141
+ The main advantages of Multiple Table Inheritance versus Single Table inheritance I see are:
142
+
143
+ 1) Table size. As databases grow, vertically adding more length to traverse the table will continue to get slower. One way to mitigate this issue would be to split up your table into multiple horizontal tables, if it makes sense for your data structure to do so (i.e. like above)
144
+
145
+ 2) STI will get out of proportion likely. I.e. if 90% of your posts are text posts, then when you are looking for a video post, you are to some degree being slowed down by the video posts in your table. (or at least at some point when you reach past xxxxxx number of records)
146
+
147
+ 3) Further on that note, only storing what you need for each individual subset of your data, on that particular subset. I.e. if video post has a video_url attribute, but none of the other post types have that, it will keep holes out of your data tables since video_url is only on the video_posts table.
148
+
149
+ 4) Real separation at the data level of non common attributes, so you dont have to write safeguards in child classes to make sure that a value didnt slip into a field or whatever, because each child class has its own individual interpretation of the schema.
150
+
151
+ The main disadvantages are:
152
+
153
+ 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)
154
+ 2) More difficult to setup since each child table needs its own table.
2
155
 
3
- TODO: Write a gem description
4
156
 
5
157
  ## Installation
6
158
 
@@ -1,4 +1,6 @@
1
+ require 'trax/core'
1
2
  require 'trax/model'
3
+
2
4
  module Trax
3
5
 
4
6
  end
@@ -21,6 +21,7 @@ module Trax
21
21
  autoload :UUIDPrefix
22
22
  autoload :UniqueId
23
23
  autoload :Matchable
24
+ autoload :MTI
24
25
  autoload :Validators
25
26
 
26
27
  include ::Trax::Model::Matchable
@@ -34,7 +35,6 @@ module Trax
34
35
  register_trax_models(self)
35
36
  end
36
37
 
37
-
38
38
  module ClassMethods
39
39
  delegate :register_trax_model, :to => "::Trax::Model::Registry"
40
40
  delegate :[], :to => :find
@@ -0,0 +1,11 @@
1
+ module Trax
2
+ module Model
3
+ module MTI
4
+ extend ::ActiveSupport::Autoload
5
+
6
+ autoload :Abstract
7
+ autoload :Entity
8
+ autoload :Namespace
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,62 @@
1
+ module Trax
2
+ module Model
3
+ module MTI
4
+ module Abstract
5
+ extend ::ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :mti_config
9
+
10
+ self.abstract_class = true
11
+ self.mti_config = ::Hashie::Mash.new(:foreign_key => :id)
12
+
13
+ scope :records, lambda{
14
+ map(&:entity)
15
+ }
16
+ end
17
+
18
+ module ClassMethods
19
+ def inherited(subklass)
20
+ super(subklass)
21
+
22
+ subklass.after_create do |record|
23
+ entity_model = mti_config[:class_name].constantize.new
24
+
25
+ record.attributes.each_pair do |k,v|
26
+ entity_model.__send__("#{k}=", v) if entity_model.respond_to?(k)
27
+ end
28
+
29
+ entity_model.save
30
+ end
31
+
32
+ subklass.after_update do |record|
33
+ entity_model = record.entity
34
+
35
+ if record.changed.any?
36
+ record.changes.each_pair do |k,v|
37
+ entity_model.__send__("#{k}=", v[1]) if entity.respond_to?(:"#{k}")
38
+ end
39
+ end
40
+
41
+ entity_model.save if entity_model.changed.any?
42
+ end
43
+
44
+ subklass.after_destroy do |record|
45
+ entity_model = record.entity
46
+
47
+ entity_model.destroy
48
+ end
49
+ end
50
+
51
+ def entity_model(options)
52
+ valid_options = options.assert_valid_keys(:class_name, :foreign_key)
53
+ mti_config.merge!(valid_options)
54
+
55
+ self.has_one(:entity, mti_config.symbolize_keys)
56
+ self.accepts_nested_attributes_for(:entity)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,63 @@
1
+ module Trax
2
+ module Model
3
+ module MTI
4
+ module Entity
5
+ extend ::ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :_mti_namespace
9
+
10
+ scope :records, lambda{
11
+ map(&:entity)
12
+ }
13
+ end
14
+
15
+ def model_type
16
+ @model_type ||= uuid.record_type
17
+ end
18
+
19
+ def model_type_key
20
+ :"#{model_type.name.demodulize.underscore}_entity"
21
+ end
22
+
23
+ def model
24
+ @model ||= __send__(model_type_key)
25
+ end
26
+
27
+ def uuid
28
+ @uuid ||= self[:id].uuid
29
+ end
30
+
31
+ module ClassMethods
32
+ def all
33
+ relation = super
34
+ entity_ids = relation.pluck(:id)
35
+ entity_types = entity_ids.map { |id| ::Trax::Model::UUID.new(id[0..1]) }
36
+ .map(&:record_type)
37
+ .flatten
38
+ .compact
39
+ .uniq
40
+
41
+ relation_names = entity_types.map{ |type| :"#{type.name.demodulize.underscore}_entity" }
42
+
43
+ relation.includes(relation_names).references(relation_names)
44
+ end
45
+
46
+ def mti_namespace(namespace)
47
+ _mti_namespace = (namespace.is_a?(String)) ? namespace.constantize : namespace
48
+
49
+ _mti_namespace.all.reject{|model| model.abstract_class }.each do |subclass|
50
+ key = :"#{subclass.name.demodulize.underscore}_entity"
51
+ has_one key, :class_name => subclass.name, :foreign_key => :id
52
+ end
53
+ end
54
+
55
+ def multiple_table_inheritance_namespace(namespace)
56
+ mti_namespace(namespace)
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ module Trax
2
+ module Model
3
+ module MTI
4
+ module Namespace
5
+ extend ::Trax::Core::EagerAutoloadNamespace
6
+
7
+ class << self
8
+ attr_reader :base_mti_model
9
+ end
10
+
11
+ def self.base_model(model)
12
+ @base_mti_model = model
13
+ end
14
+
15
+ def self.all
16
+ @all ||= base_mti_model.subclasses
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -52,7 +52,7 @@ module Trax
52
52
  end
53
53
 
54
54
  def self.uuid_map
55
- @uuid_map ||= models.values.reject!{|model| !model.try(:uuid_prefix) }.inject(::Hashie::Mash.new) do |result, model|
55
+ models.values.reject{|model| model.try(:uuid_prefix) == nil }.inject(::Hashie::Mash.new) do |result, model|
56
56
  result[model.uuid_prefix] = model
57
57
  result
58
58
  end
@@ -1,4 +1,4 @@
1
- require 'trax'
1
+ require_relative './trax'
2
2
 
3
3
  module TraxModel
4
4
  end
@@ -1,3 +1,3 @@
1
1
  module TraxModel
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "trax_core"
21
+ spec.add_dependency "trax_core", "~> 0.0.3"
22
22
  spec.add_dependency "default_value_for", "~> 3.0.0"
23
23
  spec.add_development_dependency "activerecord"
24
24
  spec.add_development_dependency "bundler", "~> 1.6"
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trax_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Ayre
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-04 00:00:00.000000000 Z
11
+ date: 2014-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trax_core
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.0.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.0.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: default_value_for
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -268,6 +268,10 @@ files:
268
268
  - lib/trax/model/config.rb
269
269
  - lib/trax/model/freezable.rb
270
270
  - lib/trax/model/matchable.rb
271
+ - lib/trax/model/mti.rb
272
+ - lib/trax/model/mti/abstract.rb
273
+ - lib/trax/model/mti/entity.rb
274
+ - lib/trax/model/mti/namespace.rb
271
275
  - lib/trax/model/registry.rb
272
276
  - lib/trax/model/unique_id.rb
273
277
  - lib/trax/model/uuid.rb
@@ -317,7 +321,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
317
321
  version: '0'
318
322
  requirements: []
319
323
  rubyforge_project:
320
- rubygems_version: 2.2.2
324
+ rubygems_version: 2.4.3
321
325
  signing_key:
322
326
  specification_version: 4
323
327
  summary: Higher level ActiveRecord models for rails