Fix conflict between normalize and array_attribute macros.
Fix the `normalize` and `array_attribute` macros conflicting with each other on the WikiPage model. This meant code like `wiki_page.other_names = "foo bar"` didn't work. Both macros defined a `other_names=` method, but one method overrode the other. The fix is to use anonymous modules and prepend so we can chain method calls with super.
This commit is contained in:
36
app/logical/concerns/array_attribute.rb
Normal file
36
app/logical/concerns/array_attribute.rb
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
module ArrayAttribute
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
# Defines `<attribute>_string`, `<attribute>_string=`, and `<attribute>=`
|
||||||
|
# methods for converting an array attribute to or from a string.
|
||||||
|
#
|
||||||
|
# The `<attribute>=` setter parses strings into an array using the
|
||||||
|
# `parse` regex. The resulting strings can be converted to another type
|
||||||
|
# with the `cast` option.
|
||||||
|
def array_attribute(name, parse: /[^[:space:]]+/, cast: :itself)
|
||||||
|
mod = Module.new do
|
||||||
|
define_method "#{name}_string" do
|
||||||
|
send(name).join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method "#{name}_string=" do |value|
|
||||||
|
raise ArgumentError, "#{name} must be a String" unless value.respond_to?(:to_str)
|
||||||
|
send("#{name}=", value)
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method "#{name}=" do |value|
|
||||||
|
if value.respond_to?(:to_str)
|
||||||
|
super value.to_str.scan(parse).map(&cast)
|
||||||
|
elsif value.respond_to?(:to_a)
|
||||||
|
super value.to_a
|
||||||
|
else
|
||||||
|
raise ArgumentError, "#{name} must be a String or an Array"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
prepend mod
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -3,10 +3,14 @@ module Normalizable
|
|||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
def normalize(attribute, method_name)
|
def normalize(attribute, method_name)
|
||||||
define_method("#{attribute}=") do |value|
|
mod = Module.new do
|
||||||
normalized_value = self.class.send(method_name, value)
|
define_method("#{attribute}=") do |value|
|
||||||
super(normalized_value)
|
normalized_value = self.class.send(method_name, value)
|
||||||
|
super(normalized_value)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
prepend mod
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ class ApplicationRecord < ActiveRecord::Base
|
|||||||
include Deletable
|
include Deletable
|
||||||
include Mentionable
|
include Mentionable
|
||||||
include Normalizable
|
include Normalizable
|
||||||
|
include ArrayAttribute
|
||||||
extend HasBitFlags
|
extend HasBitFlags
|
||||||
extend Searchable
|
extend Searchable
|
||||||
|
|
||||||
@@ -157,37 +158,6 @@ class ApplicationRecord < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
concerning :AttributeMethods do
|
|
||||||
class_methods do
|
|
||||||
# Defines `<attribute>_string`, `<attribute>_string=`, and `<attribute>=`
|
|
||||||
# methods for converting an array attribute to or from a string.
|
|
||||||
#
|
|
||||||
# The `<attribute>=` setter parses strings into an array using the
|
|
||||||
# `parse` regex. The resulting strings can be converted to another type
|
|
||||||
# with the `cast` option.
|
|
||||||
def array_attribute(name, parse: /[^[:space:]]+/, cast: :itself)
|
|
||||||
define_method "#{name}_string" do
|
|
||||||
send(name).join(" ")
|
|
||||||
end
|
|
||||||
|
|
||||||
define_method "#{name}_string=" do |value|
|
|
||||||
raise ArgumentError, "#{name} must be a String" unless value.respond_to?(:to_str)
|
|
||||||
send("#{name}=", value)
|
|
||||||
end
|
|
||||||
|
|
||||||
define_method "#{name}=" do |value|
|
|
||||||
if value.respond_to?(:to_str)
|
|
||||||
super value.to_str.scan(parse).map(&cast)
|
|
||||||
elsif value.respond_to?(:to_a)
|
|
||||||
super value.to_a
|
|
||||||
else
|
|
||||||
raise ArgumentError, "#{name} must be a String or an Array"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def warnings
|
def warnings
|
||||||
@warnings ||= ActiveModel::Errors.new(self)
|
@warnings ||= ActiveModel::Errors.new(self)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ class WikiPage < ApplicationRecord
|
|||||||
normalize :body, :normalize_text
|
normalize :body, :normalize_text
|
||||||
normalize :other_names, :normalize_other_names
|
normalize :other_names, :normalize_other_names
|
||||||
|
|
||||||
|
array_attribute :other_names # XXX must come after `normalize :other_names`
|
||||||
|
|
||||||
validates :title, tag_name: true, presence: true, uniqueness: true, if: :title_changed?
|
validates :title, tag_name: true, presence: true, uniqueness: true, if: :title_changed?
|
||||||
validates :body, presence: true, unless: -> { is_deleted? || other_names.present? }
|
validates :body, presence: true, unless: -> { is_deleted? || other_names.present? }
|
||||||
validate :validate_rename
|
validate :validate_rename
|
||||||
validate :validate_other_names
|
validate :validate_other_names
|
||||||
|
|
||||||
array_attribute :other_names
|
|
||||||
has_one :tag, :foreign_key => "name", :primary_key => "title"
|
has_one :tag, :foreign_key => "name", :primary_key => "title"
|
||||||
has_one :artist, -> { active }, foreign_key: "name", primary_key: "title"
|
has_one :artist, -> { active }, foreign_key: "name", primary_key: "title"
|
||||||
has_many :versions, -> {order("wiki_page_versions.id ASC")}, :class_name => "WikiPageVersion", :dependent => :destroy
|
has_many :versions, -> {order("wiki_page_versions.id ASC")}, :class_name => "WikiPageVersion", :dependent => :destroy
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ class WikiPageTest < ActiveSupport::TestCase
|
|||||||
should normalize_attribute(:other_names).from(["ABC"]).to(["ABC"])
|
should normalize_attribute(:other_names).from(["ABC"]).to(["ABC"])
|
||||||
should normalize_attribute(:other_names).from(["foo", "foo"]).to(["foo"])
|
should normalize_attribute(:other_names).from(["foo", "foo"]).to(["foo"])
|
||||||
should normalize_attribute(:other_names).from(%w[foo*bar baz baz 加賀(艦これ)]).to(%w[foo*bar baz 加賀(艦これ)])
|
should normalize_attribute(:other_names).from(%w[foo*bar baz baz 加賀(艦これ)]).to(%w[foo*bar baz 加賀(艦これ)])
|
||||||
|
|
||||||
|
should normalize_attribute(:other_names).from("foo foo").to(["foo"])
|
||||||
|
should normalize_attribute(:other_names).from("foo bar").to(["foo", "bar"])
|
||||||
|
should normalize_attribute(:other_names).from("_foo_ Bar").to(["foo", "Bar"])
|
||||||
end
|
end
|
||||||
|
|
||||||
context "during title validation" do
|
context "during title validation" do
|
||||||
|
|||||||
Reference in New Issue
Block a user