Skip to content

Commit

Permalink
fix: Allow to use attribute aliases
Browse files Browse the repository at this point in the history
Add sti classic tests with alias

Make aliases work

Fix cops
  • Loading branch information
gregorw committed Apr 20, 2022
1 parent c9cc3f8 commit 448e4e8
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 4 deletions.
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
- [ ] Rails 5 support
- [ ] test permitted attributes
- [ ] `self.abstract_class = true`
- [ ] scoping…
- [ ] ~~use `type` column with enum, int, string, etc.~~
21 changes: 17 additions & 4 deletions lib/discriminable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,27 @@ def discriminable(**options)

attribute, map = options.first

# E.g. { value: "ClassName" }
self.discriminable_map = map.with_indifferent_access

# Use first key as default discriminator
# { a: "C", b: "C" }.invert => { "C" => :b }
# { a: "C", b: "C" }.to_a.reverse.to_h.invert => { "C" => :a }
# E.g. { "ClassName" => :value }
self.discriminable_inverse_map = map.to_a.reverse.to_h.invert
self.inheritance_column = attribute.to_s

attribute = attribute.to_s
self.inheritance_column = attribute_aliases[attribute] || attribute
end

def discriminable_by(attribute)
raise "Subclasses should not override .discriminable_by" unless base_class?

self.discriminable_map ||= discriminable_map_memoized
self.discriminable_inverse_map ||= discriminable_inverse_map_memoized
self.inheritance_column = attribute.to_s

attribute = attribute.to_s
self.inheritance_column = attribute_aliases[attribute] || attribute
end

def discriminable_as(*values)
Expand All @@ -61,6 +67,7 @@ def discriminable_as(*values)
end
end

# This is the value of the discriminable attribute
def sti_name
discriminable_inverse_map[name]
end
Expand All @@ -78,8 +85,7 @@ def subclass_from_attributes(attrs)
attrs = attrs.to_h if attrs.respond_to?(:permitted?)
return unless attrs.is_a?(Hash)

value = attrs.with_indifferent_access[inheritance_column]
value = base_class.type_for_attribute(inheritance_column).cast(value)
value = discriminable_value(attrs)
sti_class_for(value)
end

Expand All @@ -94,5 +100,12 @@ def discriminable_inverse_map_memoized
map[value] = value.constantize.discriminable_values&.first
end
end

def discriminable_value(attrs)
attrs = attrs.with_indifferent_access
value = attrs[inheritance_column]
value ||= attrs[attribute_aliases.invert[inheritance_column]]
base_class.type_for_attribute(inheritance_column).cast(value)
end
end
end
53 changes: 53 additions & 0 deletions test/test_open_closed_principle_aliased_integer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

require "helper"

class TestOpenClosedPrincipleAliasedInteger < Case
class Property < ActiveRecord::Base
include Discriminable

alias_attribute :kind, :kind_with_some_postfix

# No enum this time
discriminable_by :kind
end

class NumberProperty < Property
discriminable_as 1
end

class OptionProperty < Property
discriminable_as 2, 3
end

def setup
ActiveRecord::Schema.define do
create_table :properties do |t|
t.integer :kind_with_some_postfix, limit: 1, null: true
end
end
end

def test_sti_name_default
assert_equal 1, NumberProperty.sti_name
assert_equal 2, OptionProperty.sti_name
end

def test_creation_and_loading
assert_equal 1, NumberProperty.create.kind
assert_equal 2, OptionProperty.create.kind
assert_instance_of NumberProperty, Property.first
assert_instance_of OptionProperty, Property.last
end

def test_creation_using_parent
assert_instance_of NumberProperty, Property.create(kind: 1)
assert_instance_of OptionProperty, Property.create(kind: 3)
end

def test_building
assert_instance_of NumberProperty, Property.new(kind: 1)
assert_instance_of OptionProperty, Property.new(kind: 2)
assert_instance_of OptionProperty, Property.new(kind: 3)
end
end
61 changes: 61 additions & 0 deletions test/test_sti_aliased.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

require "helper"

class TestStiAliased < Case
class Order < ActiveRecord::Base
self.inheritance_column = "type_with_some_postfix"
alias_attribute :type, :type_with_some_postfix
end

class Cart < Order
end

def setup
ActiveRecord::Schema.define do
create_table :orders do |t|
t.string :type_with_some_postfix
end
end
end

def test_class_methods
assert_equal Order.inheritance_column, "type_with_some_postfix"
assert_equal Order.sti_name, "TestStiAliased::Order"
assert_equal Cart.sti_name, "TestStiAliased::Cart"
end

def test_count
Order.create
Cart.create
assert_equal 2, Order.count

assert_equal 1, Cart.count
end

def test_loading
Order.create
Cart.create
assert_instance_of TestStiAliased::Order, Order.first
assert_instance_of TestStiAliased::Cart, Cart.first

assert_instance_of TestStiAliased::Cart, Order.where(type: "TestStiAliased::Cart").first
end

def test_creating_and_building
assert_instance_of TestStiAliased::Cart, Order.new(type_with_some_postfix: "TestStiAliased::Cart")
assert_instance_of TestStiAliased::Cart, Order.where(type: "TestStiAliased::Cart").build

skip "This is not supported"
assert_instance_of TestStiAliased::Cart, Order.new(type: "TestStiAliased::Cart")
end

def test_changes
refute_predicate Order.new, :changed?
assert_empty Order.new.changes

# See https://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html
assert_predicate Cart.new, :changed?
assert_equal({ "type_with_some_postfix" => [nil, "TestStiAliased::Cart"] }, Cart.new.changes)
end
end

0 comments on commit 448e4e8

Please sign in to comment.