Skip to content

Commit

Permalink
Merge pull request #1 from mkllnk/setter
Browse files Browse the repository at this point in the history
Allow to provide a setter for each property
  • Loading branch information
lecoqlibre authored Jul 10, 2023
2 parents c62bb73 + 2d77b93 commit 7019323
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 169 deletions.
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--require spec_helper
--require virtual_assembly/semantizer
271 changes: 131 additions & 140 deletions lib/virtual_assembly/semantizer/semantic_object.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# Copyright © 2023 Maxime Lecoq, <[email protected]>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
Expand All @@ -23,153 +25,142 @@
# A semanticObject holds semantic properties (SemanticProperty)
# that refers to linked data concepts.
#
# For example, a Person object including this module could register
# For example, a Person object including this module could register
# in its initializer method a semantic property for its name like:
# Person.registerSemanticProperty("http://xmlns.com/foaf/0.1/name") {self.name}
module VirtualAssembly::Semantizer::SemanticObject

# The semantic ID implements the concept of linked data ID.
#
# This ID is an uri pointing to the location of the object
# on the web like "https://mywebsite/myobject" for instance.
#
# If a SemanticObject doesn't define its ID, it
# will be considered as a blank node.
#
# This should be a String or nil.
attr_accessor :semanticId

# The semantic type implements the concept of linked data type
# (also called class).
#
# This type is an uri pointing to the location of the linked
# data concept on the web like "http://xmlns.com/foaf/0.1/Person"
# for instance.
#
# This should be a String or nil.
attr_accessor :semanticType

# This Array stores the semantic properties of the object.
# To append a SemanticProperty, use the dedicated
# registerSemanticProperty method. You should pass the value
# of the property as a block (callback) like so:
# registerSemanticProperty("http://xmlns.com/foaf/0.1/name") {self.name}.
attr_reader :semanticProperties

# If the semanticId is nil, the object will be treated as a blank node.
def initialize(semanticId = nil, semanticType = nil)
@semanticProperties = Array.new

# This Hash allows us to find a property using its name.
#
# Hash<String, Integer>
#
# The key store the name of a property (String).
# The value store the index of the property in the
# semanticProperties array (Integer).
@semanticPropertiesNameIndex = Hash.new
module VirtualAssembly
module Semantizer
module SemanticObject
# The semantic ID implements the concept of linked data ID.
#
# This ID is an uri pointing to the location of the object
# on the web like "https://mywebsite/myobject" for instance.
#
# If a SemanticObject doesn't define its ID, it
# will be considered as a blank node.
#
# This should be a String or nil.
attr_accessor :semanticId

# The semantic type implements the concept of linked data type
# (also called class).
#
# This type is an uri pointing to the location of the linked
# data concept on the web like "http://xmlns.com/foaf/0.1/Person"
# for instance.
#
# This should be a String or nil.
attr_accessor :semanticType

# If the semanticId is nil, the object will be treated as a blank node.
def initialize(semanticId = nil, semanticType = nil)
@semanticPropertiesMap = {}

# Ensure to call the setter methods
self.semanticId = semanticId
self.semanticType = semanticType
end

def hasSemanticProperty?(name)
return @semanticPropertiesNameIndex.include?(name)
end

def isBlankNode?
return @semanticId == nil || @semanticId == ""
end

# Given the name of the property, it returns the value
# associated to a property of this object.
def semanticPropertyValue(name)
index = @semanticPropertiesNameIndex.fetch(name, nil)
return index != nil ? @semanticProperties[index].value : nil;
end

# Use this method to append a semantic property to this object.
# The value of the property should be passed as a block so its
# value would be up to date when we will access it.
def registerSemanticProperty(name, &valueGetter)
end

# This Array stores the semantic properties of the object.
# To append a SemanticProperty, use the dedicated
# registerSemanticProperty method. You should pass the value
# of the property as a block (callback) like so:
# registerSemanticProperty("http://xmlns.com/foaf/0.1/name") {self.name}.
def semanticProperties
@semanticPropertiesMap.values
end

def hasSemanticProperty?(name)
@semanticPropertiesMap.key?(name)
end

def isBlankNode?
@semanticId.nil? || @semanticId == ''
end

# Given the name of the property, it returns the value
# associated to a property of this object.
def semanticPropertyValue(name)
semanticProperty(name)&.value
end

# Given its name, returns the corresponding SemanticProperty
# stored by this object or nil if the property does not exist.
def semanticProperty(name)
@semanticPropertiesMap[name]
end

# Use this method to append a semantic property to this object.
# The value of the property should be passed as a block so its
# value would be up to date when we will access it.
def registerSemanticProperty(name, &valueGetter)
createOrUpdateSemanticProperty(name, valueGetter)
end

# Sets the semantic id of the object and registers the
# corresponding semantic property.
#
# The semantic ID implements the concept of linked data ID.
#
# This ID is an uri pointing to the location of the object
# on the web like "https://mywebsite/myobject" for instance.
#
# If a SemanticObject doesn't define its ID, it
# will be considered as a blank node.
#
# This should be a String or nil.
def semanticId=(uri)
end

# Sets the semantic id of the object and registers the
# corresponding semantic property.
#
# The semantic ID implements the concept of linked data ID.
#
# This ID is an uri pointing to the location of the object
# on the web like "https://mywebsite/myobject" for instance.
#
# If a SemanticObject doesn't define its ID, it
# will be considered as a blank node.
#
# This should be a String or nil.
def semanticId=(uri)
@semanticId = uri
registerSemanticProperty("@id") {self.semanticId}
end

# Sets the semantic type of the object and registers the
# corresponding semantic property.
#
# The semantic type implements the concept of linked data type
# (also called class).
#
# This type is an uri pointing to the location of the linked
# data concept on the web like "http://xmlns.com/foaf/0.1/Person"
# for instance.
#
# This should be a String or nil.
def semanticType=(type)
property = registerSemanticProperty('@id') { semanticId }
property.valueSetter = ->(value) { @semanticId = value }
end

# Sets the semantic type of the object and registers the
# corresponding semantic property.
#
# The semantic type implements the concept of linked data type
# (also called class).
#
# This type is an uri pointing to the location of the linked
# data concept on the web like "http://xmlns.com/foaf/0.1/Person"
# for instance.
#
# This should be a String or nil.
def semanticType=(type)
@semanticType = type
registerSemanticProperty("@type") {self.semanticType}
end

# Serialize all the semantic properties of this object
# to an output format.
#
# You could use the HashSerializer to export as a Hash.
# This Hash should be then exported to JSON for instance.
def serialize(serializer)
return serializer.process(self)
end

protected

# If the semantic property already exist in this object, this
# method will simply update the valueGetter of the property.
#
# If this object does not holds the property, the new property
# will be added into the semanticProperties Array of this object.
def createOrUpdateSemanticProperty(name, valueGetter)
# Update
if (hasSemanticProperty?(name))
semanticProperty = findSemanticProperty(name)
if (semanticProperty != nil)
semanticProperty.valueGetter = valueGetter
end

# Create
else
@semanticProperties.push(VirtualAssembly::Semantizer::SemanticProperty.new(name, &valueGetter))
index = @semanticProperties.count - 1
@semanticPropertiesNameIndex.store(name, index);
end
end

# Given its name, returns the corresponding SemanticProperty
# stored by this object or nil if the property does not exist.
def findSemanticProperty(name)
begin
index = @semanticPropertiesNameIndex.fetch(name)
return @semanticProperties.at(index)
rescue
return nil
end
property = registerSemanticProperty('@type') { semanticType }
property.valueSetter = ->(value) { @semanticType = value }
end

# Serialize all the semantic properties of this object
# to an output format.
#
# You could use the HashSerializer to export as a Hash.
# This Hash should be then exported to JSON for instance.
def serialize(serializer)
serializer.process(self)
end

protected

# If the semantic property already exist in this object, this
# method will simply update the valueGetter of the property.
#
# If this object does not holds the property, the new property
# will be added into the semanticProperties Array of this object.
def createOrUpdateSemanticProperty(name, valueGetter)
# Update
if hasSemanticProperty?(name)
property = semanticProperty(name)
property&.valueGetter = valueGetter

# Create
else
property = VirtualAssembly::Semantizer::SemanticProperty.new(name, &valueGetter)
@semanticPropertiesMap[name] = property
end

end
property
end
end
end
end
72 changes: 43 additions & 29 deletions lib/virtual_assembly/semantizer/semantic_property.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# Copyright © 2023 Maxime Lecoq, <[email protected]>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
Expand All @@ -15,49 +17,61 @@
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# The SemanticPropety class is designed to turn properties of
# The SemanticPropety class is designed to turn properties of
# objects into linked data.
#
# A SemanticProperty has a name and a corresponding value that
# can be fetched later (so its value would be up to date).
#
# This class is intented to be used through the SemanticObject
# This class is intented to be used through the SemanticObject
# class.
#
#
# For instance, we can tell that the name of a Person object refers
# to the linked data concept "name" from the FOAF project. The name
# of the property would be the uri of the FOAF:name property while the
# value would be the attribute reader of the name of the Person object.
#
# You should use a block to pass the value like so:
# SemanticProperty.new("http://xmlns.com/foaf/0.1/name") {self.name}
class VirtualAssembly::Semantizer::SemanticProperty

# The name of the property. It generally points to an uri
# like "http://xmlns.com/foaf/0.1/name" or it is used to
# define a reserved linked data property like "@id".
#
# This should be a String.
attr_accessor :name

# The function to call when the value is requested.
#
# This should be a Proc passed as a Block.
attr_accessor :valueGetter

# @param name The name of the property, like
# "http://xmlns.com/foaf/0.1/name" or "@id" for instance.
#
# @param valueGetter A Proc used to retrieve the value of the
# property when requested.
def initialize(name, &valueGetter)
module VirtualAssembly
module Semantizer
class SemanticProperty
# The name of the property. It generally points to an uri
# like "http://xmlns.com/foaf/0.1/name" or it is used to
# define a reserved linked data property like "@id".
#
# This should be a String.
attr_accessor :name

# The function to call when the value is requested.
#
# This should be a Proc passed as a Block.
attr_accessor :valueGetter

# The function to call to store a new value.
#
# This should be a Proc
attr_accessor :valueSetter

# @param name The name of the property, like
# "http://xmlns.com/foaf/0.1/name" or "@id" for instance.
#
# @param valueGetter A Proc used to retrieve the value of the
# property when requested.
def initialize(name, &valueGetter)
@name = name
@valueGetter = valueGetter
end
end

# Fetch and returns the value associated to this property.
def value
return @valueGetter.call
end
# Fetch and returns the value associated to this property.
def value
@valueGetter.call
end

end
# Stores a new value for this property.
def value=(new_value)
@valueSetter.call(new_value)
end
end
end
end
Loading

0 comments on commit 7019323

Please sign in to comment.