module Sequel::Plugins::UnusedAssociations::ClassMethods

Public Instance Methods

associate(type, assoc_name, opts=OPTS) click to toggle source

If modifying associations, and this association is marked as not used, and the association does not include the specific :is_used option, skip defining the association.

Calls superclass method
# File lib/sequel/plugins/unused_associations.rb, line 283
def associate(type, assoc_name, opts=OPTS)
  if !opts[:is_used] && @unused_associations_data && (data = @unused_associations_data[name]) && data[assoc_name.to_s] == 'unused'
    return
  end
  
  super
end
association_reflection(association) click to toggle source

Record access to association reflections to determine which associations are not used.

Calls superclass method
# File lib/sequel/plugins/unused_associations.rb, line 274
def association_reflection(association)
  uar = used_association_reflections
  Sequel.synchronize{uar[association] ||= true}
  super
end
delete_unused_associations_files() click to toggle source

Delete the unused associations coverage file and unused associations data file, if either exist.

# File lib/sequel/plugins/unused_associations.rb, line 460
def delete_unused_associations_files
  _delete_unused_associations_file(@unused_associations_coverage_file)
  _delete_unused_associations_file(@unused_associations_file)
end
freeze() click to toggle source

Setup the #used_association_reflections storage before freezing

Calls superclass method
# File lib/sequel/plugins/unused_associations.rb, line 292
def freeze
  used_association_reflections
  super
end
unused_association_options(opts=OPTS) click to toggle source

Return an array of unused association options. These are associations some but not all of the association methods are used, according to the coverage information. Each entry in the array is an array of three elements. The first element is the class name string, the second element is the association name string, and the third element is a hash of association options that can be used in the association so it does not define methods that are not used.

Options:

:unused_associations_data

The data to use for determining which associations are unused, which is returned from update_unused_associations_data. If not given, loads the data from the file specified by the :file plugin option.

# File lib/sequel/plugins/unused_associations.rb, line 444
def unused_association_options(opts=OPTS)
  unused_associations_data = opts[:unused_associations_data] || Sequel.parse_json(File.binread(@unused_associations_file))

  unused_association_methods = []
  unused_associations_data.each do |sc, associations|
    associations.each do |assoc, unused|
      unless unused == 'unused'
        unused_association_methods << [sc, assoc, set_unused_options_for_association({}, unused)]
      end
    end
  end
  unused_association_methods
end
unused_associations(opts=OPTS) click to toggle source

Return an array of unused associations. These are associations where none of the association methods are used, according to the coverage information. Each entry in the array is an array of two strings, with the first string being the class name and the second string being the association name.

Options:

:unused_associations_data

The data to use for determining which associations are unused, which is returned from update_unused_associations_data. If not given, loads the data from the file specified by the :file plugin option.

# File lib/sequel/plugins/unused_associations.rb, line 417
def unused_associations(opts=OPTS)
  unused_associations_data = opts[:unused_associations_data] || Sequel.parse_json(File.binread(@unused_associations_file))

  unused_associations = []
  unused_associations_data.each do |sc, associations|
    associations.each do |assoc, unused|
      if unused == 'unused'
        unused_associations << [sc, assoc]
      end
    end
  end
  unused_associations
end
update_associations_coverage(opts=OPTS) click to toggle source

Parse the coverage result, and return the coverage data for the associations for descendants of this class. If the plugin uses the :coverage_file option, the existing coverage file will be loaded if present, and before the method returns, the coverage file will be updated.

Options:

:coverage_result

The coverage result to use. This defaults to Coverage.result.

# File lib/sequel/plugins/unused_associations.rb, line 304
def update_associations_coverage(opts=OPTS)
  coverage_result = opts[:coverage_result] || Coverage.result
  module_mapping = {}
  file = @unused_associations_coverage_file

  coverage_data = if file && File.file?(file)
    Sequel.parse_json(File.binread(file))
  else
    {}
  end

  ([self] + descendants).each do |sc|
    next if sc.associations.empty? || !sc.name
    module_mapping[sc.send(:overridable_methods_module)] = sc
    cov_data = coverage_data[sc.name] ||= {''=>[]}
    cov_data[''].concat(sc.used_association_reflections.keys.map(&:to_s).sort).uniq!
  end

  coverage_result.each do |file, coverage|
    coverage[:methods].each do |(mod, meth), times|
      next unless sc = module_mapping[mod]
      coverage_data[sc.name][meth.to_s] ||= 0
      coverage_data[sc.name][meth.to_s] += times
    end
  end

  if file
    File.binwrite(file, Sequel.object_to_json(coverage_data))
  end

  coverage_data
end
update_unused_associations_data(options=OPTS) click to toggle source

Parse the coverage data returned by update_associations_coverage, and return data on unused associations and unused association methods.

Options:

:coverage_data

The coverage data to use. If not given, it is taken from the file specified by the :coverage_file plugin option.

:keep_coverage

Do not delete the file specified by the :coverage_file plugin option, even if it exists.

# File lib/sequel/plugins/unused_associations.rb, line 345
def update_unused_associations_data(options=OPTS)
  coverage_data = options[:coverage_data] || Sequel.parse_json(File.binread(@unused_associations_coverage_file))

  unused_associations_data = {}

  ([self] + descendants).each do |sc|
    next unless cov_data = coverage_data[sc.name]
    reflection_data = cov_data[''] || []

    sc.association_reflections.each do |assoc, ref|
      # Only report associations for the class they are defined in
      next unless ref[:model] == sc

      # Do not report associations using methods_module option, because this plugin only
      # looks in the class's overridable_methods_module
      next if ref[:methods_module]

      info = {}
      if reflection_data.include?(assoc.to_s)
        info[:used] = [:reflection]
      end

      _update_association_coverage_info(info, cov_data, ref.dataset_method, :dataset_method)
      _update_association_coverage_info(info, cov_data, ref.association_method, :association_method)

      unless ref[:orig_opts][:read_only]
        if ref.returns_array?
          _update_association_coverage_info(info, cov_data, ref[:add_method], :adder)
          _update_association_coverage_info(info, cov_data, ref[:remove_method], :remover)
          _update_association_coverage_info(info, cov_data, ref[:remove_all_method], :clearer)
        else
          _update_association_coverage_info(info, cov_data, ref[:setter_method], :setter)
        end
      end

      next if info.keys == [:missing]

      if !info[:used]
        (unused_associations_data[sc.name] ||= {})[assoc.to_s] = 'unused'
      elsif unused = info[:unused]
        if unused.include?(:setter) || [:adder, :remover, :clearer].all?{|k| unused.include?(k)}
          [:setter, :adder, :remover, :clearer].each do |k|
            unused.delete(k)
          end
          unused << :read_only
        end
        (unused_associations_data[sc.name] ||= {})[assoc.to_s] = unused.map(&:to_s)
      end
    end
  end

  if @unused_associations_file
    File.binwrite(@unused_associations_file, Sequel.object_to_json(unused_associations_data))
  end
  unless options[:keep_coverage]
    _delete_unused_associations_file(@unused_associations_coverage_file)
  end

  unused_associations_data
end
used_association_reflections() click to toggle source

Synchronize access to the used association reflections.

# File lib/sequel/plugins/unused_associations.rb, line 269
def used_association_reflections
  Sequel.synchronize{@used_association_reflections ||= {}}
end

Private Instance Methods

_delete_unused_associations_file(file) click to toggle source

Delete the given file if it exists.

# File lib/sequel/plugins/unused_associations.rb, line 468
def _delete_unused_associations_file(file)
  if file && File.file?(file)
    File.unlink(file)
  end
end
_update_association_coverage_info(info, coverage_data, meth, key) click to toggle source

Update the info hash with information on whether the given method was called, according to the coverage information.

# File lib/sequel/plugins/unused_associations.rb, line 476
def _update_association_coverage_info(info, coverage_data, meth, key)
  type = case coverage_data[meth.to_s]
  when 0
    :unused
  when Integer
    :used
  else
    # Missing here means there is no coverage information for the
    # the method, which indicates the expected method was never
    # defined.  In that case, it can be ignored.
    :missing
  end

  (info[type] ||= []) << key
end
def_association(opts) click to toggle source

If modifying associations, and this association has unused association methods, automatically set the appropriate options so the unused association methods are not defined, unless the association explicitly uses the :is_used options.

Calls superclass method
# File lib/sequel/plugins/unused_associations.rb, line 509
def def_association(opts)
  if !opts[:is_used] && @unused_associations_data && (data = @unused_associations_data[name]) && (unused = data[opts[:name].to_s])
    set_unused_options_for_association(opts, unused)
  end
  
  super
end
set_unused_options_for_association(opts, unused) click to toggle source

Based on the value of the unused, update the opts hash with association options that will prevent unused association methods from being defined.

# File lib/sequel/plugins/unused_associations.rb, line 495
def set_unused_options_for_association(opts, unused)
  opts[:read_only] = true if unused.include?('read_only')
  opts[:no_dataset_method] = true if unused.include?('dataset_method')
  opts[:no_association_method] = true if unused.include?('association_method')
  opts[:adder] = nil if unused.include?('adder')
  opts[:remover] = nil if unused.include?('remover')
  opts[:clearer] = nil if unused.include?('clearer')
  opts
end