discriminable 2.2.1 → 2.2.4
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 +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +59 -24
- data/TODO.md +8 -2
- data/lib/discriminable/version.rb +1 -1
- data/lib/discriminable.rb +40 -14
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91332d7c33b8d092f2071dd5d4a59b12786058d8347ba844a8426354133f4e2c
|
4
|
+
data.tar.gz: b743b5f2db1ea1690a5bad69b6b046a58dd14e76c64e47de03dd18a8b57b8003
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0a230f1257e8a53e914c8ac39b879143a897ca0d909441403494afa2e0ba2a48ea529b58efb85b8946c97416dc407ac7bf5cda60a12482cec0aceff22cc6169
|
7
|
+
data.tar.gz: abcc79722fafc181c21f1e27220c4ce46de790e90499de8dd97c94f568b1143d094d6498c93d5975836ec3781fba5e38d8ba51e8b8f002ba1e8dfb4d1d20bb25
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
### [2.2.4](https://github.com/gregorw/discriminable/compare/v2.2.3...v2.2.4) (2022-04-25)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* changelog url ([3054a1a](https://github.com/gregorw/discriminable/commit/3054a1a0137d8d3cccb55d7f2f06cb392053dab0))
|
9
|
+
* support querying when multiple values are provided ([f75190b](https://github.com/gregorw/discriminable/commit/f75190bcf33dcbddf507626bc77aa4709a594f4d))
|
10
|
+
|
11
|
+
### [2.2.3](https://github.com/gregorw/discriminable/compare/v2.2.2...v2.2.3) (2022-04-20)
|
12
|
+
|
13
|
+
|
14
|
+
### Bug Fixes
|
15
|
+
|
16
|
+
* Allow to use attribute aliases ([448e4e8](https://github.com/gregorw/discriminable/commit/448e4e81925543f1ee4949898f0ac6628eb2d3fd))
|
17
|
+
|
18
|
+
### [2.2.2](https://github.com/gregorw/discriminable/compare/v2.2.1...v2.2.2) (2022-04-17)
|
19
|
+
|
20
|
+
|
21
|
+
### Bug Fixes
|
22
|
+
|
23
|
+
* Typo in gemspec uris ([f77253f](https://github.com/gregorw/discriminable/commit/f77253f2cb2f400e04baf3c7bd6083f42ae3aa6b))
|
24
|
+
|
3
25
|
### [2.2.1](https://github.com/gregorw/discriminable/compare/v2.2.0...v2.2.1) (2022-04-17)
|
4
26
|
|
5
27
|
|
data/README.md
CHANGED
@@ -3,28 +3,21 @@
|
|
3
3
|
# Discriminable
|
4
4
|
|
5
5
|
[](https://rubygems.org/gems/discriminable)
|
6
|
-
[](https://github.com/gregorw/discriminable/actions/workflows/main.yml)
|
6
|
+
[](https://github.com/gregorw/discriminable/actions/workflows/main.yml)
|
7
7
|
[](https://codeclimate.com/github/gregorw/discriminable/maintainability)
|
8
8
|
[](https://codeclimate.com/github/gregorw/discriminable/test_coverage)
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
In other words, use any _existing_ model attribute for STI instead of storing class names in a `type` column.
|
13
|
-
|
14
|
-
**Related work**
|
15
|
-
|
16
|
-
The idea was originally described in [“Bye Bye STI, Hello Discriminable Model”](https://www.salsify.com/blog/engineering/bye-bye-sti-hello-discriminable-model) by Randy Burkes and this Gem has started out with [his code](https://gist.github.com/rlburkes/798e186acb2f93e787a5).
|
10
|
+
This is a Ruby gem that implements single-table inheritance (STI) for ActiveRecord models using string, integer and boolean column types.
|
17
11
|
|
12
|
+
In other words, use any (existing) model attribute to discriminate between different classes in your class hierarchy. This makes storing class names in a `type` column redundant.
|
18
13
|
|
19
14
|
## Installation
|
20
15
|
|
21
|
-
|
22
|
-
|
23
|
-
$ bundle add discriminable
|
16
|
+
bundle add discriminable
|
24
17
|
|
25
|
-
|
18
|
+
or
|
26
19
|
|
27
|
-
|
20
|
+
gem install discriminable
|
28
21
|
|
29
22
|
## Usage
|
30
23
|
|
@@ -32,28 +25,29 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
32
25
|
class Order < ActiveRecord::Base
|
33
26
|
include Discriminable
|
34
27
|
|
35
|
-
|
36
|
-
discriminable state: { open: "Cart" }
|
28
|
+
discriminable_by :state
|
37
29
|
end
|
38
30
|
|
39
31
|
class Cart < Order
|
32
|
+
discriminable_as :open
|
40
33
|
end
|
41
34
|
|
42
35
|
Cart.create
|
43
|
-
=> #<Cart id: 1, state: "open">
|
36
|
+
# => #<Cart id: 1, state: "open">
|
44
37
|
Order.all
|
45
|
-
=> #<ActiveRecord::Relation [#<Cart id: 1, state: "open">]>
|
38
|
+
# => #<ActiveRecord::Relation [#<Cart id: 1, state: "open">]>
|
46
39
|
```
|
47
40
|
|
48
|
-
|
41
|
+
## Features
|
49
42
|
|
50
|
-
|
43
|
+
### Compatible with enums
|
51
44
|
|
52
45
|
```ruby
|
53
46
|
class Order < ActiveRecord::Base
|
54
47
|
include Discriminable
|
55
48
|
|
56
|
-
enum state: { open: 0,
|
49
|
+
enum state: { open: 0, processing: 1, invoiced: 2 }
|
50
|
+
|
57
51
|
discriminable_by :state
|
58
52
|
end
|
59
53
|
|
@@ -61,12 +55,42 @@ class Cart < Order
|
|
61
55
|
discriminable_as :open
|
62
56
|
end
|
63
57
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
=> #<ActiveRecord::Relation [#<Cart id: 1, state: "open">]>
|
58
|
+
class Invoice < Order
|
59
|
+
discriminable_as :invoiced
|
60
|
+
end
|
68
61
|
```
|
69
62
|
|
63
|
+
### Aliased attributes
|
64
|
+
|
65
|
+
In case you are working with a legacy database and cannot change the column name easily it’s easy to reference an aliased attribute in the `discriminable_by` definition.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class Property < ActiveRecord::Base
|
69
|
+
include Discriminable
|
70
|
+
|
71
|
+
alias_attribute :kind, :kind_with_legacy_postfix
|
72
|
+
|
73
|
+
# Aliased attributes are supported when specifying the discriminable attribute
|
74
|
+
discriminable_by :kind
|
75
|
+
end
|
76
|
+
|
77
|
+
class NumberProperty < Property
|
78
|
+
discriminable_as 1
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
### Multiple values
|
83
|
+
|
84
|
+
Sometimes, in a real project, you may want to map a number of values to a single class. This is possible by specifying:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
class OptionProperty < Property
|
88
|
+
# The first mention becomes the default value
|
89
|
+
discriminable_as 2, 3, 4
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
Note that when creating new records with e.g. `OptionProperty.create` a _default_ value needs to be set in the database for this discriminable class. The Discriminable gem uses the _first_ value in the list as the default.
|
70
94
|
|
71
95
|
## Contributing
|
72
96
|
|
@@ -79,3 +103,14 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
79
103
|
## Code of Conduct
|
80
104
|
|
81
105
|
Everyone interacting in the Discriminable project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/gregorw/discriminable/blob/main/CODE_OF_CONDUCT.md).
|
106
|
+
|
107
|
+
## Related work
|
108
|
+
|
109
|
+
The idea for this Gem was influenced by [“Bye Bye STI, Hello Discriminable Model”](https://www.salsify.com/blog/engineering/bye-bye-sti-hello-discriminable-model) by Randy Burkes. This Gem has started out with [his code](https://gist.github.com/rlburkes/798e186acb2f93e787a5).
|
110
|
+
|
111
|
+
See also:
|
112
|
+
|
113
|
+
- Rails [single table inheritance](https://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html) and [DelegatedType](https://api.rubyonrails.org/classes/ActiveRecord/DelegatedType.html)
|
114
|
+
- Java [JPA discrimanator](https://openjpa.apache.org/builds/1.0.2/apache-openjpa-1.0.2/docs/manual/jpa_overview_mapping_discrim.html)
|
115
|
+
- Python [model inheritance](https://docs.djangoproject.com/en/4.0/topics/db/models/#model-inheritance-1)
|
116
|
+
- [Discriminator](https://github.com/gdpelican/discriminator) gem.
|
data/TODO.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
- [x] multiple values per subclass
|
2
2
|
- [x] default to first value when using hash syntax
|
3
3
|
- [x] open-closed principle
|
4
|
-
- [
|
4
|
+
- [x] Bug: multiple values: Child.all query (double-check)
|
5
|
+
- [ ] Rails 5 support (see rails-5 branch)
|
6
|
+
- [ ] rubocop-minitest
|
7
|
+
- [ ] more tests / examples
|
8
|
+
- [ ] Documentation
|
5
9
|
- [ ] test permitted attributes
|
6
|
-
- [ ]
|
10
|
+
- [ ] scoping… should work OOTB
|
11
|
+
- [ ] ~~`self.abstract_class = true` ➔ This results in separate tables~~
|
12
|
+
- [ ] ~~use `type` column with enum, int, string, etc.~~
|
data/lib/discriminable.rb
CHANGED
@@ -36,28 +36,28 @@ module Discriminable
|
|
36
36
|
|
37
37
|
attribute, map = options.first
|
38
38
|
|
39
|
+
# E.g. { value: "ClassName" }
|
39
40
|
self.discriminable_map = map.with_indifferent_access
|
40
41
|
|
41
42
|
# Use first key as default discriminator
|
42
43
|
# { a: "C", b: "C" }.invert => { "C" => :b }
|
43
44
|
# { a: "C", b: "C" }.to_a.reverse.to_h.invert => { "C" => :a }
|
45
|
+
# E.g. { "ClassName" => :value }
|
44
46
|
self.discriminable_inverse_map = map.to_a.reverse.to_h.invert
|
45
|
-
|
47
|
+
|
48
|
+
attribute = attribute.to_s
|
49
|
+
self.inheritance_column = attribute_aliases[attribute] || attribute
|
46
50
|
end
|
47
51
|
|
48
|
-
# rubocop:disable Metrics/AbcSize
|
49
52
|
def discriminable_by(attribute)
|
50
53
|
raise "Subclasses should not override .discriminable_by" unless base_class?
|
51
54
|
|
52
|
-
self.
|
53
|
-
self.
|
54
|
-
|
55
|
-
|
56
|
-
self.
|
57
|
-
map[value] = value.constantize.discriminable_values&.first
|
58
|
-
end
|
55
|
+
self.discriminable_map ||= discriminable_map_memoized
|
56
|
+
self.discriminable_inverse_map ||= discriminable_inverse_map_memoized
|
57
|
+
|
58
|
+
attribute = attribute.to_s
|
59
|
+
self.inheritance_column = attribute_aliases[attribute] || attribute
|
59
60
|
end
|
60
|
-
# rubocop:enable Metrics/AbcSize
|
61
61
|
|
62
62
|
def discriminable_as(*values)
|
63
63
|
raise "Only subclasses should specify .discriminable_as" if base_class?
|
@@ -67,10 +67,22 @@ module Discriminable
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
+
# This is the value of the discriminable attribute
|
70
71
|
def sti_name
|
71
72
|
discriminable_inverse_map[name]
|
72
73
|
end
|
73
74
|
|
75
|
+
def sti_names
|
76
|
+
([self] + descendants).flat_map(&:discriminable_values)
|
77
|
+
end
|
78
|
+
|
79
|
+
def type_condition(table = arel_table)
|
80
|
+
return super unless discriminable_values.present?
|
81
|
+
|
82
|
+
sti_column = table[inheritance_column]
|
83
|
+
predicate_builder.build(sti_column, sti_names)
|
84
|
+
end
|
85
|
+
|
74
86
|
def sti_class_for(value)
|
75
87
|
return self unless (type_name = discriminable_map[value])
|
76
88
|
|
@@ -84,13 +96,27 @@ module Discriminable
|
|
84
96
|
attrs = attrs.to_h if attrs.respond_to?(:permitted?)
|
85
97
|
return unless attrs.is_a?(Hash)
|
86
98
|
|
87
|
-
value = attrs
|
88
|
-
value = base_class.type_for_attribute(inheritance_column).cast(value)
|
99
|
+
value = discriminable_value(attrs)
|
89
100
|
sti_class_for(value)
|
90
101
|
end
|
91
102
|
|
92
|
-
def
|
93
|
-
|
103
|
+
def discriminable_map_memoized
|
104
|
+
Hash.new do |map, value|
|
105
|
+
map[value] = descendants.detect { |d| d.discriminable_values.include? value }&.name
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def discriminable_inverse_map_memoized
|
110
|
+
Hash.new do |map, value|
|
111
|
+
map[value] = value.constantize.discriminable_values&.first
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def discriminable_value(attrs)
|
116
|
+
attrs = attrs.with_indifferent_access
|
117
|
+
value = attrs[inheritance_column]
|
118
|
+
value ||= attrs[attribute_aliases.invert[inheritance_column]]
|
119
|
+
base_class.type_for_attribute(inheritance_column).cast(value)
|
94
120
|
end
|
95
121
|
end
|
96
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: discriminable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregor Wassmann
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -131,13 +131,13 @@ files:
|
|
131
131
|
- lib/discriminable.rb
|
132
132
|
- lib/discriminable/version.rb
|
133
133
|
- sig/discriminable.rbs
|
134
|
-
homepage: https://github.com/gregorw/
|
134
|
+
homepage: https://github.com/gregorw/discriminable
|
135
135
|
licenses:
|
136
136
|
- MIT
|
137
137
|
metadata:
|
138
|
-
homepage_uri: https://github.com/gregorw/
|
139
|
-
source_code_uri: https://github.com/gregorw/
|
140
|
-
changelog_uri: https://github.com/gregorw/
|
138
|
+
homepage_uri: https://github.com/gregorw/discriminable
|
139
|
+
source_code_uri: https://github.com/gregorw/discriminable
|
140
|
+
changelog_uri: https://github.com/gregorw/discriminable/blob/main/CHANGELOG.md
|
141
141
|
rubygems_mfa_required: 'false'
|
142
142
|
post_install_message:
|
143
143
|
rdoc_options: []
|