From 7dd37708a94aab658a7383e6fbca78c7036f709f Mon Sep 17 00:00:00 2001 From: Tom Johnson Date: Wed, 29 Jun 2016 15:26:30 -0700 Subject: [PATCH 1/2] Fix RDF::List#== comparisons to RDF::Values Ruby 2.3.0 changed the behavior of [`Comparable#==`](https://bugs.ruby-lang.org/issues/7688) to avoid hiding errors. This led to `NoMethodError`s for comparisons that return `false` for previous Rubies. This introduces a custom `RDF::List#==` implementation for explicitly supported comparisons between `RDF::Value` classes. We return `false` immediately for `RDF::Value`s which are not `#list?`; falling back on `Comparable` for other types. Further, `RDF::List`s with three elements that happen to coincide with the terms of an RDF::Statement would previously return `true`. E.g.: `RDF::List[:s, :p, :o] == RDF::Statement(:s, :p, :o)`. This unusual edge case is patched by way of the changes described above. Closes #304 --- lib/rdf/model/list.rb | 9 ++++++++- spec/model_list_spec.rb | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/rdf/model/list.rb b/lib/rdf/model/list.rb index 80476fa0..e702c10d 100644 --- a/lib/rdf/model/list.rb +++ b/lib/rdf/model/list.rb @@ -139,6 +139,13 @@ def valid? # @return [RDF::Graph] the underlying graph storing the statements that constitute this list attr_reader :graph + ## + # @see RDF::Value#== + def ==(other) + return false if other.is_a?(RDF::Value) && !other.list? + super + end + ## # Returns the set intersection of this list and `other`. # @@ -272,7 +279,7 @@ def [](index) # a[3, 0] = "B" #=> [1, 2, "A", "B"] # # @overload []=(index, term) - # Replaces the element at `index` with `term`. + # Replaces the element at `index` with `term`. # @param [Integer] index # @param [RDF::Term] term # A non-RDF::Term is coerced to a Literal. diff --git a/spec/model_list_spec.rb b/spec/model_list_spec.rb index d63ba8b3..37fcd5ad 100644 --- a/spec/model_list_spec.rb +++ b/spec/model_list_spec.rb @@ -585,6 +585,20 @@ it "returns true when comparing a list to its contents" do expect(ten).to eq ten.to_a end + + it "returns false when comparing to non-list Values" do + expect(ten).not_to eq ten.subject + expect(ten).not_to eq ten.statements.first + expect(ten).not_to eq RDF::Node.new + expect(ten).not_to eq RDF::Graph.new + end + + it "returns false when comparing to similar statements" do + statement = RDF::Statement(:s, :p, :o) + quasistatement = RDF::List[:s, :p, :o] + + expect(quasistatement).not_to eq statement + end end describe "#empty?" do From 9bd9cadaf480252bf619df58bf726e3232833704 Mon Sep 17 00:00:00 2001 From: Tom Johnson Date: Wed, 29 Jun 2016 15:50:02 -0700 Subject: [PATCH 2/2] Fix Statement#== comparisons to RDF::List Similar to the change for #304, an `RDF::List` containing the terms of a Statement was evaluated as equal by `Statement#==`. This makes a change to preserve symmetry of the `#==` relation. --- lib/rdf/model/statement.rb | 15 ++++++++------- spec/model_statement_spec.rb | 4 ++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/rdf/model/statement.rb b/lib/rdf/model/statement.rb index 79ad8370..5e1ca927 100644 --- a/lib/rdf/model/statement.rb +++ b/lib/rdf/model/statement.rb @@ -135,7 +135,7 @@ def statement? # # @return [Boolean] def variable? - !(has_subject? && subject.resource? && + !(has_subject? && subject.resource? && has_predicate? && predicate.resource? && has_object? && (object.resource? || object.literal?) && (has_graph? ? graph_name.resource? : true )) @@ -150,7 +150,7 @@ def invalid? ## # @return [Boolean] def valid? - has_subject? && subject.resource? && subject.valid? && + has_subject? && subject.resource? && subject.valid? && has_predicate? && predicate.uri? && predicate.valid? && has_object? && object.term? && object.valid? && (has_graph? ? graph_name.resource? && graph_name.valid? : true ) @@ -252,25 +252,26 @@ def eql?(other) # @see RDF::Literal#== # @see RDF::Query::Variable#== def ==(other) - to_a == Array(other) + to_a == Array(other) && + !(other.is_a?(RDF::Value) && other.list?) end ## # Checks statement equality with patterns. - # + # # Uses `#eql?` to compare each of `#subject`, `#predicate`, `#object`, and - # `#graph_name` to those of `other`. Any statement part which is not + # `#graph_name` to those of `other`. Any statement part which is not # present in `self` is ignored. # # @example # statement = RDF::Statement.new(RDF::URI('s'), RDF::URI('p'), RDF::URI('o')) # pattern = RDF::Statement.new(RDF::URI('s'), RDF::URI('p'), RDF::Query::Variable.new) - # + # # # true # statement === statement # pattern === statement # RDF::Statement.new(nil, nil, nil) === statement - # + # # # false # statement === pattern # statement === RDF::Statement.new(nil, nil, nil) diff --git a/spec/model_statement_spec.rb b/spec/model_statement_spec.rb index bb9d98df..f85a6bda 100644 --- a/spec/model_statement_spec.rb +++ b/spec/model_statement_spec.rb @@ -207,6 +207,10 @@ expect(subject).not_to equal RDF::Statement.new s, p, o expect(subject).to equal subject end + + it "is not == a RDF::List" do + expect(subject).not_to eq RDF::List[*subject] + end end context "completness" do