<NME> query.rb
<BEF> module SPARQL; class Client
  ##
  # A SPARQL query builder.
  # A SPARQL query builder.
  #
  # @example Iterating over all found solutions
  #   query.each_solution { |solution| puts solution.inspect }
  #
  class Query < RDF::Query
    ##
    # The form of the query.
    #
    # @return [:select, :ask, :construct, :describe]
    # @see    https://www.w3.org/TR/sparql11-query/#QueryForms
    attr_reader :form

    ##
    # @return [Hash{Symbol => Object}]
    attr_reader :options

    ##
    # Creates a boolean `ASK` query.
    #
    # @example ASK WHERE { ?s ?p ?o . }
    #   Query.ask.where([:s, :p, :o])
    #
    # @param  [Hash{Symbol => Object}] options (see {#initialize})
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#ask
    def self.ask(**options)
      self.new(:ask, **options)
    end

    ##
    # Creates a tuple `SELECT` query.
    #
    # @example `SELECT * WHERE { ?s ?p ?o . }`
    #   Query.select.where([:s, :p, :o])
    #
    # @example `SELECT ?s WHERE {?s ?p ?o .}`
    #   Query.select(:s).where([:s, :p, :o])
    #
    # @example `SELECT COUNT(?uri as ?c) WHERE {?uri a owl:Class}`
    #   Query.select(count: {uri: :c}).where([:uri, RDF.type, RDF::OWL.Class])
    #
    # @param  [Array<Symbol>]          variables
    # @return [Query]
    #
    # @overload self.select(*variables, **options)
    #   @param  [Array<Symbol>]          variables
    #   @param  [Hash{Symbol => Object}] options (see {#initialize})
    #   @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#select
    def self.select(*variables, **options)
      self.new(:select, **options).select(*variables)
    end

    ##
    # Creates a `DESCRIBE` query.
    #
    # @example DESCRIBE * WHERE { ?s ?p ?o . }
    #   Query.describe.where([:s, :p, :o])
    #
    # @param  [Array<Symbol, RDF::URI>] variables
    # @return [Query]
    #
    # @overload self.describe(*variables, **options)
    #   @param  [Array<Symbol, RDF::URI>] variables
    #   @param  [Hash{Symbol => Object}] options (see {#initialize})
    #   @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#describe
    def self.describe(*variables, **options)
      self.new(:describe, **options).describe(*variables)
    end

    ##
    # Creates a graph `CONSTRUCT` query.
    #
    # @example CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . }
    #   Query.construct([:s, :p, :o]).where([:s, :p, :o])
    #
    # @param  [Array<RDF::Query::Pattern, Array>] patterns
    # @return [Query]
    #
    # @overload self.construct(*variables, **options)
    #   @param  [Array<RDF::Query::Pattern, Array>] patterns
    #   @param  [Hash{Symbol => Object}] options (see {#initialize})
    #   @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#construct
    def self.construct(*patterns, **options)
      self.new(:construct, **options).construct(*patterns) # FIXME
    end

    ##
    # @param  [Symbol, #to_s]          form
    # @overload self.construct(*variables, **options)
    #   @param  [Symbol, #to_s]          form
    #   @param  [Hash{Symbol => Object}] options (see {Client#initialize})
    #   @option options [Hash{Symbol => Symbol}] :count
    #     Contents are symbols relating a variable described within the query,
    #     to the projected variable.
    #     
    # @yield  [query]
    # @yieldparam [Query]
    def initialize(form = :ask, **options, &block)
      @subqueries = []
      @form = form.respond_to?(:to_sym) ? form.to_sym : form.to_s.to_sym
      super([], **options, &block)
    end

    ##
    # @example ASK WHERE { ?s ?p ?o . }
    #   query.ask.where([:s, :p, :o])
    #
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#ask
    def ask
      @form = :ask
      self
    end

    ##
    # @example `SELECT * WHERE { ?s ?p ?o . }`
    #   query.select.where([:s, :p, :o])
    #
    # @example `SELECT ?s WHERE {?s ?p ?o .}`
    #   query.select(:s).where([:s, :p, :o])
    #
    # @example `SELECT COUNT(?uri as ?c) WHERE {?uri a owl:Class}`
    #   query.select(count: {uri: :c}).where([:uri, RDF.type, RDF::OWL.Class])
    #
    # @param  [Array<Symbol>, Hash{Symbol => RDF::Query::Variable}] variables
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#select
    def select(*variables)
      @values = if variables.length == 1 && variables.first.is_a?(Hash)
        variables.to_a
      else
        variables.map { |var| [var, RDF::Query::Variable.new(var)] }
      end
      self
    end

    ##
    # @example DESCRIBE * WHERE { ?s ?p ?o . }
    #   query.describe.where([:s, :p, :o])
    #
    # @param  [Array<Symbol>] variables
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#describe
    def describe(*variables)
      @values = variables.map { |var|
        [var, var.is_a?(RDF::URI) ? var : RDF::Query::Variable.new(var)]
      }
      self
    end

    ##
    # @example CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . }
    #   query.construct([:s, :p, :o]).where([:s, :p, :o])
    #
    # @param  [Array<RDF::Query::Pattern, Array>] patterns
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#construct
    def construct(*patterns)
      options[:template] = build_patterns(patterns)
      self
    end

    ##
    # @example SELECT * FROM <a> WHERE \{ ?s ?p ?o . \}
    #   query.select.from(RDF::URI.new(a)).where([:s, :p, :o])
    #
    # @param [RDF::URI] uri
    # @return [Query]
    # @see https://www.w3.org/TR/sparql11-query/#specifyingDataset
    def from(uri)
      options[:from] = uri
      self
    end

    ##
    # @example SELECT * WHERE { ?s ?p ?o . }
    #   query.select.where([:s, :p, :o])
    #   query.select.whether([:s, :p, :o])
    #
    # @example SELECT * WHERE { { SELECT * WHERE { ?s ?p ?o . } } . ?s ?p ?o . }
    #   subquery = query.select.where([:s, :p, :o])
    #   query.select.where([:s, :p, :o], subquery)
    #
    # @example SELECT * WHERE { { SELECT * WHERE { ?s ?p ?o . } } . ?s ?p ?o . }
    #   query.select.where([:s, :p, :o]) do |q|
    #     q.select.where([:s, :p, :o])
    #   end
    #
    # Block form can be used for chaining calls in addition to creating sub-select queries.
    #
    # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o
    #   query.select.where([:s, :p, :o]) do
    #     order(:o)
    #   end
    #
    # @param  [Array<RDF::Query::Pattern, Array>] patterns_queries
    #   splat of zero or more patterns followed by zero or more queries.
    # @yield [query]
    #   Yield form with or without argument; without an argument, evaluates within the query.
    # @yieldparam [SPARQL::Client::Query] query Actually a delegator to query. Methods other than `#select` are evaluated against `self`. For `#select`, a new Query is created, and the result added as a subquery.
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#GraphPattern
    def where(*patterns_queries, &block)
      subqueries, patterns = patterns_queries.partition {|pq| pq.is_a? SPARQL::Client::Query}
      @patterns += build_patterns(patterns)
      @subqueries += subqueries

      if block_given?
        decorated_query = WhereDecorator.new(self)
        case block.arity
          when 1 then block.call(decorated_query)
          else decorated_query.instance_eval(&block)
        end
      end
        when :select
          buffer << 'DISTINCT' if options[:distinct]
          buffer << 'REDUCED'  if options[:reduced]
          buffer << (variables.empty? ? '*' : variables.values.map(&:to_s).join(' '))
        when :construct
          buffer << '{'
          buffer += options[:template].map(&:to_s)
          buffer << '}'
      end

      buffer << 'WHERE {'
      buffer += patterns.map(&:to_s)
      if options[:filters]
        buffer += options[:filters].map { |filter| "FILTER(#{filter})" }
      end
    # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o ?p
    #   query.select.where([:s, :p, :o]).order_by(:o, :p)
    #
    # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ASC(?o) DESC(?p)
    #   query.select.where([:s, :p, :o]).order_by(o: :asc, p: :desc)
    #
    # @param  [Array<Symbol, String>] variables
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#modOrderBy
    def order(*variables)
      options[:order_by] = variables
      self
    end

    alias_method :order_by, :order

    ##
    # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ASC(?o)
    #   query.select.where([:s, :p, :o]).order.asc(:o)
    #   query.select.where([:s, :p, :o]).asc(:o)
    #
    # @param  [Array<Symbol, String>] var
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#modOrderBy
    def asc(var)
      (options[:order_by] ||= []) << {var => :asc}
    def inspect
      sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)
    end
  end
end; end
      self
    end

    alias_method :group_by, :group

    ##
    # @example SELECT DISTINCT ?s WHERE { ?s ?p ?o . }
    #   query.select(:s).distinct.where([:s, :p, :o])
    #
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#modDuplicates
    def distinct(state = true)
      options[:distinct] = state
      self
    end

    ##
    # @example SELECT REDUCED ?s WHERE { ?s ?p ?o . }
    #   query.select(:s).reduced.where([:s, :p, :o])
    #
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#modDuplicates
    def reduced(state = true)
      options[:reduced] = state
      self
    end

    ##
    # @example SELECT * WHERE { GRAPH ?g { ?s ?p ?o . } }
    #   query.select.graph(:g).where([:s, :p, :o])
    # @param  [RDF::Value] graph_uri_or_var
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#queryDataset
    def graph(graph_uri_or_var)
      options[:graph] = case graph_uri_or_var
        when Symbol then RDF::Query::Variable.new(graph_uri_or_var)
        when String then RDF::URI(graph_uri_or_var)
        when RDF::Value then graph_uri_or_var
        else raise ArgumentError
      end
      self
    end

    ##
    # @example SELECT * WHERE { ?s ?p ?o . } OFFSET 100
    #   query.select.where([:s, :p, :o]).offset(100)
    #
    # @param  [Integer, #to_i] start
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#modOffset
    def offset(start)
      slice(start, nil)
    end

    ##
    # @example SELECT * WHERE { ?s ?p ?o . } LIMIT 10
    #   query.select.where([:s, :p, :o]).limit(10)
    #
    # @param  [Integer, #to_i] length
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#modResultLimit
    def limit(length)
      slice(nil, length)
    end

    ##
    # @example SELECT * WHERE { ?s ?p ?o . } OFFSET 100 LIMIT 10
    #   query.select.where([:s, :p, :o]).slice(100, 10)
    #
    # @param  [Integer, #to_i] start
    # @param  [Integer, #to_i] length
    # @return [Query]
    def slice(start, length)
      options[:offset] = start.to_i if start
      options[:limit] = length.to_i if length
      self
    end

    ##
    # @overload prefix(prefix: uri)
    #   @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
    #     query.select.
    #       prefix(dc: RDF::URI("http://purl.org/dc/elements/1.1/")).
    #       prefix(foaf: RDF::URI("http://xmlns.com/foaf/0.1/")).
    #       where([:s, :p, :o])
    #
    #   @param [RDF::URI] uri
    #   @param [Symbol, String] prefix
    #   @return [Query]
    #
    # @overload prefix(string)
    #   @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
    #     query.select.
    #       prefix("dc: <http://purl.org/dc/elements/1.1/>").
    #       prefix("foaf: <http://xmlns.com/foaf/0.1/>").
    #       where([:s, :p, :o])
    #
    #   @param [string] string
    #   @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#prefNames
    def prefix(val)
      options[:prefixes] ||= []
      if val.kind_of? String
        options[:prefixes] << val
      elsif val.kind_of? Hash
        val.each do |k, v|
          options[:prefixes] << "#{k}: <#{v}>"
        end
      else
        raise ArgumentError, "prefix must be a kind of String or a Hash"
      end
      self
    end

    ##
    # @example SELECT * WHERE \{ ?s ?p ?o . OPTIONAL \{ ?s a ?o . ?s \<http://purl.org/dc/terms/abstract\> ?o . \} \}
    #   query.select.where([:s, :p, :o]).
    #     optional([:s, RDF.type, :o], [:s, RDF::Vocab::DC.abstract, :o])
    #
    # The block form can be used for adding filters:
    #
    # @example ASK WHERE { ?s ?p ?o . OPTIONAL { ?s ?p ?o . FILTER(regex(?s, 'Abiline, Texas'))} }
    #   query.ask.where([:s, :p, :o]).optional([:s, :p, :o]) do
    #     filter("regex(?s, 'Abiline, Texas')")
    #   end
    #
    # @param  [Array<RDF::Query::Pattern, Array>] patterns
    #   splat of zero or more patterns followed by zero or more queries.
    # @yield [query]
    #   Yield form with or without argument; without an argument, evaluates within the query.
    # @yieldparam [SPARQL::Client::Query] query used for creating filters on the optional patterns.
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#optionals
    def optional(*patterns, &block)
      (options[:optionals] ||= []) << build_patterns(patterns)

      if block_given?
        # Steal options[:filters]
        query_filters = options[:filters]
        options[:filters] = []
        case block.arity
          when 1 then block.call(self)
          else instance_eval(&block)
        end
        options[:optionals].last.concat(options[:filters])
        options[:filters] = query_filters
      end

      self
    end

    ##
    # @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title \}
    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).
    #     union([:book, RDF::Vocab::DC11.title, :title])
    #
    # @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\}
    #   query1 = SPARQL::Client::Query.select.
    #     where([:book, RDF::Vocab::DC11.title, :title]).
    #     filter("langmatches(?title, 'en')")
    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).union(query1)
    #
    # The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):
    #
    # @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\}
    #   query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title]).filter("langmatches(?title, 'en')")
    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).union do |q|
    #     q.select.
    #       where([:book, RDF::Vocab::DC11.title, :title]).
    #       filter("langmatches(?title, 'en')")
    #   end
    #
    # @param  [Array<RDF::Query::Pattern, Array>] patterns
    #   splat of zero or more patterns followed by zero or more queries.
    # @yield [query]
    #   Yield form with or without argument; without an argument, evaluates within the query.
    # @yieldparam [SPARQL::Client::Query] query used for adding select clauses.
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#optionals
    def union(*patterns, &block)
      options[:unions] ||= []

      if block_given?
        raise ArgumentError, "#union requires either arguments or a block, not both." unless patterns.empty?
        # Evaluate calls in a new query instance
        query = self.class.select
        case block.arity
          when 1 then block.call(query)
          else query.instance_eval(&block)
        end
        options[:unions] << query
      elsif patterns.all? {|p| p.is_a?(SPARQL::Client::Query)}
        # With argument form, all must be patterns or queries
        options[:unions] += patterns
      elsif patterns.all? {|p| p.is_a?(Array)}
        # With argument form, all must be patterns, or queries
        options[:unions] << self.class.select.where(*patterns)
      else
        raise ArgumentError, "#union arguments are triple patters or queries, not both."
      end

      self
    end

    ##
    # @example SELECT * WHERE \{ ?book dc:title ?title . MINUS \{ ?book dc11:title ?title \} \}
    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).
    #     minus([:book, RDF::Vocab::DC11.title, :title])
    #
    # @example SELECT * WHERE \{ ?book dc:title ?title MINUS \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN')) \} \}
    #   query1 = SPARQL::Client::Query.select.
    #     where([:book, RDF::Vocab::DC11.title, :title]).
    #     filter("langmatches(?title, 'en')")
    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).minus(query1)
    #
    # The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):
    #
    # @example SELECT * WHERE \{ ?book dc:title ?title MINUS \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\} \}
    #   query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title]).filter("langmatches(?title, 'en')")
    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).minus do |q|
    #     q.select.
    #       where([:book, RDF::Vocab::DC11.title, :title]).
    #       filter("langmatches(?title, 'en')")
    #   end
    #
    # @param  [Array<RDF::Query::Pattern, Array>] patterns
    #   splat of zero or more patterns followed by zero or more queries.
    # @yield [query]
    #   Yield form with or without argument; without an argument, evaluates within the query.
    # @yieldparam [SPARQL::Client::Query] query used for adding select clauses.
    # @return [Query]
    # @see    https://www.w3.org/TR/sparql11-query/#optionals
    def minus(*patterns, &block)
      options[:minuses] ||= []

      if block_given?
        raise ArgumentError, "#minus requires either arguments or a block, not both." unless patterns.empty?
        # Evaluate calls in a new query instance
        query = self.class.select
        case block.arity
          when 1 then block.call(query)
          else query.instance_eval(&block)
        end
        options[:minuses] << query
      elsif patterns.all? {|p| p.is_a?(SPARQL::Client::Query)}
        # With argument form, all must be patterns or queries
        options[:minuses] += patterns
      elsif patterns.all? {|p| p.is_a?(Array)}
        # With argument form, all must be patterns, or queries
        options[:minuses] << self.class.select.where(*patterns)
      else
        raise ArgumentError, "#minus arguments are triple patters or queries, not both."
      end

      self
    end

    ##
    # Specify inline data for a query
    #
    # @overload values
    #   Values returned from previous query.
    #
    #   @return [Array<Array(key, RDF::Value)>]
    #
    # @overload values(vars, *data)
    #   @example single variable with multiple values
    #     query.select
    #      .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title])
    #      .values(:title, "This title", "Another title")
    #
    #   @example multiple variables with multiple values
    #     query.select
    #      .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title],
    #             [:s, RDF.type, :type])
    #      .values([:type, :title],
    #              [RDF::URI('http://pcdm.org/models#Object'), "This title"],
    #              [RDF::URI('http://pcdm.org/models#Collection', 'Another title'])
    #
    #   @example multiple variables with UNDEF
    #     query.select
    #      .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title],
    #             [:s, RDF.type, :type])
    #      .values([:type, :title],
    #              [nil "This title"],
    #              [RDF::URI('http://pcdm.org/models#Collection', nil])
    #
    #   @param [Symbol, Array<Symbol>] vars
    #   @param [Array<RDF::Term, String, nil>] *data
    #   @return [Query]
    def values(*args)
      return @values if args.empty?
      vars, *data = *args
      vars = Array(vars).map {|var| RDF::Query::Variable.new(var)}
      if vars.length == 1
        # data may be a in array form or simple form
        if data.any? {|d| d.is_a?(Array)} && !data.all? {|d| d.is_a?(Array)}
          raise ArgumentError, "values data must all be in array form or all simple"
        end
        data = data.map {|d| Array(d)}
      end

      # Each data value must be an array with the same number of entries as vars
      unless data.all? {|d| d.is_a?(Array) && d.all? {|dd| dd.is_a?(RDF::Value) || dd.is_a?(String) || dd.nil?}}
        raise ArgumentError, "values data must each be an array of terms, strings, or nil"
      end

      # Turn strings into Literals
      data = data.map do |d|
        d.map do |nil_literal_or_term|
          case nil_literal_or_term
          when nil then nil
          when String then RDF::Literal(nil_literal_or_term)
          when RDF::Value then nil_literal_or_term
          else raise ArgumentError
          end
        end
      end
      options[:values] = [vars, *data]
      self
    end

    ##
    # @return expects_statements?
    def expects_statements?
      [:construct, :describe].include?(form)
    end

    ##
    # @private 
    def build_patterns(patterns)
      patterns.map {|pattern| RDF::Query::Pattern.from(pattern)}
    end

    ##
    # @example ASK WHERE { ?s ?p ?o . FILTER(regex(?s, 'Abiline, Texas')) }
    #   query.ask.where([:s, :p, :o]).filter("regex(?s, 'Abiline, Texas')")
    # @return [Query]
    def filter(string)
      ((options[:filters] ||= []) << Filter.new(string)) if string and not string.empty?
      self
    end

    ##
    # @return [Boolean]
    def true?
      case result
        when TrueClass, FalseClass then result
        when RDF::Literal::Boolean then result.true?
        when Enumerable then !result.empty?
        else false
      end
    end

    ##
    # @return [Boolean]
    def false?
      !true?
    end

    ##
    # @return [Enumerable<RDF::Query::Solution>]
    def solutions
      result
    end

    ##
    # @yield  [statement]
    # @yieldparam [RDF::Statement]
    # @return [Enumerator]
    def each_statement(&block)
      result.each_statement(&block)
    end

    # Enumerates over each matching query solution.
    #
    # @yield  [solution]
    # @yieldparam [RDF::Query::Solution] solution
    # @return [Enumerator]
    def each_solution(&block)
      @solutions = result
      super
    end

    ##
    # @return [Object]
    def result
      @result ||= execute
    end

    ##
    # @return [Object]
    def execute
      raise NotImplementedError
    end

    ##
    # Returns the string representation of this query.
    #
    # @return [String]
    def to_s
      buffer = [form.to_s.upcase]

      case form
        when :select, :describe
          only_count = values.empty? && options[:count]
          buffer << 'DISTINCT' if options[:distinct] and not only_count
          buffer << 'REDUCED'  if options[:reduced]
          buffer << ((values.empty? and not options[:count]) ? '*' : values.map { |v| SPARQL::Client.serialize_value(v[1]) }.join(' '))
          if options[:count]
            options[:count].each do |var, count|
              buffer << '( COUNT(' + (options[:distinct] ? 'DISTINCT ' : '') +
                (var.is_a?(String) ? var : "?#{var}") + ') AS ' + (count.is_a?(String) ? count : "?#{count}") + ' )'
            end
          end
        when :construct
          buffer << '{'
          buffer += SPARQL::Client.serialize_patterns(options[:template])
          buffer << '}'
      end

      buffer << "FROM #{SPARQL::Client.serialize_value(options[:from])}" if options[:from]

      unless patterns.empty? && form == :describe
        buffer += self.to_s_ggp.unshift('WHERE')
      end

      options.fetch(:unions, []).each do |query|
        buffer += query.to_s_ggp.unshift('UNION')
      end

      if options[:group_by]
        buffer << 'GROUP BY'
        buffer += options[:group_by].map { |var| var.is_a?(String) ? var : "?#{var}" }
      end

      if options[:order_by]
        buffer << 'ORDER BY'
        options[:order_by].map { |elem|
          case elem
            # .order_by({ var1: :asc, var2: :desc})
            when Hash
              elem.each { |key, val|
                # check provided values
                if !key.is_a?(Symbol)
                  raise ArgumentError, 'keys of hash argument must be a Symbol'
                elsif !val.is_a?(Symbol) || (val != :asc && val != :desc)
                  raise ArgumentError, 'values of hash argument must either be `:asc` or `:desc`'
                end
                buffer << "#{val == :asc ? 'ASC' : 'DESC'}(?#{key})"
              }
            # .order_by([:var1, :asc], [:var2, :desc])
            when Array
              # check provided values
              if elem.length != 2
                raise ArgumentError, 'array argument must specify two elements'
              elsif !elem[0].is_a?(Symbol)
                raise ArgumentError, '1st element of array argument must contain a Symbol'
              elsif !elem[1].is_a?(Symbol) || (elem[1] != :asc && elem[1] != :desc)
                raise ArgumentError, '2nd element of array argument must either be `:asc` or `:desc`'
              end
              buffer << "#{elem[1] == :asc ? 'ASC' : 'DESC'}(?#{elem[0]})"
            # .order_by(:var1, :var2)
            when Symbol
              buffer << "?#{elem}"
            # .order_by('ASC(?var1) DESC(?var2)')
            when String
              buffer << elem
            else
              raise ArgumentError, 'argument provided to `order()` must either be an Array, Symbol or String'
          end
        }
      end

      buffer << "OFFSET #{options[:offset]}" if options[:offset]
      buffer << "LIMIT #{options[:limit]}"   if options[:limit]
      options[:prefixes].reverse.each { |e| buffer.unshift("PREFIX #{e}") } if options[:prefixes]

      buffer.join(' ')
    end

    # Serialize a Group Graph Pattern
    # @private
    def to_s_ggp
      buffer = ["{"]

      if options[:graph]
        buffer << 'GRAPH ' + SPARQL::Client.serialize_value(options[:graph])
        buffer << '{'
      end

      @subqueries.each do |sq|
        buffer << "{ #{sq.to_s} } ."
      end

      buffer += SPARQL::Client.serialize_patterns(patterns)
      if options[:optionals]
        options[:optionals].each do |patterns|
          buffer << 'OPTIONAL {'
          buffer += SPARQL::Client.serialize_patterns(patterns)
          buffer << '}'
        end
      end
      if options[:filters]
        buffer += options[:filters].map(&:to_s)
      end
      if options[:values]
        vars = options[:values].first.map {|var| SPARQL::Client.serialize_value(var)}
        buffer << "VALUES (#{vars.join(' ')}) {"
        options[:values][1..-1].each do |data_block_value|
          buffer << '('
          buffer << data_block_value.map do |value|
            value.nil? ? 'UNDEF' : SPARQL::Client.serialize_value(value)
          end.join(' ')
          buffer << ')'
        end
        buffer << '}'
      end
      if options[:graph]
        buffer << '}' # GRAPH
      end

      options.fetch(:minuses, []).each do |query|
        buffer += query.to_s_ggp.unshift('MINUS')
      end

      buffer << '}'
      buffer
    end

    ##
    # Outputs a developer-friendly representation of this query to `stderr`.
    #
    # @return [void]
    def inspect!
      warn(inspect)
      self
    end

    ##
    # Returns a developer-friendly representation of this query.
    #
    # @return [String]
    def inspect
      sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)
    end

    # Allow Filters to be 
    class Filter < SPARQL::Client::QueryElement
      def initialize(*args)
        super
      end

      def to_s
        "FILTER(#{elements.join(' ')})"
      end
    end
  end
end

<MSG> Don't wrap literals in < > when serializing for a query

<DFF> @@ -1,3 +1,4 @@
+
 module SPARQL; class Client
   ##
   # A SPARQL query builder.
@@ -222,15 +223,19 @@ module SPARQL; class Client
         when :select
           buffer << 'DISTINCT' if options[:distinct]
           buffer << 'REDUCED'  if options[:reduced]
-          buffer << (variables.empty? ? '*' : variables.values.map(&:to_s).join(' '))
+          buffer << (variables.empty? ? '*' : variables.values.map {|v| serialize_value(v) }.join(' '))
         when :construct
           buffer << '{'
-          buffer += options[:template].map(&:to_s)
+          buffer += options[:template].map do |p|
+            p.to_a.map {|v| serialize_value(v) }.join(' ') + " . "
+          end
           buffer << '}'
       end
 
       buffer << 'WHERE {'
-      buffer += patterns.map(&:to_s)
+      buffer += patterns.map do |p|
+        p.to_a.map {|v| serialize_value(v) }.join(' ') + " . "
+      end
       if options[:filters]
         buffer += options[:filters].map { |filter| "FILTER(#{filter})" }
       end
@@ -263,5 +268,25 @@ module SPARQL; class Client
     def inspect
       sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)
     end
+
+    ##
+    # Serializes an RDF::Value into a format appropriate for select, construct, and where clauses
+    #
+    # @param [RDF::Value]
+    # @return [String]
+    # @private
+    def serialize_value(value)
+      # We don't want to use the NTriples serializer, which is ASCII, as SPARQL is utf-8
+      case
+        when value.is_a?(RDF::Query::Variable)
+          value.to_s
+        when value.variable?
+          value.to_s
+        when value.literal?
+          value.to_s
+        else
+          "<" + value.to_s + ">"
+      end
+    end
   end
 end; end

{'commit': 'diff --git a/lib/sparql/client/query.rb b/lib/sparql/client/query.rb\nindex 4fe6bcb..0339caf 100644\n--- a/lib/sparql/client/query.rb\n+++ b/lib/sparql/client/query.rb\n@@ -1,3 +1,4 @@\n+\n module SPARQL; class Client\n   ##\n   # A SPARQL query builder.\n@@ -222,15 +223,19 @@ module SPARQL; class Client\n         when :select\n           buffer << \'DISTINCT\' if options[:distinct]\n           buffer << \'REDUCED\'  if options[:reduced]\n-          buffer << (variables.empty? ? \'*\' : variables.values.map(&:to_s).join(\' \'))\n+          buffer << (variables.empty? ? \'*\' : variables.values.map {|v| serialize_value(v) }.join(\' \'))\n         when :construct\n           buffer << \'{\'\n-          buffer += options[:template].map(&:to_s)\n+          buffer += options[:template].map do |p|\n+            p.to_a.map {|v| serialize_value(v) }.join(\' \') + " . "\n+          end\n           buffer << \'}\'\n       end\n \n       buffer << \'WHERE {\'\n-      buffer += patterns.map(&:to_s)\n+      buffer += patterns.map do |p|\n+        p.to_a.map {|v| serialize_value(v) }.join(\' \') + " . "\n+      end\n       if options[:filters]\n         buffer += options[:filters].map { |filter| "FILTER(#{filter})" }\n       end\n@@ -263,5 +268,25 @@ module SPARQL; class Client\n     def inspect\n       sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)\n     end\n+\n+    ##\n+    # Serializes an RDF::Value into a format appropriate for select, construct, and where clauses\n+    #\n+    # @param [RDF::Value]\n+    # @return [String]\n+    # @private\n+    def serialize_value(value)\n+      # We don\'t want to use the NTriples serializer, which is ASCII, as SPARQL is utf-8\n+      case\n+        when value.is_a?(RDF::Query::Variable)\n+          value.to_s\n+        when value.variable?\n+          value.to_s\n+        when value.literal?\n+          value.to_s\n+        else\n+          "<" + value.to_s + ">"\n+      end\n+    end\n   end\n end; end\n', 'commit_subject': "Don't wrap literals in < > when serializing for a query", 'repo_name': 'ruby-rdf/sparql-client', 'committer': {'name': 'Ben Lavender', 'email': 'a35ed3ea9eb7a741dff06265164f771cda392b40@gmail.com', 'time_sec': '1271450756', 'tz_offset': '120', 'date': {'seconds': '1271450756'}}, 'commit_tree': '6e315211743169e7982726c387321a16997bcab8', 'commit_message': "Don't wrap literals in < > when serializing for a query\n", 'language_name': 'Ruby', 'license': 'unlicense', 'hunks': '@@ -1,3 +1,4 @@\n+\n module SPARQL; class Client\n   ##\n   # A SPARQL query builder.\n@@ -222,15 +223,19 @@ module SPARQL; class Client\n         when :select\n           buffer << \'DISTINCT\' if options[:distinct]\n           buffer << \'REDUCED\'  if options[:reduced]\n-          buffer << (variables.empty? ? \'*\' : variables.values.map(&:to_s).join(\' \'))\n+          buffer << (variables.empty? ? \'*\' : variables.values.map {|v| serialize_value(v) }.join(\' \'))\n         when :construct\n           buffer << \'{\'\n-          buffer += options[:template].map(&:to_s)\n+          buffer += options[:template].map do |p|\n+            p.to_a.map {|v| serialize_value(v) }.join(\' \') + " . "\n+          end\n           buffer << \'}\'\n       end\n \n       buffer << \'WHERE {\'\n-      buffer += patterns.map(&:to_s)\n+      buffer += patterns.map do |p|\n+        p.to_a.map {|v| serialize_value(v) }.join(\' \') + " . "\n+      end\n       if options[:filters]\n         buffer += options[:filters].map { |filter| "FILTER(#{filter})" }\n       end\n@@ -263,5 +268,25 @@ module SPARQL; class Client\n     def inspect\n       sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)\n     end\n+\n+    ##\n+    # Serializes an RDF::Value into a format appropriate for select, construct, and where clauses\n+    #\n+    # @param [RDF::Value]\n+    # @return [String]\n+    # @private\n+    def serialize_value(value)\n+      # We don\'t want to use the NTriples serializer, which is ASCII, as SPARQL is utf-8\n+      case\n+        when value.is_a?(RDF::Query::Variable)\n+          value.to_s\n+        when value.variable?\n+          value.to_s\n+        when value.literal?\n+          value.to_s\n+        else\n+          "<" + value.to_s + ">"\n+      end\n+    end\n   end\n end; end\n', 'addition_count': 28, 'deletion_count': 3, 'src_file': 'a/lib/sparql/client/query.rb', 'tgt_file': 'b/lib/sparql/client/query.rb', 'file_extension': '.rb', 'before_file': 'module SPARQL; class Client\n  ##\n  # A SPARQL query builder.\n  # A SPARQL query builder.\n  #\n  # @example Iterating over all found solutions\n  #   query.each_solution { |solution| puts solution.inspect }\n  #\n  class Query < RDF::Query\n    ##\n    # The form of the query.\n    #\n    # @return [:select, :ask, :construct, :describe]\n    # @see    https://www.w3.org/TR/sparql11-query/#QueryForms\n    attr_reader :form\n\n    ##\n    # @return [Hash{Symbol => Object}]\n    attr_reader :options\n\n    ##\n    # Creates a boolean `ASK` query.\n    #\n    # @example ASK WHERE { ?s ?p ?o . }\n    #   Query.ask.where([:s, :p, :o])\n    #\n    # @param  [Hash{Symbol => Object}] options (see {#initialize})\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#ask\n    def self.ask(**options)\n      self.new(:ask, **options)\n    end\n\n    ##\n    # Creates a tuple `SELECT` query.\n    #\n    # @example `SELECT * WHERE { ?s ?p ?o . }`\n    #   Query.select.where([:s, :p, :o])\n    #\n    # @example `SELECT ?s WHERE {?s ?p ?o .}`\n    #   Query.select(:s).where([:s, :p, :o])\n    #\n    # @example `SELECT COUNT(?uri as ?c) WHERE {?uri a owl:Class}`\n    #   Query.select(count: {uri: :c}).where([:uri, RDF.type, RDF::OWL.Class])\n    #\n    # @param  [Array<Symbol>]          variables\n    # @return [Query]\n    #\n    # @overload self.select(*variables, **options)\n    #   @param  [Array<Symbol>]          variables\n    #   @param  [Hash{Symbol => Object}] options (see {#initialize})\n    #   @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#select\n    def self.select(*variables, **options)\n      self.new(:select, **options).select(*variables)\n    end\n\n    ##\n    # Creates a `DESCRIBE` query.\n    #\n    # @example DESCRIBE * WHERE { ?s ?p ?o . }\n    #   Query.describe.where([:s, :p, :o])\n    #\n    # @param  [Array<Symbol, RDF::URI>] variables\n    # @return [Query]\n    #\n    # @overload self.describe(*variables, **options)\n    #   @param  [Array<Symbol, RDF::URI>] variables\n    #   @param  [Hash{Symbol => Object}] options (see {#initialize})\n    #   @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#describe\n    def self.describe(*variables, **options)\n      self.new(:describe, **options).describe(*variables)\n    end\n\n    ##\n    # Creates a graph `CONSTRUCT` query.\n    #\n    # @example CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . }\n    #   Query.construct([:s, :p, :o]).where([:s, :p, :o])\n    #\n    # @param  [Array<RDF::Query::Pattern, Array>] patterns\n    # @return [Query]\n    #\n    # @overload self.construct(*variables, **options)\n    #   @param  [Array<RDF::Query::Pattern, Array>] patterns\n    #   @param  [Hash{Symbol => Object}] options (see {#initialize})\n    #   @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#construct\n    def self.construct(*patterns, **options)\n      self.new(:construct, **options).construct(*patterns) # FIXME\n    end\n\n    ##\n    # @param  [Symbol, #to_s]          form\n    # @overload self.construct(*variables, **options)\n    #   @param  [Symbol, #to_s]          form\n    #   @param  [Hash{Symbol => Object}] options (see {Client#initialize})\n    #   @option options [Hash{Symbol => Symbol}] :count\n    #     Contents are symbols relating a variable described within the query,\n    #     to the projected variable.\n    #     \n    # @yield  [query]\n    # @yieldparam [Query]\n    def initialize(form = :ask, **options, &block)\n      @subqueries = []\n      @form = form.respond_to?(:to_sym) ? form.to_sym : form.to_s.to_sym\n      super([], **options, &block)\n    end\n\n    ##\n    # @example ASK WHERE { ?s ?p ?o . }\n    #   query.ask.where([:s, :p, :o])\n    #\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#ask\n    def ask\n      @form = :ask\n      self\n    end\n\n    ##\n    # @example `SELECT * WHERE { ?s ?p ?o . }`\n    #   query.select.where([:s, :p, :o])\n    #\n    # @example `SELECT ?s WHERE {?s ?p ?o .}`\n    #   query.select(:s).where([:s, :p, :o])\n    #\n    # @example `SELECT COUNT(?uri as ?c) WHERE {?uri a owl:Class}`\n    #   query.select(count: {uri: :c}).where([:uri, RDF.type, RDF::OWL.Class])\n    #\n    # @param  [Array<Symbol>, Hash{Symbol => RDF::Query::Variable}] variables\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#select\n    def select(*variables)\n      @values = if variables.length == 1 && variables.first.is_a?(Hash)\n        variables.to_a\n      else\n        variables.map { |var| [var, RDF::Query::Variable.new(var)] }\n      end\n      self\n    end\n\n    ##\n    # @example DESCRIBE * WHERE { ?s ?p ?o . }\n    #   query.describe.where([:s, :p, :o])\n    #\n    # @param  [Array<Symbol>] variables\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#describe\n    def describe(*variables)\n      @values = variables.map { |var|\n        [var, var.is_a?(RDF::URI) ? var : RDF::Query::Variable.new(var)]\n      }\n      self\n    end\n\n    ##\n    # @example CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . }\n    #   query.construct([:s, :p, :o]).where([:s, :p, :o])\n    #\n    # @param  [Array<RDF::Query::Pattern, Array>] patterns\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#construct\n    def construct(*patterns)\n      options[:template] = build_patterns(patterns)\n      self\n    end\n\n    ##\n    # @example SELECT * FROM <a> WHERE \\{ ?s ?p ?o . \\}\n    #   query.select.from(RDF::URI.new(a)).where([:s, :p, :o])\n    #\n    # @param [RDF::URI] uri\n    # @return [Query]\n    # @see https://www.w3.org/TR/sparql11-query/#specifyingDataset\n    def from(uri)\n      options[:from] = uri\n      self\n    end\n\n    ##\n    # @example SELECT * WHERE { ?s ?p ?o . }\n    #   query.select.where([:s, :p, :o])\n    #   query.select.whether([:s, :p, :o])\n    #\n    # @example SELECT * WHERE { { SELECT * WHERE { ?s ?p ?o . } } . ?s ?p ?o . }\n    #   subquery = query.select.where([:s, :p, :o])\n    #   query.select.where([:s, :p, :o], subquery)\n    #\n    # @example SELECT * WHERE { { SELECT * WHERE { ?s ?p ?o . } } . ?s ?p ?o . }\n    #   query.select.where([:s, :p, :o]) do |q|\n    #     q.select.where([:s, :p, :o])\n    #   end\n    #\n    # Block form can be used for chaining calls in addition to creating sub-select queries.\n    #\n    # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o\n    #   query.select.where([:s, :p, :o]) do\n    #     order(:o)\n    #   end\n    #\n    # @param  [Array<RDF::Query::Pattern, Array>] patterns_queries\n    #   splat of zero or more patterns followed by zero or more queries.\n    # @yield [query]\n    #   Yield form with or without argument; without an argument, evaluates within the query.\n    # @yieldparam [SPARQL::Client::Query] query Actually a delegator to query. Methods other than `#select` are evaluated against `self`. For `#select`, a new Query is created, and the result added as a subquery.\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#GraphPattern\n    def where(*patterns_queries, &block)\n      subqueries, patterns = patterns_queries.partition {|pq| pq.is_a? SPARQL::Client::Query}\n      @patterns += build_patterns(patterns)\n      @subqueries += subqueries\n\n      if block_given?\n        decorated_query = WhereDecorator.new(self)\n        case block.arity\n          when 1 then block.call(decorated_query)\n          else decorated_query.instance_eval(&block)\n        end\n      end\n        when :select\n          buffer << \'DISTINCT\' if options[:distinct]\n          buffer << \'REDUCED\'  if options[:reduced]\n          buffer << (variables.empty? ? \'*\' : variables.values.map(&:to_s).join(\' \'))\n        when :construct\n          buffer << \'{\'\n          buffer += options[:template].map(&:to_s)\n          buffer << \'}\'\n      end\n\n      buffer << \'WHERE {\'\n      buffer += patterns.map(&:to_s)\n      if options[:filters]\n        buffer += options[:filters].map { |filter| "FILTER(#{filter})" }\n      end\n    # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o ?p\n    #   query.select.where([:s, :p, :o]).order_by(:o, :p)\n    #\n    # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ASC(?o) DESC(?p)\n    #   query.select.where([:s, :p, :o]).order_by(o: :asc, p: :desc)\n    #\n    # @param  [Array<Symbol, String>] variables\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#modOrderBy\n    def order(*variables)\n      options[:order_by] = variables\n      self\n    end\n\n    alias_method :order_by, :order\n\n    ##\n    # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ASC(?o)\n    #   query.select.where([:s, :p, :o]).order.asc(:o)\n    #   query.select.where([:s, :p, :o]).asc(:o)\n    #\n    # @param  [Array<Symbol, String>] var\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#modOrderBy\n    def asc(var)\n      (options[:order_by] ||= []) << {var => :asc}\n    def inspect\n      sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)\n    end\n  end\nend; end\n      self\n    end\n\n    alias_method :group_by, :group\n\n    ##\n    # @example SELECT DISTINCT ?s WHERE { ?s ?p ?o . }\n    #   query.select(:s).distinct.where([:s, :p, :o])\n    #\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#modDuplicates\n    def distinct(state = true)\n      options[:distinct] = state\n      self\n    end\n\n    ##\n    # @example SELECT REDUCED ?s WHERE { ?s ?p ?o . }\n    #   query.select(:s).reduced.where([:s, :p, :o])\n    #\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#modDuplicates\n    def reduced(state = true)\n      options[:reduced] = state\n      self\n    end\n\n    ##\n    # @example SELECT * WHERE { GRAPH ?g { ?s ?p ?o . } }\n    #   query.select.graph(:g).where([:s, :p, :o])\n    # @param  [RDF::Value] graph_uri_or_var\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#queryDataset\n    def graph(graph_uri_or_var)\n      options[:graph] = case graph_uri_or_var\n        when Symbol then RDF::Query::Variable.new(graph_uri_or_var)\n        when String then RDF::URI(graph_uri_or_var)\n        when RDF::Value then graph_uri_or_var\n        else raise ArgumentError\n      end\n      self\n    end\n\n    ##\n    # @example SELECT * WHERE { ?s ?p ?o . } OFFSET 100\n    #   query.select.where([:s, :p, :o]).offset(100)\n    #\n    # @param  [Integer, #to_i] start\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#modOffset\n    def offset(start)\n      slice(start, nil)\n    end\n\n    ##\n    # @example SELECT * WHERE { ?s ?p ?o . } LIMIT 10\n    #   query.select.where([:s, :p, :o]).limit(10)\n    #\n    # @param  [Integer, #to_i] length\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#modResultLimit\n    def limit(length)\n      slice(nil, length)\n    end\n\n    ##\n    # @example SELECT * WHERE { ?s ?p ?o . } OFFSET 100 LIMIT 10\n    #   query.select.where([:s, :p, :o]).slice(100, 10)\n    #\n    # @param  [Integer, #to_i] start\n    # @param  [Integer, #to_i] length\n    # @return [Query]\n    def slice(start, length)\n      options[:offset] = start.to_i if start\n      options[:limit] = length.to_i if length\n      self\n    end\n\n    ##\n    # @overload prefix(prefix: uri)\n    #   @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \\{ ?s ?p ?o . \\}\n    #     query.select.\n    #       prefix(dc: RDF::URI("http://purl.org/dc/elements/1.1/")).\n    #       prefix(foaf: RDF::URI("http://xmlns.com/foaf/0.1/")).\n    #       where([:s, :p, :o])\n    #\n    #   @param [RDF::URI] uri\n    #   @param [Symbol, String] prefix\n    #   @return [Query]\n    #\n    # @overload prefix(string)\n    #   @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \\{ ?s ?p ?o . \\}\n    #     query.select.\n    #       prefix("dc: <http://purl.org/dc/elements/1.1/>").\n    #       prefix("foaf: <http://xmlns.com/foaf/0.1/>").\n    #       where([:s, :p, :o])\n    #\n    #   @param [string] string\n    #   @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#prefNames\n    def prefix(val)\n      options[:prefixes] ||= []\n      if val.kind_of? String\n        options[:prefixes] << val\n      elsif val.kind_of? Hash\n        val.each do |k, v|\n          options[:prefixes] << "#{k}: <#{v}>"\n        end\n      else\n        raise ArgumentError, "prefix must be a kind of String or a Hash"\n      end\n      self\n    end\n\n    ##\n    # @example SELECT * WHERE \\{ ?s ?p ?o . OPTIONAL \\{ ?s a ?o . ?s \\<http://purl.org/dc/terms/abstract\\> ?o . \\} \\}\n    #   query.select.where([:s, :p, :o]).\n    #     optional([:s, RDF.type, :o], [:s, RDF::Vocab::DC.abstract, :o])\n    #\n    # The block form can be used for adding filters:\n    #\n    # @example ASK WHERE { ?s ?p ?o . OPTIONAL { ?s ?p ?o . FILTER(regex(?s, \'Abiline, Texas\'))} }\n    #   query.ask.where([:s, :p, :o]).optional([:s, :p, :o]) do\n    #     filter("regex(?s, \'Abiline, Texas\')")\n    #   end\n    #\n    # @param  [Array<RDF::Query::Pattern, Array>] patterns\n    #   splat of zero or more patterns followed by zero or more queries.\n    # @yield [query]\n    #   Yield form with or without argument; without an argument, evaluates within the query.\n    # @yieldparam [SPARQL::Client::Query] query used for creating filters on the optional patterns.\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#optionals\n    def optional(*patterns, &block)\n      (options[:optionals] ||= []) << build_patterns(patterns)\n\n      if block_given?\n        # Steal options[:filters]\n        query_filters = options[:filters]\n        options[:filters] = []\n        case block.arity\n          when 1 then block.call(self)\n          else instance_eval(&block)\n        end\n        options[:optionals].last.concat(options[:filters])\n        options[:filters] = query_filters\n      end\n\n      self\n    end\n\n    ##\n    # @example SELECT * WHERE \\{ ?book dc:title ?title \\} UNION \\{ ?book dc11:title ?title \\}\n    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).\n    #     union([:book, RDF::Vocab::DC11.title, :title])\n    #\n    # @example SELECT * WHERE \\{ ?book dc:title ?title \\} UNION \\{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), \'EN\'))\\}\n    #   query1 = SPARQL::Client::Query.select.\n    #     where([:book, RDF::Vocab::DC11.title, :title]).\n    #     filter("langmatches(?title, \'en\')")\n    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).union(query1)\n    #\n    # The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):\n    #\n    # @example SELECT * WHERE \\{ ?book dc:title ?title \\} UNION \\{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), \'EN\'))\\}\n    #   query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title]).filter("langmatches(?title, \'en\')")\n    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).union do |q|\n    #     q.select.\n    #       where([:book, RDF::Vocab::DC11.title, :title]).\n    #       filter("langmatches(?title, \'en\')")\n    #   end\n    #\n    # @param  [Array<RDF::Query::Pattern, Array>] patterns\n    #   splat of zero or more patterns followed by zero or more queries.\n    # @yield [query]\n    #   Yield form with or without argument; without an argument, evaluates within the query.\n    # @yieldparam [SPARQL::Client::Query] query used for adding select clauses.\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#optionals\n    def union(*patterns, &block)\n      options[:unions] ||= []\n\n      if block_given?\n        raise ArgumentError, "#union requires either arguments or a block, not both." unless patterns.empty?\n        # Evaluate calls in a new query instance\n        query = self.class.select\n        case block.arity\n          when 1 then block.call(query)\n          else query.instance_eval(&block)\n        end\n        options[:unions] << query\n      elsif patterns.all? {|p| p.is_a?(SPARQL::Client::Query)}\n        # With argument form, all must be patterns or queries\n        options[:unions] += patterns\n      elsif patterns.all? {|p| p.is_a?(Array)}\n        # With argument form, all must be patterns, or queries\n        options[:unions] << self.class.select.where(*patterns)\n      else\n        raise ArgumentError, "#union arguments are triple patters or queries, not both."\n      end\n\n      self\n    end\n\n    ##\n    # @example SELECT * WHERE \\{ ?book dc:title ?title . MINUS \\{ ?book dc11:title ?title \\} \\}\n    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).\n    #     minus([:book, RDF::Vocab::DC11.title, :title])\n    #\n    # @example SELECT * WHERE \\{ ?book dc:title ?title MINUS \\{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), \'EN\')) \\} \\}\n    #   query1 = SPARQL::Client::Query.select.\n    #     where([:book, RDF::Vocab::DC11.title, :title]).\n    #     filter("langmatches(?title, \'en\')")\n    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).minus(query1)\n    #\n    # The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):\n    #\n    # @example SELECT * WHERE \\{ ?book dc:title ?title MINUS \\{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), \'EN\'))\\} \\}\n    #   query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title]).filter("langmatches(?title, \'en\')")\n    #   query.select.where([:book, RDF::Vocab::DC.title, :title]).minus do |q|\n    #     q.select.\n    #       where([:book, RDF::Vocab::DC11.title, :title]).\n    #       filter("langmatches(?title, \'en\')")\n    #   end\n    #\n    # @param  [Array<RDF::Query::Pattern, Array>] patterns\n    #   splat of zero or more patterns followed by zero or more queries.\n    # @yield [query]\n    #   Yield form with or without argument; without an argument, evaluates within the query.\n    # @yieldparam [SPARQL::Client::Query] query used for adding select clauses.\n    # @return [Query]\n    # @see    https://www.w3.org/TR/sparql11-query/#optionals\n    def minus(*patterns, &block)\n      options[:minuses] ||= []\n\n      if block_given?\n        raise ArgumentError, "#minus requires either arguments or a block, not both." unless patterns.empty?\n        # Evaluate calls in a new query instance\n        query = self.class.select\n        case block.arity\n          when 1 then block.call(query)\n          else query.instance_eval(&block)\n        end\n        options[:minuses] << query\n      elsif patterns.all? {|p| p.is_a?(SPARQL::Client::Query)}\n        # With argument form, all must be patterns or queries\n        options[:minuses] += patterns\n      elsif patterns.all? {|p| p.is_a?(Array)}\n        # With argument form, all must be patterns, or queries\n        options[:minuses] << self.class.select.where(*patterns)\n      else\n        raise ArgumentError, "#minus arguments are triple patters or queries, not both."\n      end\n\n      self\n    end\n\n    ##\n    # Specify inline data for a query\n    #\n    # @overload values\n    #   Values returned from previous query.\n    #\n    #   @return [Array<Array(key, RDF::Value)>]\n    #\n    # @overload values(vars, *data)\n    #   @example single variable with multiple values\n    #     query.select\n    #      .where([:s, RDF::URI(\'http://purl.org/dc/terms/title\'), :title])\n    #      .values(:title, "This title", "Another title")\n    #\n    #   @example multiple variables with multiple values\n    #     query.select\n    #      .where([:s, RDF::URI(\'http://purl.org/dc/terms/title\'), :title],\n    #             [:s, RDF.type, :type])\n    #      .values([:type, :title],\n    #              [RDF::URI(\'http://pcdm.org/models#Object\'), "This title"],\n    #              [RDF::URI(\'http://pcdm.org/models#Collection\', \'Another title\'])\n    #\n    #   @example multiple variables with UNDEF\n    #     query.select\n    #      .where([:s, RDF::URI(\'http://purl.org/dc/terms/title\'), :title],\n    #             [:s, RDF.type, :type])\n    #      .values([:type, :title],\n    #              [nil "This title"],\n    #              [RDF::URI(\'http://pcdm.org/models#Collection\', nil])\n    #\n    #   @param [Symbol, Array<Symbol>] vars\n    #   @param [Array<RDF::Term, String, nil>] *data\n    #   @return [Query]\n    def values(*args)\n      return @values if args.empty?\n      vars, *data = *args\n      vars = Array(vars).map {|var| RDF::Query::Variable.new(var)}\n      if vars.length == 1\n        # data may be a in array form or simple form\n        if data.any? {|d| d.is_a?(Array)} && !data.all? {|d| d.is_a?(Array)}\n          raise ArgumentError, "values data must all be in array form or all simple"\n        end\n        data = data.map {|d| Array(d)}\n      end\n\n      # Each data value must be an array with the same number of entries as vars\n      unless data.all? {|d| d.is_a?(Array) && d.all? {|dd| dd.is_a?(RDF::Value) || dd.is_a?(String) || dd.nil?}}\n        raise ArgumentError, "values data must each be an array of terms, strings, or nil"\n      end\n\n      # Turn strings into Literals\n      data = data.map do |d|\n        d.map do |nil_literal_or_term|\n          case nil_literal_or_term\n          when nil then nil\n          when String then RDF::Literal(nil_literal_or_term)\n          when RDF::Value then nil_literal_or_term\n          else raise ArgumentError\n          end\n        end\n      end\n      options[:values] = [vars, *data]\n      self\n    end\n\n    ##\n    # @return expects_statements?\n    def expects_statements?\n      [:construct, :describe].include?(form)\n    end\n\n    ##\n    # @private \n    def build_patterns(patterns)\n      patterns.map {|pattern| RDF::Query::Pattern.from(pattern)}\n    end\n\n    ##\n    # @example ASK WHERE { ?s ?p ?o . FILTER(regex(?s, \'Abiline, Texas\')) }\n    #   query.ask.where([:s, :p, :o]).filter("regex(?s, \'Abiline, Texas\')")\n    # @return [Query]\n    def filter(string)\n      ((options[:filters] ||= []) << Filter.new(string)) if string and not string.empty?\n      self\n    end\n\n    ##\n    # @return [Boolean]\n    def true?\n      case result\n        when TrueClass, FalseClass then result\n        when RDF::Literal::Boolean then result.true?\n        when Enumerable then !result.empty?\n        else false\n      end\n    end\n\n    ##\n    # @return [Boolean]\n    def false?\n      !true?\n    end\n\n    ##\n    # @return [Enumerable<RDF::Query::Solution>]\n    def solutions\n      result\n    end\n\n    ##\n    # @yield  [statement]\n    # @yieldparam [RDF::Statement]\n    # @return [Enumerator]\n    def each_statement(&block)\n      result.each_statement(&block)\n    end\n\n    # Enumerates over each matching query solution.\n    #\n    # @yield  [solution]\n    # @yieldparam [RDF::Query::Solution] solution\n    # @return [Enumerator]\n    def each_solution(&block)\n      @solutions = result\n      super\n    end\n\n    ##\n    # @return [Object]\n    def result\n      @result ||= execute\n    end\n\n    ##\n    # @return [Object]\n    def execute\n      raise NotImplementedError\n    end\n\n    ##\n    # Returns the string representation of this query.\n    #\n    # @return [String]\n    def to_s\n      buffer = [form.to_s.upcase]\n\n      case form\n        when :select, :describe\n          only_count = values.empty? && options[:count]\n          buffer << \'DISTINCT\' if options[:distinct] and not only_count\n          buffer << \'REDUCED\'  if options[:reduced]\n          buffer << ((values.empty? and not options[:count]) ? \'*\' : values.map { |v| SPARQL::Client.serialize_value(v[1]) }.join(\' \'))\n          if options[:count]\n            options[:count].each do |var, count|\n              buffer << \'( COUNT(\' + (options[:distinct] ? \'DISTINCT \' : \'\') +\n                (var.is_a?(String) ? var : "?#{var}") + \') AS \' + (count.is_a?(String) ? count : "?#{count}") + \' )\'\n            end\n          end\n        when :construct\n          buffer << \'{\'\n          buffer += SPARQL::Client.serialize_patterns(options[:template])\n          buffer << \'}\'\n      end\n\n      buffer << "FROM #{SPARQL::Client.serialize_value(options[:from])}" if options[:from]\n\n      unless patterns.empty? && form == :describe\n        buffer += self.to_s_ggp.unshift(\'WHERE\')\n      end\n\n      options.fetch(:unions, []).each do |query|\n        buffer += query.to_s_ggp.unshift(\'UNION\')\n      end\n\n      if options[:group_by]\n        buffer << \'GROUP BY\'\n        buffer += options[:group_by].map { |var| var.is_a?(String) ? var : "?#{var}" }\n      end\n\n      if options[:order_by]\n        buffer << \'ORDER BY\'\n        options[:order_by].map { |elem|\n          case elem\n            # .order_by({ var1: :asc, var2: :desc})\n            when Hash\n              elem.each { |key, val|\n                # check provided values\n                if !key.is_a?(Symbol)\n                  raise ArgumentError, \'keys of hash argument must be a Symbol\'\n                elsif !val.is_a?(Symbol) || (val != :asc && val != :desc)\n                  raise ArgumentError, \'values of hash argument must either be `:asc` or `:desc`\'\n                end\n                buffer << "#{val == :asc ? \'ASC\' : \'DESC\'}(?#{key})"\n              }\n            # .order_by([:var1, :asc], [:var2, :desc])\n            when Array\n              # check provided values\n              if elem.length != 2\n                raise ArgumentError, \'array argument must specify two elements\'\n              elsif !elem[0].is_a?(Symbol)\n                raise ArgumentError, \'1st element of array argument must contain a Symbol\'\n              elsif !elem[1].is_a?(Symbol) || (elem[1] != :asc && elem[1] != :desc)\n                raise ArgumentError, \'2nd element of array argument must either be `:asc` or `:desc`\'\n              end\n              buffer << "#{elem[1] == :asc ? \'ASC\' : \'DESC\'}(?#{elem[0]})"\n            # .order_by(:var1, :var2)\n            when Symbol\n              buffer << "?#{elem}"\n            # .order_by(\'ASC(?var1) DESC(?var2)\')\n            when String\n              buffer << elem\n            else\n              raise ArgumentError, \'argument provided to `order()` must either be an Array, Symbol or String\'\n          end\n        }\n      end\n\n      buffer << "OFFSET #{options[:offset]}" if options[:offset]\n      buffer << "LIMIT #{options[:limit]}"   if options[:limit]\n      options[:prefixes].reverse.each { |e| buffer.unshift("PREFIX #{e}") } if options[:prefixes]\n\n      buffer.join(\' \')\n    end\n\n    # Serialize a Group Graph Pattern\n    # @private\n    def to_s_ggp\n      buffer = ["{"]\n\n      if options[:graph]\n        buffer << \'GRAPH \' + SPARQL::Client.serialize_value(options[:graph])\n        buffer << \'{\'\n      end\n\n      @subqueries.each do |sq|\n        buffer << "{ #{sq.to_s} } ."\n      end\n\n      buffer += SPARQL::Client.serialize_patterns(patterns)\n      if options[:optionals]\n        options[:optionals].each do |patterns|\n          buffer << \'OPTIONAL {\'\n          buffer += SPARQL::Client.serialize_patterns(patterns)\n          buffer << \'}\'\n        end\n      end\n      if options[:filters]\n        buffer += options[:filters].map(&:to_s)\n      end\n      if options[:values]\n        vars = options[:values].first.map {|var| SPARQL::Client.serialize_value(var)}\n        buffer << "VALUES (#{vars.join(\' \')}) {"\n        options[:values][1..-1].each do |data_block_value|\n          buffer << \'(\'\n          buffer << data_block_value.map do |value|\n            value.nil? ? \'UNDEF\' : SPARQL::Client.serialize_value(value)\n          end.join(\' \')\n          buffer << \')\'\n        end\n        buffer << \'}\'\n      end\n      if options[:graph]\n        buffer << \'}\' # GRAPH\n      end\n\n      options.fetch(:minuses, []).each do |query|\n        buffer += query.to_s_ggp.unshift(\'MINUS\')\n      end\n\n      buffer << \'}\'\n      buffer\n    end\n\n    ##\n    # Outputs a developer-friendly representation of this query to `stderr`.\n    #\n    # @return [void]\n    def inspect!\n      warn(inspect)\n      self\n    end\n\n    ##\n    # Returns a developer-friendly representation of this query.\n    #\n    # @return [String]\n    def inspect\n      sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)\n    end\n\n    # Allow Filters to be \n    class Filter < SPARQL::Client::QueryElement\n      def initialize(*args)\n        super\n      end\n\n      def to_s\n        "FILTER(#{elements.join(\' \')})"\n      end\n    end\n  end\nend\n', 'message': "Don't wrap literals in < > when serializing for a query\n", 'author_name': 'Ben Lavender', 'lang': 'rb'}