active-triples 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGES.md +17 -11
- data/README.md +72 -39
- data/lib/active_triples/configurable.rb +6 -1
- data/lib/active_triples/list.rb +1 -4
- data/lib/active_triples/nested_attributes.rb +10 -7
- data/lib/active_triples/persistable.rb +13 -0
- data/lib/active_triples/persistence_strategies/parent_strategy.rb +47 -34
- data/lib/active_triples/persistence_strategies/persistence_strategy.rb +14 -1
- data/lib/active_triples/properties.rb +19 -4
- data/lib/active_triples/property_builder.rb +4 -4
- data/lib/active_triples/rdf_source.rb +142 -189
- data/lib/active_triples/relation.rb +307 -156
- data/lib/active_triples/util/buffered_transaction.rb +126 -0
- data/lib/active_triples/util/extended_bounded_description.rb +75 -0
- data/lib/active_triples/version.rb +1 -1
- data/spec/active_triples/configurable_spec.rb +35 -7
- data/spec/active_triples/identifiable_spec.rb +19 -6
- data/spec/active_triples/list_spec.rb +15 -7
- data/spec/active_triples/nested_attributes_spec.rb +12 -10
- data/spec/active_triples/persistable_spec.rb +0 -4
- data/spec/active_triples/persistence_strategies/parent_strategy_spec.rb +57 -10
- data/spec/active_triples/rdf_source_spec.rb +137 -97
- data/spec/active_triples/relation_spec.rb +436 -132
- data/spec/active_triples/resource_spec.rb +8 -23
- data/spec/active_triples/util/buffered_transaction_spec.rb +187 -0
- data/spec/active_triples/util/extended_bounded_description_spec.rb +98 -0
- data/spec/integration/reciprocal_properties_spec.rb +10 -10
- data/spec/support/matchers.rb +13 -1
- metadata +7 -3
| @@ -24,7 +24,7 @@ module ActiveTriples | |
| 24 24 | 
             
                # @return [Boolean] true if the resource was sucessfully destroyed
         | 
| 25 25 | 
             
                def destroy(&block)
         | 
| 26 26 | 
             
                  yield if block_given?
         | 
| 27 | 
            -
             | 
| 27 | 
            +
             | 
| 28 28 | 
             
                  persist!
         | 
| 29 29 | 
             
                  @destroyed = true
         | 
| 30 30 | 
             
                end
         | 
| @@ -57,6 +57,19 @@ module ActiveTriples | |
| 57 57 | 
             
                  raise NotImplementedError, 'Abstract method #persist! is unimplemented'
         | 
| 58 58 | 
             
                end
         | 
| 59 59 |  | 
| 60 | 
            +
                ##
         | 
| 61 | 
            +
                # @param graph [RDF::Graph]
         | 
| 62 | 
            +
                # @return [RDF::Graph]
         | 
| 63 | 
            +
                def graph=(graph)
         | 
| 64 | 
            +
                  @graph = graph
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                ##
         | 
| 68 | 
            +
                # @return [RDF::Graph]
         | 
| 69 | 
            +
                def graph
         | 
| 70 | 
            +
                  @graph ||= RDF::Graph.new
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 60 73 | 
             
                ##
         | 
| 61 74 | 
             
                # @abstract Clear out any old assertions in the datastore / repository 
         | 
| 62 75 | 
             
                # about this node  or statement thus preparing to receive the updated 
         | 
| @@ -8,10 +8,19 @@ module ActiveTriples | |
| 8 8 | 
             
              # 
         | 
| 9 9 | 
             
              # Collaborates closely with ActiveTriples::Reflection
         | 
| 10 10 | 
             
              #
         | 
| 11 | 
            -
              #  | 
| 11 | 
            +
              # @example define properties at the class level
         | 
| 12 12 | 
             
              #
         | 
| 13 13 | 
             
              #    property :title, predicate: RDF::DC.title, class_name: ResourceClass
         | 
| 14 14 | 
             
              #
         | 
| 15 | 
            +
              # @example using property setters & getters
         | 
| 16 | 
            +
              #    resource.property :title, predicate: RDF::DC.title, 
         | 
| 17 | 
            +
              #                              class_name: ResourceClass
         | 
| 18 | 
            +
              #
         | 
| 19 | 
            +
              #    resource.title = 'Comet in Moominland'
         | 
| 20 | 
            +
              #
         | 
| 21 | 
            +
              #    resource.title                # => ['Comet in Moominland']
         | 
| 22 | 
            +
              #    resource.title(literal: true) # => [RDF::Literal('Comet in Moominland')]
         | 
| 23 | 
            +
              #
         | 
| 15 24 | 
             
              # @see {ActiveTriples::Reflection}
         | 
| 16 25 | 
             
              # @see {ActiveTriples::PropertyBuilder}
         | 
| 17 26 | 
             
              module Properties
         | 
| @@ -54,9 +63,15 @@ module ActiveTriples | |
| 54 63 | 
             
                  #
         | 
| 55 64 | 
             
                  # @return [Array<RDF::URI>]
         | 
| 56 65 | 
             
                  def unregistered_predicates
         | 
| 57 | 
            -
                     | 
| 58 | 
            -
                     | 
| 59 | 
            -
             | 
| 66 | 
            +
                    registered_preds   = registered_predicates << RDF.type
         | 
| 67 | 
            +
                    unregistered_preds = []
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    query(subject: rdf_subject) do |stmt|
         | 
| 70 | 
            +
                      unregistered_preds << stmt.predicate unless
         | 
| 71 | 
            +
                        registered_preds.include? stmt.predicate
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    unregistered_preds
         | 
| 60 75 | 
             
                  end
         | 
| 61 76 |  | 
| 62 77 | 
             
                public
         | 
| @@ -42,7 +42,7 @@ module ActiveTriples | |
| 42 42 | 
             
                  raise ArgumentError, "property names must be a Symbol" unless
         | 
| 43 43 | 
             
                    name.kind_of?(Symbol)
         | 
| 44 44 |  | 
| 45 | 
            -
                  options[:predicate] = RDF::URI. | 
| 45 | 
            +
                  options[:predicate] = RDF::URI.intern(options[:predicate])
         | 
| 46 46 | 
             
                  raise ArgumentError, "must provide an RDF::URI to :predicate" unless
         | 
| 47 47 | 
             
                    options[:predicate].valid?
         | 
| 48 48 |  | 
| @@ -67,15 +67,15 @@ module ActiveTriples | |
| 67 67 | 
             
                def self.define_readers(mixin, name)
         | 
| 68 68 | 
             
                  mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
         | 
| 69 69 | 
             
                    def #{name}(*args)
         | 
| 70 | 
            -
                      get_values(:#{name})
         | 
| 70 | 
            +
                      get_values(:#{name}, *args)
         | 
| 71 71 | 
             
                    end
         | 
| 72 72 | 
             
                  CODE
         | 
| 73 73 | 
             
                end
         | 
| 74 74 |  | 
| 75 75 | 
             
                def self.define_id_reader(mixin, name)
         | 
| 76 76 | 
             
                  mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
         | 
| 77 | 
            -
                    def #{name}_ids(* | 
| 78 | 
            -
                      get_values(:#{name}, : | 
| 77 | 
            +
                    def #{name}_ids(*)
         | 
| 78 | 
            +
                      get_values(:#{name}, cast: false)
         | 
| 79 79 | 
             
                    end
         | 
| 80 80 | 
             
                  CODE
         | 
| 81 81 | 
             
                end
         | 
| @@ -75,8 +75,8 @@ module ActiveTriples | |
| 75 75 | 
             
                  define_model_callbacks :persist
         | 
| 76 76 | 
             
                end
         | 
| 77 77 |  | 
| 78 | 
            -
                delegate :query, :each, :load!, :count, :has_statement?, : | 
| 79 | 
            -
                delegate :to_base, :term?, :escape, : | 
| 78 | 
            +
                delegate :query, :each, :load!, :count, :has_statement?, to: :graph
         | 
| 79 | 
            +
                delegate :to_base, :term?, :escape, to: :to_term
         | 
| 80 80 |  | 
| 81 81 | 
             
                ##
         | 
| 82 82 | 
             
                # Initialize an instance of this resource class. Defaults to a
         | 
| @@ -93,21 +93,19 @@ module ActiveTriples | |
| 93 93 | 
             
                  resource_uri = args.shift unless args.first.is_a?(Hash)
         | 
| 94 94 | 
             
                  @rdf_subject = get_uri(resource_uri) if resource_uri
         | 
| 95 95 |  | 
| 96 | 
            -
                   | 
| 96 | 
            +
                  if args.first.is_a?(Hash) || args.empty?
         | 
| 97 | 
            +
                    set_persistence_strategy(RepositoryStrategy)
         | 
| 98 | 
            +
                  else
         | 
| 97 99 | 
             
                    set_persistence_strategy(ParentStrategy)
         | 
| 98 100 | 
             
                    persistence_strategy.parent = args.shift
         | 
| 99 | 
            -
                  else
         | 
| 100 | 
            -
                    set_persistence_strategy(RepositoryStrategy)
         | 
| 101 101 | 
             
                  end
         | 
| 102 102 |  | 
| 103 | 
            -
                   | 
| 103 | 
            +
                  persistence_strategy.graph = RDF::Graph.new(*args, &block)
         | 
| 104 104 | 
             
                  reload
         | 
| 105 105 |  | 
| 106 106 | 
             
                  # Append type to graph if necessary.
         | 
| 107 107 | 
             
                  Array.wrap(self.class.type).each do |type|
         | 
| 108 | 
            -
                    unless  | 
| 109 | 
            -
                      self.get_values(:type) << type
         | 
| 110 | 
            -
                    end
         | 
| 108 | 
            +
                    get_values(:type) << type unless get_values(:type).include?(type)
         | 
| 111 109 | 
             
                  end
         | 
| 112 110 | 
             
                end
         | 
| 113 111 |  | 
| @@ -128,12 +126,12 @@ module ActiveTriples | |
| 128 126 | 
             
                ##
         | 
| 129 127 | 
             
                # Gives a hash containing both the registered and unregistered attributes of
         | 
| 130 128 | 
             
                # the resource. Unregistered attributes are given with full URIs.
         | 
| 131 | 
            -
                # | 
| 132 | 
            -
                # @example | 
| 129 | 
            +
                #
         | 
| 130 | 
            +
                # @example
         | 
| 133 131 | 
             
                #   class WithProperties
         | 
| 134 132 | 
             
                #     include ActiveTriples::RDFSource
         | 
| 135 133 | 
             
                #     property :title,   predicate:  RDF::Vocab::DC.title
         | 
| 136 | 
            -
                #     property :creator, predicate:  RDF::Vocab::DC.creator, | 
| 134 | 
            +
                #     property :creator, predicate:  RDF::Vocab::DC.creator,
         | 
| 137 135 | 
             
                #                        class_name: 'Agent'
         | 
| 138 136 | 
             
                #   end
         | 
| 139 137 | 
             
                #
         | 
| @@ -146,7 +144,7 @@ module ActiveTriples | |
| 146 144 | 
             
                #
         | 
| 147 145 | 
             
                #   resource.creator.build
         | 
| 148 146 | 
             
                #   resource.title << ['Comet in Moominland', 'Christmas in Moominvalley']
         | 
| 149 | 
            -
             | 
| 147 | 
            +
                #
         | 
| 150 148 | 
             
                #   resource.attributes
         | 
| 151 149 | 
             
                #   # => {"id"=>"g47123700054720",
         | 
| 152 150 | 
             
                #   #     "title"=>["Comet in Moominland", "Christmas in Moominvalley"],
         | 
| @@ -171,25 +169,33 @@ module ActiveTriples | |
| 171 169 | 
             
                end
         | 
| 172 170 |  | 
| 173 171 | 
             
                def attributes=(values)
         | 
| 174 | 
            -
                  raise | 
| 172 | 
            +
                  raise(ArgumentError, "values must be a Hash. Got: #{values.class}") unless
         | 
| 173 | 
            +
                    values.is_a? Hash
         | 
| 174 | 
            +
             | 
| 175 175 | 
             
                  values = values.with_indifferent_access
         | 
| 176 176 | 
             
                  id = values.delete(:id)
         | 
| 177 | 
            -
                  set_subject!(id) if node?
         | 
| 177 | 
            +
                  set_subject!(id) if node? && id && get_uri(id).uri?
         | 
| 178 | 
            +
             | 
| 178 179 | 
             
                  values.each do |key, value|
         | 
| 179 180 | 
             
                    if reflections.has_property?(key)
         | 
| 180 | 
            -
                      set_value( | 
| 181 | 
            -
                    elsif nested_attributes_options | 
| 181 | 
            +
                      set_value(key, value)
         | 
| 182 | 
            +
                    elsif nested_attributes_options
         | 
| 183 | 
            +
                          .keys.any? { |k| key == "#{k}_attributes" }
         | 
| 182 184 | 
             
                      send("#{key}=".to_sym, value)
         | 
| 183 185 | 
             
                    else
         | 
| 184 | 
            -
                      raise ArgumentError, "No association found for name `#{key}'.  | 
| 186 | 
            +
                      raise ArgumentError, "No association found for name `#{key}'. " \
         | 
| 187 | 
            +
                                           'Has it been defined yet?'
         | 
| 185 188 | 
             
                    end
         | 
| 186 189 | 
             
                  end
         | 
| 187 190 | 
             
                end
         | 
| 188 191 |  | 
| 189 | 
            -
                 | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            +
                ##
         | 
| 193 | 
            +
                # @return [Hash]
         | 
| 194 | 
            +
                def serializable_hash(*)
         | 
| 195 | 
            +
                  attrs = fields.map(&:to_s) << 'id'
         | 
| 196 | 
            +
                  hash = super(only: attrs)
         | 
| 192 197 | 
             
                  unregistered_predicates.map { |uri| hash[uri.to_s] = get_values(uri) }
         | 
| 198 | 
            +
             | 
| 193 199 | 
             
                  hash
         | 
| 194 200 | 
             
                end
         | 
| 195 201 |  | 
| @@ -204,7 +210,7 @@ module ActiveTriples | |
| 204 210 | 
             
                # @param args [Array<Object>]
         | 
| 205 211 | 
             
                # @return [String]
         | 
| 206 212 | 
             
                def dump(*args)
         | 
| 207 | 
            -
                  if args.first == :jsonld  | 
| 213 | 
            +
                  if args.first == :jsonld && respond_to?(:jsonld_context)
         | 
| 208 214 | 
             
                    args << {} unless args.last.is_a?(Hash)
         | 
| 209 215 | 
             
                    args.last[:context] ||= jsonld_context
         | 
| 210 216 | 
             
                  end
         | 
| @@ -214,17 +220,23 @@ module ActiveTriples | |
| 214 220 | 
             
                ##
         | 
| 215 221 | 
             
                # Delegate parent to the persistence strategy if possible
         | 
| 216 222 | 
             
                #
         | 
| 217 | 
            -
                # @todo establish a better pattern for this. `#parent` has been a public | 
| 223 | 
            +
                # @todo establish a better pattern for this. `#parent` has been a public
         | 
| 218 224 | 
             
                #   method in the past, but it's probably time to deprecate it.
         | 
| 219 225 | 
             
                def parent
         | 
| 220 | 
            -
                   | 
| 226 | 
            +
                  return persistence_strategy.parent if
         | 
| 227 | 
            +
                    persistence_strategy.respond_to?(:parent)
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                  nil
         | 
| 221 230 | 
             
                end
         | 
| 222 231 |  | 
| 223 232 | 
             
                ##
         | 
| 224 233 | 
             
                # @todo deprecate/remove
         | 
| 225 234 | 
             
                # @see #parent
         | 
| 226 235 | 
             
                def parent=(parent)
         | 
| 227 | 
            -
                   | 
| 236 | 
            +
                  return persistence_strategy.parent = parent if
         | 
| 237 | 
            +
                    persistence_strategy.respond_to?(:parent=)
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                  nil
         | 
| 228 240 | 
             
                end
         | 
| 229 241 |  | 
| 230 242 | 
             
                ##
         | 
| @@ -237,15 +249,15 @@ module ActiveTriples | |
| 237 249 | 
             
                def rdf_subject
         | 
| 238 250 | 
             
                  @rdf_subject ||= RDF::Node.new
         | 
| 239 251 | 
             
                end
         | 
| 240 | 
            -
                 | 
| 252 | 
            +
                alias to_term rdf_subject
         | 
| 241 253 |  | 
| 242 254 | 
             
                ##
         | 
| 243 255 | 
             
                # Returns `nil` as the `graph_name`. This behavior mimics an `RDF::Graph`
         | 
| 244 256 | 
             
                # with no graph name, or one without named graph support.
         | 
| 245 257 | 
             
                #
         | 
| 246 | 
            -
                # @note: it's possible to think of an `RDFSource` as "supporting named | 
| 258 | 
            +
                # @note: it's possible to think of an `RDFSource` as "supporting named
         | 
| 247 259 | 
             
                #   graphs" in the sense that the `#rdf_subject` is an implied graph name.
         | 
| 248 | 
            -
                #   For RDF.rb's purposes, however, it has a nil graph name: when | 
| 260 | 
            +
                #   For RDF.rb's purposes, however, it has a nil graph name: when
         | 
| 249 261 | 
             
                #   enumerating statements, we treat them as triples.
         | 
| 250 262 | 
             
                #
         | 
| 251 263 | 
             
                # @return [nil]
         | 
| @@ -253,7 +265,7 @@ module ActiveTriples | |
| 253 265 | 
             
                def graph_name
         | 
| 254 266 | 
             
                  nil
         | 
| 255 267 | 
             
                end
         | 
| 256 | 
            -
             | 
| 268 | 
            +
             | 
| 257 269 | 
             
                ##
         | 
| 258 270 | 
             
                # @return [String] A string identifier for the resource; '' if the
         | 
| 259 271 | 
             
                #   resource is a node
         | 
| @@ -299,12 +311,15 @@ module ActiveTriples | |
| 299 311 | 
             
                end
         | 
| 300 312 |  | 
| 301 313 | 
             
                def type
         | 
| 302 | 
            -
                   | 
| 314 | 
            +
                  get_values(:type)
         | 
| 303 315 | 
             
                end
         | 
| 304 316 |  | 
| 305 317 | 
             
                def type=(type)
         | 
| 306 | 
            -
                  raise | 
| 307 | 
            -
             | 
| 318 | 
            +
                  raise(ArgumentError,
         | 
| 319 | 
            +
                        "Type must be an RDF::URI. Got: #{type.class}, #{type}") unless
         | 
| 320 | 
            +
                    type.is_a? RDF::URI
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                  update(RDF::Statement.new(rdf_subject, RDF.type, type))
         | 
| 308 323 | 
             
                end
         | 
| 309 324 |  | 
| 310 325 | 
             
                ##
         | 
| @@ -322,16 +337,6 @@ module ActiveTriples | |
| 322 337 | 
             
                  node? ? [] : [rdf_subject.to_s]
         | 
| 323 338 | 
             
                end
         | 
| 324 339 |  | 
| 325 | 
            -
                ##
         | 
| 326 | 
            -
                # @return [Array<RDF::URI>] a group of properties to use for default labels.
         | 
| 327 | 
            -
                def default_labels
         | 
| 328 | 
            -
                  [RDF::SKOS.prefLabel,
         | 
| 329 | 
            -
                   RDF::DC.title,
         | 
| 330 | 
            -
                   RDF::RDFS.label,
         | 
| 331 | 
            -
                   RDF::SKOS.altLabel,
         | 
| 332 | 
            -
                   RDF::SKOS.hiddenLabel]
         | 
| 333 | 
            -
                end
         | 
| 334 | 
            -
             | 
| 335 340 | 
             
                ##
         | 
| 336 341 | 
             
                # Load data from the #rdf_subject URI. Retrieved data will be
         | 
| 337 342 | 
             
                # parsed into the Resource's graph from available RDF::Readers
         | 
| @@ -353,15 +358,15 @@ module ActiveTriples | |
| 353 358 | 
             
                # @yieldparam [ActiveTriples::RDFSource] resource  self
         | 
| 354 359 | 
             
                #
         | 
| 355 360 | 
             
                # @return [ActiveTriples::RDFSource] self
         | 
| 356 | 
            -
                def fetch(*args, & | 
| 361 | 
            +
                def fetch(*args, &_block)
         | 
| 357 362 | 
             
                  begin
         | 
| 358 363 | 
             
                    load(rdf_subject, *args)
         | 
| 359 364 | 
             
                  rescue => e
         | 
| 360 365 | 
             
                    if block_given?
         | 
| 361 366 | 
             
                      yield(self)
         | 
| 362 367 | 
             
                    else
         | 
| 363 | 
            -
                      raise "#{self} is a blank node;  | 
| 364 | 
            -
             | 
| 368 | 
            +
                      raise "#{self} is a blank node; " \
         | 
| 369 | 
            +
                            'Cannot fetch a resource without a URI' if node?
         | 
| 365 370 | 
             
                      raise e
         | 
| 366 371 | 
             
                    end
         | 
| 367 372 | 
             
                  end
         | 
| @@ -404,12 +409,12 @@ module ActiveTriples | |
| 404 409 | 
             
                #
         | 
| 405 410 | 
             
                # @overload set_value(subject, property, values)
         | 
| 406 411 | 
             
                #   Updates the values for the property, using the given term as the subject
         | 
| 407 | 
            -
                # | 
| 408 | 
            -
                #   @param [RDF::Term] subject  the term representing the | 
| 412 | 
            +
                #
         | 
| 413 | 
            +
                #   @param [RDF::Term] subject  the term representing the
         | 
| 409 414 | 
             
                #   @param [RDF::Term, #to_sym] property  a symbol with the property name
         | 
| 410 415 | 
             
                #     or an RDF::Term to use as a predicate.
         | 
| 411 416 | 
             
                #   @param [Array<RDF::Resource>, RDF::Resource] values  an array of values
         | 
| 412 | 
            -
                #     or a single value. If not an {RDF::Resource}, the values will be | 
| 417 | 
            +
                #     or a single value. If not an {RDF::Resource}, the values will be
         | 
| 413 418 | 
             
                #     coerced to an {RDF::Literal} or {RDF::Node} by {RDF::Statement}
         | 
| 414 419 | 
             
                #
         | 
| 415 420 | 
             
                # @return [ActiveTriples::Relation] an array {Relation} containing the
         | 
| @@ -421,16 +426,17 @@ module ActiveTriples | |
| 421 426 | 
             
                # @note This method will delete existing statements with the given
         | 
| 422 427 | 
             
                #   subject and predicate from the graph
         | 
| 423 428 | 
             
                #
         | 
| 424 | 
            -
                # @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Statement For | 
| 425 | 
            -
                #   documentation on {RDF::Statement} and the handling of | 
| 429 | 
            +
                # @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Statement For
         | 
| 430 | 
            +
                #   documentation on {RDF::Statement} and the handling of
         | 
| 426 431 | 
             
                #   non-{RDF::Resource} values.
         | 
| 427 432 | 
             
                def set_value(*args)
         | 
| 428 433 | 
             
                  # Add support for legacy 3-parameter syntax
         | 
| 429 434 | 
             
                  if args.length > 3 || args.length < 2
         | 
| 430 | 
            -
                    raise ArgumentError, | 
| 435 | 
            +
                    raise ArgumentError,
         | 
| 436 | 
            +
                          "wrong number of arguments (#{args.length} for 2-3)"
         | 
| 431 437 | 
             
                  end
         | 
| 432 438 | 
             
                  values = args.pop
         | 
| 433 | 
            -
                   | 
| 439 | 
            +
                  get_values(*args).set(values)
         | 
| 434 440 | 
             
                end
         | 
| 435 441 |  | 
| 436 442 | 
             
                ##
         | 
| @@ -442,7 +448,7 @@ module ActiveTriples | |
| 442 448 | 
             
                # properties directly on this RDFSource, is:
         | 
| 443 449 | 
             
                #    get_values(property)
         | 
| 444 450 | 
             
                #
         | 
| 445 | 
            -
                # @overload get_values(property) | 
| 451 | 
            +
                # @overload get_values(property)
         | 
| 446 452 | 
             
                #   Gets values on the RDFSource for the given property
         | 
| 447 453 | 
             
                #   @param [String, #to_term] property  the property for the values
         | 
| 448 454 | 
             
                #
         | 
| @@ -452,22 +458,25 @@ module ActiveTriples | |
| 452 458 | 
             
                #   @param [RDF::Term] uri  the term to use as the subject
         | 
| 453 459 | 
             
                #   @param [String, #to_term] property  the property for the values
         | 
| 454 460 | 
             
                #
         | 
| 455 | 
            -
                # @return [ActiveTriples::Relation] an array {Relation} containing the | 
| 461 | 
            +
                # @return [ActiveTriples::Relation] an array {Relation} containing the
         | 
| 456 462 | 
             
                #   values of the property
         | 
| 457 463 | 
             
                #
         | 
| 458 | 
            -
                # @todo should this raise an error when the property argument is not an | 
| 464 | 
            +
                # @todo should this raise an error when the property argument is not an
         | 
| 459 465 | 
             
                #   {RDF::Term} or a registered property key?
         | 
| 460 466 | 
             
                def get_values(*args)
         | 
| 461 | 
            -
                   | 
| 467 | 
            +
                  @relation_cache ||= {}
         | 
| 468 | 
            +
                  rel = Relation.new(self, args)
         | 
| 469 | 
            +
                  @relation_cache["#{rel.send(:rdf_subject)}/#{rel.property}/#{rel.rel_args}"] ||= rel
         | 
| 470 | 
            +
                  @relation_cache["#{rel.send(:rdf_subject)}/#{rel.property}/#{rel.rel_args}"]
         | 
| 462 471 | 
             
                end
         | 
| 463 472 |  | 
| 464 473 | 
             
                ##
         | 
| 465 474 | 
             
                # Returns an array of values belonging to the property requested. Elements
         | 
| 466 475 | 
             
                # in the array may RdfResource objects or a valid datatype.
         | 
| 467 | 
            -
                # | 
| 476 | 
            +
                #
         | 
| 468 477 | 
             
                # @param [RDF::Term, :to_s] term_or_property
         | 
| 469 478 | 
             
                def [](term_or_property)
         | 
| 470 | 
            -
                   | 
| 479 | 
            +
                  get_values(term_or_property)
         | 
| 471 480 | 
             
                end
         | 
| 472 481 |  | 
| 473 482 | 
             
                ##
         | 
| @@ -475,23 +484,21 @@ module ActiveTriples | |
| 475 484 | 
             
                #
         | 
| 476 485 | 
             
                # @param [RDF::Term, :to_s] term_or_property
         | 
| 477 486 | 
             
                # @param [Array<RDF::Resource>, RDF::Resource] values  an array of values
         | 
| 478 | 
            -
                #   or a single value to set the property to. | 
| 487 | 
            +
                #   or a single value to set the property to.
         | 
| 479 488 | 
             
                #
         | 
| 480 | 
            -
                # @note This method will delete existing statements with the correct | 
| 489 | 
            +
                # @note This method will delete existing statements with the correct
         | 
| 481 490 | 
             
                #   subject and predicate from the graph
         | 
| 482 491 | 
             
                def []=(term_or_property, value)
         | 
| 483 492 | 
             
                  self[term_or_property].set(value)
         | 
| 484 493 | 
             
                end
         | 
| 485 494 |  | 
| 486 495 | 
             
                ##
         | 
| 496 | 
            +
                # @deprecated for removal in 1.0; use `#get_values` instead.
         | 
| 487 497 | 
             
                # @see #get_values
         | 
| 488 | 
            -
                # @todo deprecate and remove? this is an alias to `#get_values`
         | 
| 489 498 | 
             
                def get_relation(args)
         | 
| 490 | 
            -
                   | 
| 491 | 
            -
             | 
| 492 | 
            -
                   | 
| 493 | 
            -
                  @relation_cache["#{rel.send(:rdf_subject)}/#{rel.property}/#{rel.rel_args}"] ||= rel
         | 
| 494 | 
            -
                  @relation_cache["#{rel.send(:rdf_subject)}/#{rel.property}/#{rel.rel_args}"]
         | 
| 499 | 
            +
                  warn 'DEPRECATION: `ActiveTriples::RDFSource#get_relation` will be' \
         | 
| 500 | 
            +
                       'removed in 1.0; use `#get_values` instead.'
         | 
| 501 | 
            +
                  get_values(*args)
         | 
| 495 502 | 
             
                end
         | 
| 496 503 |  | 
| 497 504 | 
             
                ##
         | 
| @@ -508,13 +515,12 @@ module ActiveTriples | |
| 508 515 | 
             
                #   the param. Otherwise it creates a URI for the resource and
         | 
| 509 516 | 
             
                #   rebuilds the graph with the updated URI.
         | 
| 510 517 | 
             
                def set_subject!(uri_or_str)
         | 
| 511 | 
            -
                  raise  | 
| 518 | 
            +
                  raise 'Refusing to update URI when one is already assigned!' unless
         | 
| 512 519 | 
             
                    node? || rdf_subject == RDF::URI(nil)
         | 
| 513 | 
            -
             | 
| 514 | 
            -
                  return  | 
| 515 | 
            -
             | 
| 516 | 
            -
             | 
| 517 | 
            -
                  
         | 
| 520 | 
            +
             | 
| 521 | 
            +
                  return if uri_or_str.nil? ||
         | 
| 522 | 
            +
                            (uri_or_str.to_s.empty? && !uri_or_str.is_a?(RDF::URI))
         | 
| 523 | 
            +
             | 
| 518 524 | 
             
                  new_subject = get_uri(uri_or_str)
         | 
| 519 525 | 
             
                  rewrite_statement_uris(rdf_subject, new_subject)
         | 
| 520 526 |  | 
| @@ -526,7 +532,7 @@ module ActiveTriples | |
| 526 532 | 
             
                #
         | 
| 527 533 | 
             
                # @return [Boolean]
         | 
| 528 534 | 
             
                def new_record?
         | 
| 529 | 
            -
                   | 
| 535 | 
            +
                  !persisted?
         | 
| 530 536 | 
             
                end
         | 
| 531 537 |  | 
| 532 538 | 
             
                def mark_for_destruction
         | 
| @@ -539,126 +545,73 @@ module ActiveTriples | |
| 539 545 |  | 
| 540 546 | 
             
                private
         | 
| 541 547 |  | 
| 542 | 
            -
             | 
| 543 | 
            -
             | 
| 544 | 
            -
             | 
| 545 | 
            -
                   | 
| 546 | 
            -
             | 
| 547 | 
            -
             | 
| 548 | 
            -
             | 
| 549 | 
            -
             | 
| 550 | 
            -
             | 
| 551 | 
            -
                  def graph
         | 
| 552 | 
            -
                    @graph
         | 
| 553 | 
            -
                  end
         | 
| 554 | 
            -
             | 
| 555 | 
            -
                  ##
         | 
| 556 | 
            -
             | 
| 557 | 
            -
                  # Lists fields registered as properties on the object.
         | 
| 558 | 
            -
                  #
         | 
| 559 | 
            -
                  # @return [Array<Symbol>] the list of registered properties.
         | 
| 560 | 
            -
                  def fields
         | 
| 561 | 
            -
                    properties.keys.map(&:to_sym).reject{ |x| x == :type }
         | 
| 562 | 
            -
                  end
         | 
| 548 | 
            +
                ##
         | 
| 549 | 
            +
                # @return [Array<RDF::URI>] a group of properties to use for default labels.
         | 
| 550 | 
            +
                def default_labels
         | 
| 551 | 
            +
                  [RDF::Vocab::SKOS.prefLabel,
         | 
| 552 | 
            +
                   RDF::Vocab::DC.title,
         | 
| 553 | 
            +
                   RDF::RDFS.label,
         | 
| 554 | 
            +
                   RDF::Vocab::SKOS.altLabel,
         | 
| 555 | 
            +
                   RDF::Vocab::SKOS.hiddenLabel]
         | 
| 556 | 
            +
                end
         | 
| 563 557 |  | 
| 564 | 
            -
             | 
| 565 | 
            -
             | 
| 566 | 
            -
             | 
| 567 | 
            -
             | 
| 568 | 
            -
             | 
| 569 | 
            -
             | 
| 570 | 
            -
             | 
| 558 | 
            +
                ##
         | 
| 559 | 
            +
                # Rewrites the subject and object of each statement containing
         | 
| 560 | 
            +
                # `old_subject` in either position. Used when setting the subject to
         | 
| 561 | 
            +
                # remove the placeholder blank node subjects.
         | 
| 562 | 
            +
                #
         | 
| 563 | 
            +
                # @param [RDF::Term] old_subject
         | 
| 564 | 
            +
                # @param [RDF::Term] new_subject
         | 
| 565 | 
            +
                # @return [void]
         | 
| 566 | 
            +
                def rewrite_statement_uris(old_subject, new_subject)
         | 
| 567 | 
            +
                  graph.query(subject: old_subject).each do |st|
         | 
| 568 | 
            +
                    graph.delete(st)
         | 
| 571 569 |  | 
| 572 | 
            -
             | 
| 573 | 
            -
             | 
| 574 | 
            -
             | 
| 575 | 
            -
                  # @return [Array<RDF::URI>]
         | 
| 576 | 
            -
                  def registered_predicates
         | 
| 577 | 
            -
                    properties.values.map { |config| config.predicate }
         | 
| 570 | 
            +
                    st.subject = new_subject
         | 
| 571 | 
            +
                    st.object  = new_subject if st.object == old_subject
         | 
| 572 | 
            +
                    graph.insert(st)
         | 
| 578 573 | 
             
                  end
         | 
| 579 574 |  | 
| 580 | 
            -
                   | 
| 581 | 
            -
             | 
| 582 | 
            -
                  # mapped to any property or accessor methods.
         | 
| 583 | 
            -
                  #
         | 
| 584 | 
            -
                  # @return [Array<RDF::URI>]
         | 
| 585 | 
            -
                  def unregistered_predicates
         | 
| 586 | 
            -
                    registered_preds = registered_predicates
         | 
| 587 | 
            -
                    registered_preds << RDF.type
         | 
| 588 | 
            -
                    unregistered_preds = []
         | 
| 589 | 
            -
                    
         | 
| 590 | 
            -
                    query(subject: rdf_subject) do |stmt| 
         | 
| 591 | 
            -
                      unregistered_preds << stmt.predicate unless
         | 
| 592 | 
            -
                        registered_preds.include? stmt.predicate
         | 
| 593 | 
            -
                    end
         | 
| 575 | 
            +
                  graph.query(object: old_subject).each do |st|
         | 
| 576 | 
            +
                    graph.delete(st)
         | 
| 594 577 |  | 
| 595 | 
            -
                     | 
| 578 | 
            +
                    st.object = new_subject
         | 
| 579 | 
            +
                    graph.insert(st)
         | 
| 596 580 | 
             
                  end
         | 
| 581 | 
            +
                end
         | 
| 597 582 |  | 
| 598 | 
            -
             | 
| 599 | 
            -
             | 
| 600 | 
            -
             | 
| 601 | 
            -
             | 
| 602 | 
            -
             | 
| 603 | 
            -
             | 
| 604 | 
            -
             | 
| 583 | 
            +
                ##
         | 
| 584 | 
            +
                # Takes a URI or String and aggressively tries to convert it into
         | 
| 585 | 
            +
                # an RDF term. If a String is given, first tries to interpret it
         | 
| 586 | 
            +
                # as a valid URI, then tries to append it to base_uri. Finally,
         | 
| 587 | 
            +
                # raises an error if no valid term can be built.
         | 
| 588 | 
            +
                #
         | 
| 589 | 
            +
                # The argument must be an RDF::Node, an object that responds to
         | 
| 590 | 
            +
                # #to_uri, a String that represents a valid URI, or a String that
         | 
| 591 | 
            +
                # appends to the Resource's base_uri to create a valid URI.
         | 
| 592 | 
            +
                #
         | 
| 593 | 
            +
                # @TODO: URI.scheme_list is naive and incomplete. Find a better
         | 
| 594 | 
            +
                #   way to check for an existing scheme.
         | 
| 595 | 
            +
                #
         | 
| 596 | 
            +
                # @param uri_or_str [RDF::Resource, String]
         | 
| 597 | 
            +
                #
         | 
| 598 | 
            +
                # @return [RDF::Resource] A term
         | 
| 599 | 
            +
                # @raise [RuntimeError] no valid RDF term could be built
         | 
| 600 | 
            +
                def get_uri(uri_or_str)
         | 
| 601 | 
            +
                  return uri_or_str.to_term if uri_or_str.respond_to? :to_term
         | 
| 605 602 |  | 
| 606 | 
            -
                   | 
| 607 | 
            -
                   | 
| 608 | 
            -
                  # `old_subject` in either position. Used when setting the subject to
         | 
| 609 | 
            -
                  # remove the placeholder blank node subjects.
         | 
| 610 | 
            -
                  #
         | 
| 611 | 
            -
                  # @param [RDF::Term] old_subject
         | 
| 612 | 
            -
                  # @param [RDF::Term] new_subject
         | 
| 613 | 
            -
                  # @return [void]
         | 
| 614 | 
            -
                  def rewrite_statement_uris(old_subject, new_subject)
         | 
| 615 | 
            -
                    graph.transaction(mutable: true) do |tx|
         | 
| 616 | 
            -
                      tx.query(subject: old_subject).each do |st|
         | 
| 617 | 
            -
                        tx.delete(st)
         | 
| 618 | 
            -
             | 
| 619 | 
            -
                        st.subject = new_subject
         | 
| 620 | 
            -
                        st.object  = new_subject if st.object == old_subject
         | 
| 621 | 
            -
                        tx.insert(st)
         | 
| 622 | 
            -
                      end
         | 
| 623 | 
            -
                      
         | 
| 624 | 
            -
                      tx.query(object: old_subject).each do |st|
         | 
| 625 | 
            -
                        tx.delete(st)
         | 
| 626 | 
            -
                        
         | 
| 627 | 
            -
                        st.object = new_subject
         | 
| 628 | 
            -
                        tx.insert(st)
         | 
| 629 | 
            -
                      end
         | 
| 630 | 
            -
                    end
         | 
| 631 | 
            -
                  end
         | 
| 603 | 
            +
                  uri_or_node = RDF::Resource.new(uri_or_str)
         | 
| 604 | 
            +
                  return uri_or_node if uri_or_node.valid?
         | 
| 632 605 |  | 
| 633 | 
            -
                   | 
| 634 | 
            -
                   | 
| 635 | 
            -
             | 
| 636 | 
            -
                  # as a valid URI, then tries to append it to base_uri. Finally,
         | 
| 637 | 
            -
                  # raises an error if no valid term can be built.
         | 
| 638 | 
            -
                  #
         | 
| 639 | 
            -
                  # The argument must be an RDF::Node, an object that responds to
         | 
| 640 | 
            -
                  # #to_uri, a String that represents a valid URI, or a String that
         | 
| 641 | 
            -
                  # appends to the Resource's base_uri to create a valid URI.
         | 
| 642 | 
            -
                  #
         | 
| 643 | 
            -
                  # @TODO: URI.scheme_list is naive and incomplete. Find a better
         | 
| 644 | 
            -
                  #   way to check for an existing scheme.
         | 
| 645 | 
            -
                  #
         | 
| 646 | 
            -
                  # @param uri_or_str [RDF::Resource, String]
         | 
| 647 | 
            -
                  #
         | 
| 648 | 
            -
                  # @return [RDF::Resource] A term
         | 
| 649 | 
            -
                  # @raise [RuntimeError] no valid RDF term could be built
         | 
| 650 | 
            -
                  def get_uri(uri_or_str)
         | 
| 651 | 
            -
                    return uri_or_str.to_term if uri_or_str.respond_to? :to_term
         | 
| 652 | 
            -
                    return uri_or_str if uri_or_str.is_a? RDF::Node
         | 
| 653 | 
            -
                    uri_or_node = RDF::Resource.new(uri_or_str)
         | 
| 654 | 
            -
                    return uri_or_node if uri_or_node.valid?
         | 
| 655 | 
            -
                    uri_or_str = uri_or_str.to_s
         | 
| 656 | 
            -
                    return RDF::URI(base_uri.to_s) / uri_or_str if base_uri && !uri_or_str.start_with?(base_uri.to_s)
         | 
| 657 | 
            -
                    raise RuntimeError, "could not make a valid RDF::URI from #{uri_or_str}"
         | 
| 658 | 
            -
                  end
         | 
| 606 | 
            +
                  uri_or_str = uri_or_str.to_s
         | 
| 607 | 
            +
                  return RDF::URI.intern(base_uri.to_s) / uri_or_str if
         | 
| 608 | 
            +
                    base_uri && !uri_or_str.start_with?(base_uri.to_s)
         | 
| 659 609 |  | 
| 660 | 
            -
             | 
| 610 | 
            +
                  raise "could not make a valid RDF::URI from #{uri_or_str}"
         | 
| 611 | 
            +
                end
         | 
| 661 612 |  | 
| 613 | 
            +
                ##
         | 
| 614 | 
            +
                # Class methods for RDFSource, included via ActiveSupport
         | 
| 662 615 | 
             
                module ClassMethods
         | 
| 663 616 | 
             
                  ##
         | 
| 664 617 | 
             
                  # Adapter for a consistent interface for creating a new Resource
         | 
| @@ -679,10 +632,8 @@ module ActiveTriples | |
| 679 632 | 
             
                  # @param [ActiveTriples::Schema, #properties] schema A schema to apply.
         | 
| 680 633 | 
             
                  # @param [#apply!] strategy A strategy for applying. Defaults
         | 
| 681 634 | 
             
                  #   to ActiveTriples::ExtensionStrategy
         | 
| 682 | 
            -
                  def apply_schema(schema, strategy=ActiveTriples::ExtensionStrategy)
         | 
| 683 | 
            -
                    schema.properties.each  | 
| 684 | 
            -
                      strategy.apply(self, property)
         | 
| 685 | 
            -
                    end
         | 
| 635 | 
            +
                  def apply_schema(schema, strategy = ActiveTriples::ExtensionStrategy)
         | 
| 636 | 
            +
                    schema.properties.each { |property| strategy.apply(self, property) }
         | 
| 686 637 | 
             
                  end
         | 
| 687 638 |  | 
| 688 639 | 
             
                  ##
         | 
| @@ -697,7 +648,8 @@ module ActiveTriples | |
| 697 648 | 
             
                  #          persisted, false will be returned presenting
         | 
| 698 649 | 
             
                  #          a window of opportunity for an ID clash.
         | 
| 699 650 | 
             
                  def id_persisted?(test_id)
         | 
| 700 | 
            -
                    rdf_subject =  | 
| 651 | 
            +
                    rdf_subject = new(test_id).rdf_subject
         | 
| 652 | 
            +
             | 
| 701 653 | 
             
                    ActiveTriples::Repositories.has_subject?(rdf_subject)
         | 
| 702 654 | 
             
                  end
         | 
| 703 655 |  | 
| @@ -713,8 +665,9 @@ module ActiveTriples | |
| 713 665 | 
             
                  #          persisted, false will be returned presenting
         | 
| 714 666 | 
             
                  #          a window of opportunity for an ID clash.
         | 
| 715 667 | 
             
                  def uri_persisted?(test_uri)
         | 
| 716 | 
            -
                     | 
| 717 | 
            -
             | 
| 668 | 
            +
                    test_uri = RDF::URI.intern(test_uri) unless test_uri.is_a?(RDF::URI)
         | 
| 669 | 
            +
             | 
| 670 | 
            +
                    ActiveTriples::Repositories.has_subject?(test_uri)
         | 
| 718 671 | 
             
                  end
         | 
| 719 672 | 
             
                end
         | 
| 720 673 | 
             
              end
         |