discriminable 2.1.0 → 2.2.1

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
  SHA256:
3
- metadata.gz: e6d47dda88efcbf4f311ff843e0b4e3e06ffda5f205d810e9a4ab4ad089d13ad
4
- data.tar.gz: 64d5ad043e453d28d98603d5c3153783ac153c97bcd70179ea86e962bcdd4f85
3
+ metadata.gz: 332984f03395d4725f162027692324d66fa591f19820d7690201677c37ab8d0c
4
+ data.tar.gz: 49017319a8f0cc0ff6bbf498ec32fc68fc51cf062821d683e58b1340b317e7f3
5
5
  SHA512:
6
- metadata.gz: 1e272415e3aef0d7c365b0189564261ac138029d3feb1e8bcc7c01468132763f2af723c8b73df8806f5f313f30b0d7bbbe070b00f3f67ddbb4103677aa6d3b3e
7
- data.tar.gz: 04c96941797c296976f88ecf00b84c51c26d9dcfd954012c3abec8ae17dec6bcd03d6958f47af2e01ba93bd206ca42f3e8c5fa0eff5019e7925e81d28f41ce24
6
+ metadata.gz: 23b33e6a0ebe2009ec62ef57747fbb585cd400837476a3199f28ff8733f607f3974b667713fd788457824adaa9a09ee93e0093e071010016d389b2eab2ec415a
7
+ data.tar.gz: fabeb7320aa926567261756a3d2c9863feb193abfd4124da42a47588d84ab3b6a4736bd197d850aa74fbd8c6841c7077bbeb41020549f0e962ca54f0d132e494
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ### [2.2.1](https://github.com/gregorw/discriminable/compare/v2.2.0...v2.2.1) (2022-04-17)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * Simplify STI using OCP ([68f49c2](https://github.com/gregorw/discriminable/commit/68f49c2d0b7a9437525fe0bc9ebef1927fccb2e8))
9
+
10
+ ## [2.2.0](https://github.com/gregorw/discriminable/compare/v2.1.1...v2.2.0) (2022-04-16)
11
+
12
+
13
+ ### Features
14
+
15
+ * Alternative syntax respecting open-closed principle ([8a8f885](https://github.com/gregorw/discriminable/commit/8a8f8857dc24753dc08f21b2bfbb7b4317b7ed5c))
16
+
17
+ ### [2.1.1](https://github.com/gregorw/discriminable/compare/v2.1.0...v2.1.1) (2022-04-15)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * Subclass is not properly initialized on new objects ([c24706c](https://github.com/gregorw/discriminable/commit/c24706c9e8cd4c80ea20016e341f010a5a3c792b))
23
+ * Use correct, i.e. first value, when using multiple states for same subclass ([3a05c0d](https://github.com/gregorw/discriminable/commit/3a05c0d3a76080a39f40b1d57ce2862316693f97))
24
+
3
25
  ## [2.1.0](https://github.com/gregorw/discriminable/compare/v2.0.0...v2.1.0) (2022-04-01)
4
26
 
5
27
 
data/README.md CHANGED
@@ -1,10 +1,20 @@
1
- [![Ruby](https://github.com/gregorw/discriminable/actions/workflows/main.yml/badge.svg)](https://github.com/gregorw/discriminable/actions/workflows/main.yml)
1
+
2
2
 
3
3
  # Discriminable
4
4
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/discriminable`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ [![Gem Version](https://badge.fury.io/rb/discriminable.svg)](https://rubygems.org/gems/discriminable)
6
+ [![CI](https://github.com/gregorw/discriminable/actions/workflows/main.yml/badge.svg)](https://github.com/gregorw/discriminable/actions/workflows/main.yml)
7
+ [![Maintainability](https://api.codeclimate.com/v1/badges/94041c5f946b64040368/maintainability)](https://codeclimate.com/github/gregorw/discriminable/maintainability)
8
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/94041c5f946b64040368/test_coverage)](https://codeclimate.com/github/gregorw/discriminable/test_coverage)
9
+
10
+ Single table inheritance (STI) for Ruby on Rails models (ActiveRecord) using enum, boolean, string and integer column types.
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).
6
17
 
7
- TODO: Delete this and the text above, and describe your gem
8
18
 
9
19
  ## Installation
10
20
 
@@ -18,13 +28,45 @@ If bundler is not being used to manage dependencies, install the gem by executin
18
28
 
19
29
  ## Usage
20
30
 
21
- TODO: Write usage instructions here
31
+ ```ruby
32
+ class Order < ActiveRecord::Base
33
+ include Discriminable
34
+
35
+ enum state: { open: 0, completed: 1 }
36
+ discriminable state: { open: "Cart" }
37
+ end
38
+
39
+ class Cart < Order
40
+ end
41
+
42
+ Cart.create
43
+ => #<Cart id: 1, state: "open">
44
+ Order.all
45
+ => #<ActiveRecord::Relation [#<Cart id: 1, state: "open">]>
46
+ ```
47
+
48
+ ### Alternative syntax
49
+
50
+ Instead of defining subclass names within the parent you may prefer to be open for extension but closed for modification (open-closed principle) using the alternative syntax.
51
+
52
+ ```ruby
53
+ class Order < ActiveRecord::Base
54
+ include Discriminable
55
+
56
+ enum state: { open: 0, completed: 1 }
57
+ discriminable_by :state
58
+ end
22
59
 
23
- ## Development
60
+ class Cart < Order
61
+ discriminable_as :open
62
+ end
24
63
 
25
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
64
+ Cart.create
65
+ => #<Cart id: 1, state: "open">
66
+ Order.all
67
+ => #<ActiveRecord::Relation [#<Cart id: 1, state: "open">]>
68
+ ```
26
69
 
27
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
70
 
29
71
  ## Contributing
30
72
 
data/Rakefile CHANGED
@@ -14,3 +14,6 @@ require "rubocop/rake_task"
14
14
  RuboCop::RakeTask.new
15
15
 
16
16
  task default: %i[test rubocop]
17
+
18
+ # Release task
19
+ # gem build *.gemspec && gem push *.gem && rm *.gem
data/TODO.md CHANGED
@@ -1,2 +1,6 @@
1
+ - [x] multiple values per subclass
2
+ - [x] default to first value when using hash syntax
3
+ - [x] open-closed principle
4
+ - [ ] use `type` column with enum, int, string, etc.
5
+ - [ ] test permitted attributes
1
6
  - [ ] `self.abstract_class = true`
2
- - [ ] …
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Discriminable
4
- VERSION = "2.1.0"
4
+ VERSION = "2.2.1"
5
5
  end
data/lib/discriminable.rb CHANGED
@@ -25,7 +25,8 @@ module Discriminable
25
25
 
26
26
  included do
27
27
  class_attribute :discriminable_map, instance_writer: false
28
- class_attribute :discriminable_inverse, instance_writer: false
28
+ class_attribute :discriminable_inverse_map, instance_writer: false
29
+ class_attribute :discriminable_values, instance_writer: false
29
30
  end
30
31
 
31
32
  # Specify the column to use for discrimination.
@@ -33,15 +34,41 @@ module Discriminable
33
34
  def discriminable(**options)
34
35
  raise "Subclasses should not override .discriminable" unless base_class?
35
36
 
36
- discriminator, map = options.first
37
+ attribute, map = options.first
37
38
 
38
39
  self.discriminable_map = map.with_indifferent_access
39
- self.discriminable_inverse = map.invert
40
- self.inheritance_column = discriminator.to_s
40
+
41
+ # Use first key as default discriminator
42
+ # { a: "C", b: "C" }.invert => { "C" => :b }
43
+ # { a: "C", b: "C" }.to_a.reverse.to_h.invert => { "C" => :a }
44
+ self.discriminable_inverse_map = map.to_a.reverse.to_h.invert
45
+ self.inheritance_column = attribute.to_s
46
+ end
47
+
48
+ # rubocop:disable Metrics/AbcSize
49
+ def discriminable_by(attribute)
50
+ raise "Subclasses should not override .discriminable_by" unless base_class?
51
+
52
+ self.inheritance_column = attribute.to_s
53
+ self.discriminable_map ||= Hash.new do |map, value|
54
+ map[value] = discriminable_descendants(value)&.name
55
+ end
56
+ self.discriminable_inverse_map ||= Hash.new do |map, value|
57
+ map[value] = value.constantize.discriminable_values&.first
58
+ end
59
+ end
60
+ # rubocop:enable Metrics/AbcSize
61
+
62
+ def discriminable_as(*values)
63
+ raise "Only subclasses should specify .discriminable_as" if base_class?
64
+
65
+ self.discriminable_values = values.map do |value|
66
+ value.instance_of?(Symbol) ? value.to_s : value
67
+ end
41
68
  end
42
69
 
43
70
  def sti_name
44
- discriminable_inverse[name]
71
+ discriminable_inverse_map[name]
45
72
  end
46
73
 
47
74
  def sti_class_for(value)
@@ -57,8 +84,13 @@ module Discriminable
57
84
  attrs = attrs.to_h if attrs.respond_to?(:permitted?)
58
85
  return unless attrs.is_a?(Hash)
59
86
 
60
- value = base_class.type_for_attribute(inheritance_column).cast(attrs[inheritance_column])
87
+ value = attrs.with_indifferent_access[inheritance_column]
88
+ value = base_class.type_for_attribute(inheritance_column).cast(value)
61
89
  sti_class_for(value)
62
90
  end
91
+
92
+ def discriminable_descendants(value)
93
+ descendants.detect { |d| d.discriminable_values.include? value }
94
+ end
63
95
  end
64
96
  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.1.0
4
+ version: 2.2.1
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-01 00:00:00.000000000 Z
11
+ date: 2022-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.26'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.17.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.17.0
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: sqlite3
85
99
  requirement: !ruby/object:Gem::Requirement