Class: DepSelector::Selector
- Inherits:
-
Object
- Object
- DepSelector::Selector
- Defined in:
- lib/dep_selector/selector.rb
Constant Summary collapse
- DEFAULT_ERROR_REPORTER =
ErrorReporter::SimpleTreeTraverser.new
Instance Attribute Summary collapse
-
#dep_graph ⇒ Object
Returns the value of attribute dep_graph.
-
#error_reporter ⇒ Object
Returns the value of attribute error_reporter.
-
#time_bound ⇒ Object
Returns the value of attribute time_bound.
Instance Method Summary collapse
-
#find_solution(solution_constraints, valid_packages = nil) ⇒ Object
Based on solution_constraints, this method tries to find an assignment of PackageVersions that is compatible with the DependencyGraph.
-
#initialize(dep_graph, time_bound = 5, error_reporter = DEFAULT_ERROR_REPORTER) ⇒ Selector
constructor
A new instance of Selector.
Constructor Details
#initialize(dep_graph, time_bound = 5, error_reporter = DEFAULT_ERROR_REPORTER) ⇒ Selector
Returns a new instance of Selector.
37 38 39 40 41 |
# File 'lib/dep_selector/selector.rb', line 37 def initialize(dep_graph, time_bound = 5, error_reporter = DEFAULT_ERROR_REPORTER) @dep_graph = dep_graph @time_bound = time_bound @error_reporter = error_reporter end |
Instance Attribute Details
#dep_graph ⇒ Object
Returns the value of attribute dep_graph.
33 34 35 |
# File 'lib/dep_selector/selector.rb', line 33 def dep_graph @dep_graph end |
#error_reporter ⇒ Object
Returns the value of attribute error_reporter.
33 34 35 |
# File 'lib/dep_selector/selector.rb', line 33 def error_reporter @error_reporter end |
#time_bound ⇒ Object
Returns the value of attribute time_bound.
33 34 35 |
# File 'lib/dep_selector/selector.rb', line 33 def time_bound @time_bound end |
Instance Method Details
#find_solution(solution_constraints, valid_packages = nil) ⇒ Object
Based on solution_constraints, this method tries to find an assignment of PackageVersions that is compatible with the DependencyGraph. If one cannot be found, the constraints are added one at a time until the first unsatisfiable constraint is detected. Once the unsatisfiable solution constraint is identified, required non-existent packages and the most constrained packages are identified and thrown in a NoSolutionExists exception.
If a solution constraint refers to a package that doesn’t exist or the constraint matches no versions, it is considered invalid. All invalid solution constraints are collected and raised in an InvalidSolutionConstraints exception. If valid_packages is non-nil, it is considered the authoritative list of extant Packages; otherwise, Package#valid? is used. This is useful if the dependency graph represents an already filtered set of packages such that a Package actually exists in your domain but is added to the dependency graph with no versions, in which case Package#valid? would return false even though we don’t want to report that the package is non-existent.
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 |
# File 'lib/dep_selector/selector.rb', line 63 def find_solution(solution_constraints, valid_packages = nil) # this is a performance optimization so that packages that are # completely unreachable by the solution constraints don't get # added to the CSP packages_to_include_in_solve = trim_unreachable_packages(dep_graph, solution_constraints) begin # first, try to solve the whole set of constraints solve(dep_graph.clone, solution_constraints, valid_packages, packages_to_include_in_solve) rescue Exceptions::NoSolutionFound, Exceptions::TimeBoundExceededNoSolution # since we're here, solving the whole system failed, so add # the solution_constraints one-by-one and try to solve in # order to find the constraint that breaks the system in order # to give helpful debugging info # # TODO [cw,2010/11/28]: for an efficiency gain, instead of # continually re-building the problem and looking for a # solution, turn solution_constraints into a Generator and # iteratively add and solve in order to re-use # propagations. This will require separating setting up the # constraints from searching for the solution. Timeout::timeout(@time_bound, Exceptions::TimeBoundExceededNoSolution) do solution_constraints.each_index do |idx| workspace = dep_graph.clone begin solve(workspace, solution_constraints[0..idx], valid_packages, packages_to_include_in_solve) rescue Exceptions::NoSolutionFound => nsf disabled_packages = packages_to_include_in_solve.inject([]) do |acc, elt| pkg = workspace.package(elt.name) acc << pkg if nsf.unsatisfiable_problem.is_package_disabled?(pkg.gecode_package_id) acc end # disambiguate between packages disabled becuase they # don't exist and those that have otherwise problematic # constraints disabled_non_existent_packages = [] disabled_most_constrained_packages = [] disabled_packages.each do |disabled_pkg| disabled_collection = if disabled_pkg.valid? || (valid_packages && valid_packages.include?(disabled_pkg)) disabled_most_constrained_packages else disabled_non_existent_packages end disabled_collection << disabled_pkg end # Pick the first non-existent or most-constrained package # that was required or the package whose constraints had # to be disabled in order to find a solution and generate # feedback for it. We only report feedback for one # package, because it is in fact actionable and dispalying # feedback for every disabled package would probably be # too long. The full set of disabled packages is # accessible in the NoSolutionExists exception. disabled_package_to_report_on = disabled_non_existent_packages.first || disabled_most_constrained_packages.first feedback = error_reporter.give_feedback(dep_graph, solution_constraints, idx, disabled_package_to_report_on) raise Exceptions::NoSolutionExists.new(feedback, solution_constraints[idx], disabled_non_existent_packages, disabled_most_constrained_packages) end end end end end |