Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Update resource settings to use a YAML file to get attributes metadata & implement onUpdate callback #151

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.7.8
4 changes: 3 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ GEM

PLATFORMS
x86_64-darwin-18
x86_64-darwin-23
x86_64-linux

DEPENDENCIES
activesupport
Expand All @@ -139,4 +141,4 @@ DEPENDENCIES
uuid

BUNDLED WITH
2.3.15
2.4.22
3 changes: 2 additions & 1 deletion goo.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Gem::Specification.new do |s|
s.email = "[email protected]"
s.files = Dir["lib/**/*.rb"]
s.homepage = "http://github.com/ncbo/goo"
s.add_dependency("addressable", "~> 2.8")

s.add_dependency("addressable", "~> 2.8")
s.add_dependency("pry")
s.add_dependency("rdf", "= 1.0.8")
s.add_dependency("redis")
Expand Down
82 changes: 40 additions & 42 deletions lib/goo/base/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ def valid?
self.class.attributes.each do |attr|
inst_value = self.instance_variable_get("@#{attr}")
attr_errors = Goo::Validators::Enforce.enforce(self,attr,inst_value)
unless attr_errors.nil?
validation_errors[attr] = attr_errors
end
validation_errors[attr] = attr_errors unless attr_errors.nil?
end

if !@persistent && validation_errors.length == 0
Expand All @@ -70,9 +68,7 @@ def valid?
end

def id=(new_id)
if [email protected]? and @persistent
raise ArgumentError, "The id of a persistent object cannot be changed."
end
raise ArgumentError, "The id of a persistent object cannot be changed." if [email protected]? and @persistent
raise ArgumentError, "ID must be an RDF::URI" unless new_id.kind_of?(RDF::URI)
@id = new_id
end
Expand Down Expand Up @@ -128,6 +124,7 @@ def unmapped_set(attribute,value)

def unmmaped_to_array
cpy = {}

@unmapped.each do |attr,v|
cpy[attr] = v.to_a
end
Expand All @@ -136,19 +133,15 @@ def unmmaped_to_array

def delete(*args)
if self.kind_of?(Goo::Base::Enum)
unless args[0] && args[0][:init_enum]
raise ArgumentError, "Enums cannot be deleted"
end
raise ArgumentError, "Enums cannot be deleted" unless args[0] && args[0][:init_enum]
end

raise ArgumentError, "This object is not persistent and cannot be deleted" if !@persistent

if !fully_loaded?
missing = missing_load_attributes
options_load = { models: [ self ], klass: self.class, :include => missing }
if self.class.collection_opts
options_load[:collection] = self.collection
end
options_load[:collection] = self.collection if self.class.collection_opts
Goo::SPARQL::Queries.model_load(options_load)
end

Expand All @@ -164,25 +157,19 @@ def delete(*args)
end
@persistent = false
@modified = true
if self.class.inmutable? && self.class.inm_instances
self.class.load_inmutable_instances
end
self.class.load_inmutable_instances if self.class.inmutable? && self.class.inm_instances
return nil
end

def bring(*opts)
opts.each do |k|
if k.kind_of?(Hash)
k.each do |k2,v|
if self.class.handler?(k2)
raise ArgumentError, "Unable to bring a method based attr #{k2}"
end
raise ArgumentError, "Unable to bring a method based attr #{k2}" if self.class.handler?(k2)
self.instance_variable_set("@#{k2}",nil)
end
else
if self.class.handler?(k)
raise ArgumentError, "Unable to bring a method based attr #{k}"
end
raise ArgumentError, "Unable to bring a method based attr #{k}" if self.class.handler?(k)
self.instance_variable_set("@#{k}",nil)
end
end
Expand All @@ -197,9 +184,7 @@ def bring(*opts)

def graph
opts = self.class.collection_opts
if opts.nil?
return self.class.uri_type
end
return self.class.uri_type if opts.nil?
col = collection
if col.is_a?Array
if col.length == 1
Expand Down Expand Up @@ -281,9 +266,7 @@ def collection
if opts.instance_of?(Symbol)
if self.class.attributes.include?(opts)
value = self.send("#{opts}")
if value.nil?
raise ArgumentError, "Collection `#{opts}` is nil"
end
raise ArgumentError, "Collection `#{opts}` is nil" if value.nil?
return value
else
raise ArgumentError, "Collection `#{opts}` is not an attribute"
Expand All @@ -298,26 +281,45 @@ def add_aggregate(attribute,aggregate,value)
def save(*opts)

if self.kind_of?(Goo::Base::Enum)
unless opts[0] && opts[0][:init_enum]
raise ArgumentError, "Enums can only be created on initialization"
end
raise ArgumentError, "Enums can only be created on initialization" unless opts[0] && opts[0][:init_enum]
end
batch_file = nil
if opts && opts.length > 0
if opts.first.is_a?(Hash) && opts.first[:batch] && opts.first[:batch].is_a?(File)
callbacks = true
if opts && opts.length > 0 && opts.first.is_a?(Hash)
if opts.first[:batch] && opts.first[:batch].is_a?(File)
batch_file = opts.first[:batch]
end

callbacks = opts.first[:callbacks]
end

if !batch_file
if not modified?
return self
end
return self if not modified?
raise Goo::Base::NotValidException, "Object is not valid. Check errors." unless valid?
end

#set default values before saving
unless self.persistent?
self.class.attributes_with_defaults.each do |attr|
value = self.send("#{attr}")
if value.nil?
value = self.class.default(attr).call(self)
self.send("#{attr}=", value)
end
end
end

#call update callback before saving
if callbacks
self.class.attributes_with_update_callbacks.each do |attr|
Goo::Validators::Enforce.enforce_callbacks(self, attr)
end
end

graph_insert, graph_delete = Goo::SPARQL::Triples.model_update_triples(self)
graph = self.graph()
graph = self.graph


if graph_delete and graph_delete.size > 0
begin
Goo.sparql_update_client.delete_data(graph_delete, graph: graph)
Expand Down Expand Up @@ -351,9 +353,7 @@ def save(*opts)

@modified_attributes = Set.new
@persistent = true
if self.class.inmutable? && self.class.inm_instances
self.class.load_inmutable_instances
end
self.class.load_inmutable_instances if self.class.inmutable? && self.class.inm_instances
return self
end

Expand Down Expand Up @@ -391,9 +391,7 @@ def to_hash
end
end
@unmapped.each do |attr,values|
unless all_attr_uris.include?(attr)
attr_hash[attr] = values.map { |v| v.to_s }
end
attr_hash[attr] = values.map { |v| v.to_s } unless all_attr_uris.include?(attr)
end
end
attr_hash[:id] = @id
Expand Down
50 changes: 41 additions & 9 deletions lib/goo/base/settings/settings.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'active_support/core_ext/string'
require_relative 'yaml_settings'

module Goo
module Base
Expand All @@ -12,8 +13,10 @@ module ClassMethods
attr_reader :model_name
attr_reader :attribute_uris

include YAMLScheme

def default_model_options
return {}
{}
end

def model(*args)
Expand All @@ -34,7 +37,9 @@ def model(*args)

@model_settings = default_model_options.merge(options || {})

unless options.include?:name_with
init_yaml_scheme_settings

unless options.include? :name_with
raise ArgumentError, "The model `#{model_name}` definition should include the :name_with option"
end
Goo.add_model(@model_name,self)
Expand Down Expand Up @@ -91,6 +96,16 @@ def attributes_with_defaults
select{ |attr,opts| opts[:default] }).keys()
end

def attributes_with_update_callbacks
(@model_settings[:attributes].
select{ |attr,opts| opts[:onUpdate] }).keys
end


def update_callbacks(attr)
@model_settings[:attributes][attr][:onUpdate]
end

def default(attr)
return @model_settings[:attributes][attr][:default]
end
Expand Down Expand Up @@ -185,10 +200,14 @@ def attribute(*args)
attr_name = attr_name.to_sym
options = options.pop
options = {} if options.nil?
if options[:enforce].nil? or !options[:enforce].include?(:list)
options[:enforce] = options[:enforce] ? (options[:enforce] << :no_list) : [:no_list]
end

options[:enforce] ||= []

set_data_type(options)
set_no_list_by_default(options)

@model_settings[:attributes][attr_name] = options
load_yaml_scheme_options(attr_name)
shape_attribute(attr_name)
namespace = attribute_namespace(attr_name)
namespace = namespace || @model_settings[:namespace]
Expand Down Expand Up @@ -232,15 +251,13 @@ def shape_attribute(attr)
raise ArgumentError, "Method based attributes cannot be set"
end
if self.class.inverse?(attr) && !(args && args.last.instance_of?(Hash) && args.last[:on_load])
raise ArgumentError,
"`#{attr}` is an inverse attribute. Values cannot be assigned."
raise ArgumentError, "`#{attr}` is an inverse attribute. Values cannot be assigned."
end
@loaded_attributes.add(attr)
value = args[0]
unless args.last.instance_of?(Hash) and args.last[:on_load]
if self.persistent? and self.class.name_with == attr
raise ArgumentError,
"`#{attr}` attribute is used to name this resource and cannot be modified."
raise ArgumentError, "`#{attr}` attribute is used to name this resource and cannot be modified."
end
prev = self.instance_variable_get("@#{attr}")
if !prev.nil? and !@modified_attributes.include?(attr)
Expand Down Expand Up @@ -372,6 +389,21 @@ def read_only(attributes)
instance
end


private

def set_no_list_by_default(options)
if options[:enforce].nil? or !options[:enforce].include?(:list)
options[:enforce] = options[:enforce] ? (options[:enforce] << :no_list) : [:no_list]
end
end
def set_data_type(options)
if options[:type]
options[:enforce] += Array(options[:type])
options[:enforce].uniq!
options.delete :type
end
end
end
end
end
Expand Down
45 changes: 45 additions & 0 deletions lib/goo/base/settings/yaml_settings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'yaml'

module Goo
module Base
module Settings
module YAMLScheme
attr_reader :yaml_settings

def init_yaml_scheme_settings
scheme_file_path = @model_settings[:scheme]
@yaml_settings = read_yaml_settings_file(scheme_file_path)
end

def attribute_yaml_settings(attr)

return {} if yaml_settings.nil?

yaml_settings[attr.to_sym]
end



private

def load_yaml_scheme_options(attr)
settings = attribute_settings(attr)
yaml_settings = attribute_yaml_settings(attr)
settings.merge! yaml_settings unless yaml_settings.nil? || yaml_settings.empty?
end

def read_yaml_settings_file(scheme_file_path)
return if scheme_file_path.nil?

yaml_contents = File.read(scheme_file_path) rescue return

YAML.safe_load(yaml_contents, symbolize_names: true)
end
end
end
end
end




17 changes: 11 additions & 6 deletions lib/goo/sparql/query_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -311,13 +311,18 @@ def query_filter_sparql(klass, filter, filter_patterns, filter_graphs,
filter_var = inspected_patterns[filter_pattern_match]

if !filter_operation.value.instance_of?(Goo::Filter)
if filter_operation.operator == :unbound || filter_operation.operator == :bound
if filter_operation.operator == :unbound
filter_operations << "!BOUND(?#{filter_var.to_s})"
else
filter_operations << "BOUND(?#{filter_var.to_s})"
end
case filter_operation.operator
when :unbound
filter_operations << "!BOUND(?#{filter_var.to_s})"
return :optional

when :bound
filter_operations << "BOUND(?#{filter_var.to_s})"
return :optional
when :regex
if filter_operation.value.is_a?(String)
filter_operations << "REGEX(STR(?#{filter_var.to_s}) , \"#{filter_operation.value.to_s}\", \"i\")"
end
else
value = RDF::Literal.new(filter_operation.value)
if filter_operation.value.is_a? String
Expand Down
1 change: 1 addition & 0 deletions lib/goo/sparql/solutions_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def initialize(aggregate_projections, bnode_extraction, embed_struct,
@incl = options[:include]
@count = options[:count]
@collection = options[:collection]
@options = options
end

def map_each_solutions(select)
Expand Down
Loading