1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
# frozen-string-literal: true
module Sequel
module Plugins
# The detect_unnecessary_association_options plugin can detect unnecessary
# association options, and either warn or raise if they are detected.
# This allows you to find and remove the unnecessary options.
# Association options are considered unnecessary if they specify the same
# value as Sequel's defaults.
#
# To detect unnecessary association options, you should load the plugin
# into your model base class (e.g. Sequel::Model) before loading your model
# classes. Then, after all models have been loaded, you can call the
# detect_unnecessary_association_options on each to check for unnecessary
# association options. Additionally, if you are calling finalize_associations,
# it will automatically check for unnecessary association options.
#
# A typical usage would be to combine this with the subclasses plugin:
#
# Sequel::Model.plugin :detect_unnecessary_association_options
# Sequel::Model.plugin :subclasses
# # load model classes
#
# # implicitly check all subclasses when freezing descendants
# Sequel::Model.freeze_descendants
#
# # or, if not freezing all descendants
# Sequel::Model.descendants.each(&:detect_unnecessary_association_options)
#
# By default, the plugin warns for every unnecessary association option.
# To raise an error instead, you can pass the <tt>action: :raise</tt> option when loading the
# plugin:
#
# Sequel::Model.plugin :detect_unnecessary_association_options, action: :raise
#
# This plugin only detects the most common unnecessary association options, such as:
#
# * :class (all associations)
# * :key and :primary_key (associations without join tables)
# * :join_table, :left_key, :right_key, :left_primary_key, :right_primary_key (single join table associations)
# * :left_primary_key, :right_primary_key (*_through_many associations)
#
# Only association types supported by default or supported by a plugin that
# ships with Sequel are supported by this plugin. Other association types are
# ignored.
module DetectUnnecessaryAssociationOptions
def self.configure(model, opts={})
model.instance_variable_set(:@detect_unnecessary_association_options_action, opts[:action] || :warn)
end
# Raised if the plugin action is to raise and an unnecessary association option
# is detected.
class UnnecessaryAssociationOption < Sequel::Error
end
module ClassMethods
Plugins.inherited_instance_variables(self, :@detect_unnecessary_association_options_action => nil)
# Implicitly check for unnecessary association options when finalizing associations.
def finalize_associations
res = super
detect_unnecessary_association_options
res
end
# Check for unnecessary association options.
def detect_unnecessary_association_options
@association_reflections.each_value do |ref|
meth = "detect_unnecessary_association_options_#{ref[:type]}"
# Expected to call private methods.
# Ignore unrecognized association types.
# External association types can define the appropriate method to
# support their own unnecessary association option checks.
if respond_to?(meth, true)
# All recognized association types need same class check
_detect_unnecessary_association_options_class(ref)
send(meth, ref)
end
end
nil
end
private
# Action to take if an unnecessary association option is detected.
def unnecessary_association_options_detected(ref, key)
if @detect_unnecessary_association_options_action == :raise
raise UnnecessaryAssociationOption, "#{ref.inspect} :#{key} option unnecessary"
else
warn "#{ref.inspect} :#{key} option unnecessary"
end
end
# Detect unnecessary :class option.
def _detect_unnecessary_association_options_class(ref)
return unless ref[:orig_class]
h = {}
name = ref[:name]
late_binding_class_option(h, ref.returns_array? ? singularize(name) : name)
begin
default_association_class = constantize(h[:class_name])
actual_association_class = ref.associated_class
rescue NameError
# Do not warn. For the default association class to not be a valid
# constant is expected. For the actual association class to not be
# a valid constant is not expected and a bug in the association, but
# the job of this plugin is not to detect invalid options, only
# unnecessary options.
else
if default_association_class.equal?(actual_association_class)
unnecessary_association_options_detected(ref, "class")
end
end
end
# Detect other unnecessary options. An option is considered unnecessary
# if the key was submitted as an association option and the value for
# the option is the same as the given value.
def _detect_unnecessary_association_options_key_value(ref, key, value)
if ref[:orig_opts].has_key?(key) && ref[:orig_opts][key] == value
unnecessary_association_options_detected(ref, key)
end
end
# Same as _detect_unnecessary_association_options_key_value, but calls
# the default_* method on the association reflection to get the default value.
def _detect_unnecessary_association_options_key(ref, key)
_detect_unnecessary_association_options_key_value(ref, key, ref.send(:"default_#{key}"))
end
def detect_unnecessary_association_options_many_to_one(ref)
_detect_unnecessary_association_options_key(ref, :key)
_detect_unnecessary_association_options_key_value(ref, :primary_key, ref.associated_class.primary_key)
end
alias detect_unnecessary_association_options_pg_array_to_many detect_unnecessary_association_options_many_to_one
def detect_unnecessary_association_options_one_to_many(ref)
_detect_unnecessary_association_options_key(ref, :key)
_detect_unnecessary_association_options_key_value(ref, :primary_key, primary_key)
end
alias detect_unnecessary_association_options_one_to_one detect_unnecessary_association_options_one_to_many
alias detect_unnecessary_association_options_many_to_pg_array detect_unnecessary_association_options_one_to_many
def detect_unnecessary_association_options_many_to_many(ref)
[:join_table, :left_key, :right_key].each do |key|
_detect_unnecessary_association_options_key(ref, key)
end
_detect_unnecessary_association_options_key_value(ref, :left_primary_key, primary_key)
_detect_unnecessary_association_options_key_value(ref, :right_primary_key, ref.associated_class.primary_key)
end
alias detect_unnecessary_association_options_one_through_one detect_unnecessary_association_options_many_to_many
def detect_unnecessary_association_options_many_through_many(ref)
_detect_unnecessary_association_options_key_value(ref, :left_primary_key, primary_key)
_detect_unnecessary_association_options_key_value(ref, :right_primary_key, ref.associated_class.primary_key)
end
alias detect_unnecessary_association_options_one_through_many detect_unnecessary_association_options_many_through_many
end
end
end
end
|