Skip to content

Commit

Permalink
optionally return results as literals
Browse files Browse the repository at this point in the history
DEFAULT remains returning results as string or the specified data type

Also...
* remove pin of bundler
* add example results to README
  • Loading branch information
elrayle committed Nov 20, 2019
1 parent 19fc5fb commit 1ab3963
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 53 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ context = RDF::Graph.new << [uri, RDF::Vocab::DC.title, "Some Title"]

program = Ldpath::Program.parse my_program
output = program.evaluate uri, context: context
# => { ... }
# => {"title"=>["Some Title"]}
```

## Compatibility
Expand Down
1 change: 0 additions & 1 deletion ldpath.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Gem::Specification.new do |spec|
spec.add_dependency "linkeddata"

# spec.add_development_dependency "bixby", "~> 1.0.0"
spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "byebug"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"
Expand Down
28 changes: 20 additions & 8 deletions lib/ldpath/field_mapping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ def initialize(name:, selector:, field_type: nil, options: {})
@options = options
end

def evaluate(program, uri, context)
def evaluate(program, uri, context, maintain_literals: false)
case selector
when Ldpath::Selector
return to_enum(:evaluate, program, uri, context) unless block_given?
return to_enum(:evaluate, program, uri, context, maintain_literals: maintain_literals) unless block_given?

selector.evaluate(program, uri, context).each do |value|
yield transform_value(value)
selector.evaluate(program, uri, context, maintain_literals: maintain_literals).each do |value|
yield transform_value(value, maintain_literals: maintain_literals)
end
when RDF::Literal
Array(selector.canonicalize.object)
Expand All @@ -26,18 +26,30 @@ def evaluate(program, uri, context)

private

def transform_value(value)
v = if value.is_a? RDF::Literal
def transform_value(value, maintain_literals: false)
v = if value.is_a?(RDF::Literal) && !maintain_literals
value.canonicalize.object
else
value
end

if field_type
RDF::Literal.new(v.to_s, datatype: field_type).canonicalize.object
if field_type && !same_type(v, field_type)
v_literal = RDF::Literal.new(v.to_s, datatype: field_type)
maintain_literals ? v_literal : v_literal.canonicalize.object
else
v
end
end

def same_type(object, field_type)
case object
when RDF::Literal
object.comperable_datatype? field_type
when RDF::URI
field_type.to_s.end_with? 'anyURI'
else
false
end
end
end
end
6 changes: 3 additions & 3 deletions lib/ldpath/program.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ def initialize(mappings, default_loader: Ldpath::Loaders::Direct.new, prefixes:

end

def evaluate(uri, context: nil, limit_to_context: false)
result = Ldpath::Result.new(self, uri, context: context, limit_to_context: limit_to_context)
def evaluate(uri, context: nil, limit_to_context: false, maintain_literals: false)
result = Ldpath::Result.new(self, uri, context: context, limit_to_context: limit_to_context, maintain_literals: maintain_literals)
unless filters.empty?
return {} unless filters.all? { |f| f.evaluate(result, uri, result.context) }
return {} unless filters.all? { |f| f.evaluate(result, uri, result.context, maintain_literals: maintain_literals) }
end

result.to_hash
Expand Down
9 changes: 7 additions & 2 deletions lib/ldpath/result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ class Result
include Ldpath::Functions
attr_reader :program, :uri, :cache, :loaded

def initialize(program, uri, cache: RDF::Util::Cache.new, context: nil, limit_to_context: false)
def initialize(program, uri, cache: RDF::Util::Cache.new, context: nil, limit_to_context: false, maintain_literals: false)
@program = program
@uri = uri
@cache = cache
@loaded = {}
@context = context
@limit_to_context = limit_to_context
@maintain_literals = maintain_literals
end

def loading(uri, context)
Expand Down Expand Up @@ -59,7 +60,7 @@ def meta
private

def evaluate(mapping)
mapping.evaluate(self, uri, context)
mapping.evaluate(self, uri, context, maintain_literals: maintain_literals?)
end

def function_method?(function)
Expand All @@ -73,5 +74,9 @@ def mappings
def limit_to_context?
@limit_to_context
end

def maintain_literals?
@maintain_literals
end
end
end
45 changes: 24 additions & 21 deletions lib/ldpath/selectors.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Ldpath
class Selector
def evaluate(program, uris, context)
return to_enum(:evaluate, program, uris, context) unless block_given?
def evaluate(program, uris, context, maintain_literals: false)
return to_enum(:evaluate, program, uris, context, maintain_literals: maintain_literals) unless block_given?
enum_wrap(uris).map do |uri|
loading program, uri, context
enum_flatten_one(evaluate_one(uri, context)).each do |x|
Expand Down Expand Up @@ -55,15 +55,15 @@ def initialize(fname, arguments = [])
@arguments = Array(arguments)
end

def evaluate(program, uris, context)
return to_enum(:evaluate, program, uris, context) unless block_given?
def evaluate(program, uris, context, maintain_literals: false)
return to_enum(:evaluate, program, uris, context, maintain_literals: maintain_literals) unless block_given?

enum_wrap(uris).map do |uri|
loading program, uri, context
args = arguments.map do |i|
case i
when Selector
i.evaluate(program, uri, context)
i.evaluate(program, uri, context, maintain_literals: maintain_literals)
else
i
end
Expand Down Expand Up @@ -138,14 +138,14 @@ def initialize(property, repeat)
@repeat = repeat
end

def evaluate(program, uris, context)
return to_enum(:evaluate, program, uris, context) unless block_given?
def evaluate(program, uris, context, maintain_literals: false)
return to_enum(:evaluate, program, uris, context, maintain_literals: maintain_literals) unless block_given?

input = enum_wrap(uris)

(0..repeat.max).each_with_index do |i, idx|
break if input.none? || (repeat.max == Ldpath::Transform::Infinity && idx > 25) # we're probably lost..
input = property.evaluate program, input, context
input = property.evaluate program, input, context, maintain_literals: maintain_literals

next unless idx >= repeat.min

Expand All @@ -165,19 +165,20 @@ def initialize(left, right)
end

class PathSelector < CompoundSelector
def evaluate(program, uris, context, &block)
return to_enum(:evaluate, program, uris, context) unless block_given?
def evaluate(program, uris, context, maintain_literals: false, &block)
return to_enum(:evaluate, program, uris, context, maintain_literals: maintain_literals) unless block_given?

output = left.evaluate(program, uris, context)
right.evaluate(program, output, context, &block)
output = left.evaluate(program, uris, context, maintain_literals: maintain_literals)
right.evaluate(program, output, context, maintain_literals: maintain_literals, &block)
end
end

class UnionSelector < CompoundSelector
def evaluate(program, uris, context)
return to_enum(:evaluate, program, uris, context) unless block_given?
def evaluate(program, uris, context, maintain_literals: false)
return to_enum(:evaluate, program, uris, context, maintain_literals: maintain_literals) unless block_given?

enum_union(left.evaluate(program, uris, context), right.evaluate(program, uris, context)).each do |x|
enum_union(left.evaluate(program, uris, context, maintain_literals: maintain_literals),
right.evaluate(program, uris, context, maintain_literals: maintain_literals)).each do |x|
yield x
end
end
Expand All @@ -198,10 +199,11 @@ def enum_union(left, right)
end

class IntersectionSelector < CompoundSelector
def evaluate(program, uris, context)
return to_enum(:evaluate, program, uris, context) unless block_given?
def evaluate(program, uris, context, maintain_literals: false)
return to_enum(:evaluate, program, uris, context, maintain_literals: maintain_literals) unless block_given?

result = left.evaluate(program, uris, context).to_a & right.evaluate(program, uris, context).to_a
result = left.evaluate(program, uris, context, maintain_literals: maintain_literals).to_a &
right.evaluate(program, uris, context, maintain_literals: maintain_literals).to_a

result.each do |x|
yield x
Expand All @@ -216,10 +218,11 @@ def initialize(identifier, tap)
@tap = tap
end

def evaluate(program, uris, context)
return to_enum(:evaluate, program, uris, context) unless block_given?
def evaluate(program, uris, context, maintain_literals: false)
return to_enum(:evaluate, program, uris, context, maintain_literals: maintain_literals) unless block_given?

program.meta[identifier] = tap.evaluate(program, uris, context).map { |x| RDF::Literal.new(x.to_s).canonicalize.object }
program.meta[identifier] = tap.evaluate(program, uris, context, maintain_literals: maintain_literals)
.map { |x| RDF::Literal.new(x.to_s).canonicalize.object }

enum_wrap(uris).map do |uri|
loading program, uri, context
Expand Down
31 changes: 16 additions & 15 deletions lib/ldpath/tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ def initialize(delegate, test)
@test = test
end

def evaluate(program, uris, context)
return to_enum(:evaluate, program, uris, context) unless block_given?
def evaluate(program, uris, context, maintain_literals: false)
return to_enum(:evaluate, program, uris, context, maintain_literals: maintain_literals) unless block_given?

entries = delegate.evaluate program, uris, context
entries = delegate.evaluate program, uris, context, maintain_literals: maintain_literals
entries.select do |uri|
result = enum_wrap(test.evaluate(program, uri, context)).any? do |x|
result = enum_wrap(test.evaluate(program, uri, context, maintain_literals: maintain_literals)).any? do |x|
x
end
yield uri if result
Expand All @@ -26,7 +26,7 @@ def initialize(lang)
@lang = lang
end

def evaluate(_program, uri, _context)
def evaluate(_program, uri, _context, maintain_literals: false)
return unless uri.literal?

uri if (lang.to_s == "none" && !uri.has_language?) || uri.language.to_s == lang.to_s
Expand All @@ -39,7 +39,7 @@ def initialize(type)
@type = type
end

def evaluate(program, uri, _context)
def evaluate(program, uri, _context, maintain_literals: false)
return unless uri.literal?

uri if uri.has_datatype? && uri.datatype == type
Expand All @@ -53,8 +53,8 @@ def initialize(delegate)
@delegate = delegate
end

def evaluate(program, uri, context)
!enum_wrap(delegate.evaluate(program, uri, context)).any? { |x| x }
def evaluate(program, uri, context, maintain_literals: false)
!enum_wrap(delegate.evaluate(program, uri, context, maintain_literals: maintain_literals)).any? { |x| x }
end
end

Expand All @@ -66,8 +66,9 @@ def initialize(left, right)
@right = right
end

def evaluate(program, uri, context)
left.evaluate(program, uri, context).any? || right.evaluate(program, uri, context).any?
def evaluate(program, uri, context, maintain_literals: false)
left.evaluate(program, uri, context, maintain_literals: maintain_literals).any? ||
right.evaluate(program, uri, context, maintain_literals: maintain_literals).any?
end
end

Expand All @@ -79,9 +80,9 @@ def initialize(left, right)
@right = right
end

def evaluate(program, uri, context)
left.evaluate(program, uri, context).any? &&
right.evaluate(program, uri, context).any?
def evaluate(program, uri, context, maintain_literals: false)
left.evaluate(program, uri, context, maintain_literals: maintain_literals).any? &&
right.evaluate(program, uri, context, maintain_literals: maintain_literals).any?
end
end

Expand All @@ -93,8 +94,8 @@ def initialize(left, right)
@right = right
end

def evaluate(program, uri, context)
left.evaluate(program, uri, context).include?(right)
def evaluate(program, uri, context, maintain_literals: false)
left.evaluate(program, uri, context, maintain_literals: maintain_literals).include?(right)
end
end
end
54 changes: 52 additions & 2 deletions spec/ldpath_program_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
titles = dcterms:title | (dcterms:isPartOf / dcterms:title) | (^dcterms:isPartOf / dcterms:title) :: xsd:string ;
no_titles = dcterms:title & (dcterms:isPartOf / dcterms:title) & (^dcterms:isPartOf / dcterms:title) :: xsd:string ;
self = . :: xsd:string ;
wildcard = * ::xsd:string ;
str_wildcard = * ::xsd:string ;
uri_wildcard = * ::xsd:anyURI ;
child_title = ^dcterms:isPartOf / dcterms:title :: xsd:string ;
child_description_en = ^dcterms:isPartOf / dcterms:description[@en] :: xsd:string ;
recursive = (dcterms:isPartOf)* ;
Expand Down Expand Up @@ -57,7 +58,8 @@
expect(result["parent_title"]).to match_array ["Parent title", "Parent English!", "Parent French!"]
expect(result["parent_title_en"]).to match_array "Parent English!"
expect(result["self"]).to match_array(object)
expect(result["wildcard"]).to include "Hello, world!", parent
expect(result["str_wildcard"]).to include "Hello, world!", parent
expect(result["uri_wildcard"]).to include parent
expect(result["child_title"]).to match_array "Child title"
expect(result["titles"]).to match_array ["Hello, world!", "Parent title", "Child title", "Parent English!", "Parent French!"]
expect(result["no_titles"]).to be_empty
Expand All @@ -73,6 +75,54 @@
expect(result["is_test"]).to match_array(object)
expect(result["is_not_test"]).to be_empty
end

context "when requesting literals" do
let(:title) { RDF::Literal.new("Hello, world!") }
let(:parent_title) { RDF::Literal.new("Parent title") }
let(:child_title) { RDF::Literal.new("Child title") }
let(:en_description) { RDF::Literal.new("English!", language: "en") }
let(:fr_description) { RDF::Literal.new("French!", language: "fr") }
let(:en_parent_title) { RDF::Literal.new("Parent English!", language: "en") }
let(:fr_parent_title) { RDF::Literal.new("Parent French!", language: "fr") }

it "should return literals" do
graph << [object, RDF::Vocab::DC.title, title.canonicalize.object]
graph << [object, RDF::Vocab::DC.isPartOf, parent]
graph << [object, RDF::Vocab::DC.description, en_description]
graph << [object, RDF::Vocab::DC.description, fr_description]
graph << [object, RDF::URI.new("info:intProperty"), 1]
graph << [object, RDF::URI.new("info:intProperty"), "garbage"]
graph << [object, RDF::URI.new("info:numericProperty"), "1"]
graph << [parent, RDF::Vocab::DC.title, parent_title.canonicalize.object]
graph << [child, RDF::Vocab::DC.isPartOf, object]
graph << [child, RDF::Vocab::DC.title, child_title.canonicalize.object]
graph << [parent, RDF::Vocab::DC.title, en_parent_title]
graph << [parent, RDF::Vocab::DC.title, fr_parent_title]
graph << [parent, RDF::Vocab::DC.isPartOf, grandparent]

result = subject.evaluate object, context: graph, maintain_literals: true
expect(result["title"]).to match_array RDF::Literal.new("Hello, world!")
expect(result["parent_title"]).to match_array [parent_title, en_parent_title, fr_parent_title]
expect(result["parent_title_en"]).to match_array en_parent_title
expect(result["self"]).to match_array(object.to_s)
expect(result["str_wildcard"]).to include title, parent.to_s
expect(result["uri_wildcard"]).to include parent
expect(result["child_title"]).to match_array child_title
expect(result["titles"]).to match_array [title, parent_title, child_title, en_parent_title, fr_parent_title]
expect(result["no_titles"]).to be_empty
expect(result["recursive"]).to match_array [parent, grandparent]
expect(result["en_description"].first.to_s).to eq "English!"
expect(result["conditional"]).to match_array parent
expect(result["conditional_false"]).to be_empty
expect(result["int_value"]).to match_array RDF::Literal::Integer.new("1")
expect(result["numeric_value"]).to match_array RDF::Literal::Integer.new("1")
expect(result["escaped_string"]).to match_array '\"'
expect(result["and_test"]).to be_empty
expect(result["or_test"]).to match_array(object)
expect(result["is_test"]).to match_array(object)
expect(result["is_not_test"]).to be_empty
end
end
end

describe "functions" do
Expand Down

0 comments on commit 1ab3963

Please sign in to comment.