ladder 0.3.1 → 0.3.2
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/.gitignore +4 -1
- data/.semver +5 -0
- data/Gemfile +1 -1
- data/README.md +11 -11
- data/Rakefile +1 -2
- data/ladder.gemspec +26 -23
- data/lib/ladder.rb +1 -1
- data/lib/ladder/file.rb +49 -40
- data/lib/ladder/resource.rb +204 -60
- data/lib/ladder/resource/dynamic.rb +134 -83
- data/lib/ladder/resource/serializable.rb +56 -43
- data/lib/ladder/searchable.rb +14 -12
- data/lib/ladder/searchable/background.rb +40 -31
- data/lib/ladder/searchable/file.rb +33 -26
- data/lib/ladder/searchable/resource.rb +26 -15
- data/lib/ladder/version.rb +2 -2
- data/spec/ladder/file_spec.rb +9 -7
- data/spec/ladder/resource/dynamic_spec.rb +13 -143
- data/spec/ladder/resource_spec.rb +47 -226
- data/spec/ladder/searchable/background_spec.rb +37 -42
- data/spec/ladder/searchable/file_spec.rb +8 -5
- data/spec/ladder/searchable/resource_spec.rb +30 -38
- data/spec/shared/file.rb +9 -9
- data/spec/shared/graph.jsonld +31 -0
- data/spec/shared/resource.rb +397 -14
- data/spec/shared/searchable/file.rb +2 -4
- data/spec/shared/searchable/resource.rb +137 -145
- data/spec/spec_helper.rb +3 -2
- metadata +49 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94262dd2e8eb69badfa00eeb2671a19f8637614c
|
4
|
+
data.tar.gz: 0d2b3f22370d591e2c0aa12ccb803156c9712ba6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8afcec27c1a5f503137ebbfba804f7392fed957a62058d2d1c2a3632121b72607cfb96bc2ef44ebc55049666fe55bc45620de08f93fc1b2b308303191ae51fe
|
7
|
+
data.tar.gz: 2c7025c69534858a30b0c5717bf4575a98f3800f85b92111100f28185b6a891394066eab0bbd75f348658e837d836edcf18b5b58e5b188036878d9366adf4459
|
data/.gitignore
CHANGED
data/.semver
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -64,7 +64,7 @@ end
|
|
64
64
|
|
65
65
|
steve = Person.new(first_name: 'Steve', description: 'Funny-looking')
|
66
66
|
=> #<Person _id: 542f0c124169720ea0000000, first_name: {"en"=>"Steve"}, description: {"en"=>"Funny-looking"}>
|
67
|
-
|
67
|
+
|
68
68
|
steve.as_document
|
69
69
|
=> {"_id"=>BSON::ObjectId('542f0c124169720ea0000000'),
|
70
70
|
"first_name"=>{"en"=>"Steve"},
|
@@ -190,7 +190,7 @@ class Place
|
|
190
190
|
|
191
191
|
property :city, predicate: RDF::VCARD.locality
|
192
192
|
property :country, predicate: RDF::VCARD.send('country-name')
|
193
|
-
|
193
|
+
|
194
194
|
embedded_in :resident, class_name: 'Person', inverse_of: :address
|
195
195
|
property :resident, predicate: RDF::VCARD.agent
|
196
196
|
end
|
@@ -448,8 +448,8 @@ results = Person.search 'shay'
|
|
448
448
|
# #<Elasticsearch::Model::Searching::SearchRequest:0x007fa2ca830a58
|
449
449
|
# @definition={:index=>"people", :type=>"person", :q=>"Shay"},
|
450
450
|
# @klass=[PROXY] Person,
|
451
|
-
# @
|
452
|
-
|
451
|
+
# @params={}>>
|
452
|
+
|
453
453
|
results.count
|
454
454
|
=> 1
|
455
455
|
|
@@ -677,8 +677,8 @@ results = OCR.search 'Moomintroll'
|
|
677
677
|
# #<Elasticsearch::Model::Searching::SearchRequest:0x007fa2ca830a58
|
678
678
|
# @definition={:index=>"ocrs", :type=>"ocr", :q=>"Moomintroll"},
|
679
679
|
# @klass=[PROXY] OCR,
|
680
|
-
# @
|
681
|
-
|
680
|
+
# @params={}>>
|
681
|
+
|
682
682
|
results.count
|
683
683
|
=> 1
|
684
684
|
|
@@ -708,8 +708,8 @@ results = OCR.search 'Moomintroll', fields: '*'
|
|
708
708
|
# #<Elasticsearch::Model::Searching::SearchRequest:0x007fc36cadab10
|
709
709
|
# @definition={:index=>"ocrs", :type=>"ocr", :body=>{:query=>{:query_string=>{:query=>"Moomintroll"}}, :fields=>"*"}},
|
710
710
|
# @klass=[PROXY] OCR,
|
711
|
-
# @
|
712
|
-
|
711
|
+
# @params={}>>
|
712
|
+
|
713
713
|
results.count
|
714
714
|
=> 1
|
715
715
|
|
@@ -734,8 +734,8 @@ results = OCR.search query: { query_string: { query: 'his' } }, highlight: { fie
|
|
734
734
|
# @definition={:index=>"ocrs", :type=>"ocr", :body=>{:query=>{:query_string=>"Moomintroll"},
|
735
735
|
# :highlight=>{:fields=>{:file=>{}}}}},
|
736
736
|
# @klass=[PROXY] OCR,
|
737
|
-
# @
|
738
|
-
|
737
|
+
# @params={}>>
|
738
|
+
|
739
739
|
results.count
|
740
740
|
=> 1
|
741
741
|
|
@@ -760,7 +760,7 @@ class OCR
|
|
760
760
|
end
|
761
761
|
|
762
762
|
# ...
|
763
|
-
|
763
|
+
|
764
764
|
class Person
|
765
765
|
include Ladder::Resource
|
766
766
|
include Ladder::Searchable::Background
|
data/Rakefile
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/gem_tasks'
|
data/ladder.gemspec
CHANGED
@@ -4,33 +4,36 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'ladder/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'ladder'
|
8
8
|
spec.version = Ladder::VERSION
|
9
9
|
spec.platform = Gem::Platform::RUBY
|
10
|
-
spec.authors =
|
11
|
-
spec.email =
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
10
|
+
spec.authors = 'MJ Suhonos'
|
11
|
+
spec.email = 'mj@suhonos.ca'
|
12
|
+
spec.summary = 'ActiveModel Linked Data framework.'
|
13
|
+
spec.description = 'Dynamic framework for Linked Data modelling, persistence, and full-text indexing.'
|
14
|
+
spec.homepage = 'https://github.com/ladder/ladder'
|
15
|
+
spec.license = 'APACHE2'
|
16
16
|
spec.required_ruby_version = '>= 2.0.0'
|
17
17
|
|
18
18
|
spec.files = `git ls-files -z`.split("\x0")
|
19
|
-
spec.executables = spec.files.grep(
|
20
|
-
spec.test_files = spec.files.grep(
|
21
|
-
spec.require_paths = [
|
19
|
+
spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)/)
|
21
|
+
spec.require_paths = ['lib']
|
22
22
|
|
23
|
-
spec.add_dependency
|
24
|
-
spec.add_dependency
|
25
|
-
spec.add_dependency
|
26
|
-
spec.add_dependency
|
27
|
-
spec.add_dependency
|
23
|
+
spec.add_dependency 'active-triples', '~> 0.6'
|
24
|
+
spec.add_dependency 'activejob', '~> 4.2'
|
25
|
+
spec.add_dependency 'elasticsearch-model', '~> 0.1'
|
26
|
+
spec.add_dependency 'mongoid', '~> 4.0'
|
27
|
+
spec.add_dependency 'mongoid-grid_fs', '~> 2.1'
|
28
28
|
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
spec.add_development_dependency
|
33
|
-
spec.add_development_dependency
|
34
|
-
spec.add_development_dependency
|
35
|
-
spec.add_development_dependency
|
36
|
-
|
29
|
+
spec.add_development_dependency 'awesome_print', '~> 1.6'
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
31
|
+
spec.add_development_dependency 'mimemagic', '~> 0.2'
|
32
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
33
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
34
|
+
spec.add_development_dependency 'rubocop', '~> 0.29'
|
35
|
+
spec.add_development_dependency 'semver', '~> 1.0'
|
36
|
+
spec.add_development_dependency 'simplecov', '~> 0.9'
|
37
|
+
spec.add_development_dependency 'wirble', '~> 0.1'
|
38
|
+
spec.add_development_dependency 'yard', '~> 0.8'
|
39
|
+
end
|
data/lib/ladder.rb
CHANGED
data/lib/ladder/file.rb
CHANGED
@@ -1,62 +1,71 @@
|
|
1
1
|
require 'mongoid/grid_fs'
|
2
2
|
require 'active_triples'
|
3
3
|
|
4
|
-
module Ladder
|
5
|
-
|
4
|
+
module Ladder
|
5
|
+
module File
|
6
|
+
extend ActiveSupport::Concern
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
include Mongoid::Document
|
9
|
+
include ActiveTriples::Identifiable
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
included do
|
12
|
+
configure base_uri: RDF::URI.new(LADDER_BASE_URI) / name.underscore.pluralize if defined? LADDER_BASE_URI
|
12
13
|
|
13
|
-
|
14
|
+
store_in collection: "#{ grid.prefix }.files"
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
# Define accessor methods for attributes
|
17
|
+
define_method(:content_type) { read_attribute(:contentType) }
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
grid::File.fields.keys.map(&:to_sym).each do |attr|
|
20
|
+
define_method(attr) { read_attribute(attr) }
|
21
|
+
end
|
22
|
+
|
23
|
+
around_save :save_file
|
20
24
|
end
|
21
25
|
|
22
|
-
|
23
|
-
end
|
26
|
+
attr_accessor :file
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
##
|
29
|
+
# Output content of object from stored file or readable input
|
30
|
+
#
|
31
|
+
# @return [String] string-encoded copy of binary data
|
32
|
+
def data
|
33
|
+
@grid_file ||= self.class.grid.get(id) if persisted?
|
34
|
+
return @grid_file.data if @grid_file
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
resource
|
41
|
-
|
42
|
-
|
43
|
-
|
36
|
+
file.rewind if file.respond_to? :rewind
|
37
|
+
file.read
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Return an empty ActiveTriples resource for serializing related resources
|
42
|
+
#
|
43
|
+
# @return [ActiveTriples::Resource] resource for the object
|
44
|
+
def update_resource
|
45
|
+
resource
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
44
49
|
|
45
50
|
##
|
46
51
|
# Make save behave like Mongoid::Document as much as possible
|
47
|
-
|
52
|
+
#
|
53
|
+
# @return result of #run_callbacks, @see ActiveSupport::Callbacks
|
54
|
+
def save_file
|
48
55
|
attributes[:content_type] = file.content_type if file.respond_to? :content_type
|
49
|
-
@grid_file ? @grid_file.save :
|
56
|
+
@grid_file ? @grid_file.save : @grid_file = self.class.grid.put(file, attributes.symbolize_keys)
|
50
57
|
|
51
58
|
persisted? ? run_callbacks(:update) : run_callbacks(:create)
|
52
59
|
end
|
53
60
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
61
|
+
module ClassMethods
|
62
|
+
##
|
63
|
+
# Create a namespaced GridFS module for this class
|
64
|
+
#
|
65
|
+
# @return [Module] a Mongoid::GridFs module for this class
|
66
|
+
def grid
|
67
|
+
@grid ||= Mongoid::GridFs.build_namespace_for name
|
68
|
+
end
|
59
69
|
end
|
60
70
|
end
|
61
|
-
|
62
|
-
end
|
71
|
+
end
|
data/lib/ladder/resource.rb
CHANGED
@@ -1,56 +1,112 @@
|
|
1
1
|
require 'mongoid'
|
2
2
|
require 'active_triples'
|
3
3
|
|
4
|
-
module Ladder
|
5
|
-
|
6
|
-
|
4
|
+
module Ladder
|
5
|
+
module Resource
|
6
|
+
autoload :Dynamic, 'ladder/resource/dynamic'
|
7
|
+
autoload :Serializable, 'ladder/resource/serializable'
|
7
8
|
|
8
|
-
|
9
|
+
extend ActiveSupport::Concern
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
include Mongoid::Document
|
12
|
+
include ActiveTriples::Identifiable
|
13
|
+
include Ladder::Resource::Serializable
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
included do
|
16
|
+
configure base_uri: RDF::URI.new(LADDER_BASE_URI) / name.underscore.pluralize if defined? LADDER_BASE_URI
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
+
delegate :rdf_label, to: :update_resource
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
##
|
22
|
+
# Update the delegated ActiveTriples::Resource from
|
23
|
+
# ActiveModel properties & relations
|
24
|
+
#
|
25
|
+
# @param [Hash] opts options to pass to Mongoid / ActiveTriples
|
26
|
+
# @option opts [Boolean] :related whether to include related resources
|
27
|
+
# @return [ActiveTriples::Resource] resource for the object
|
28
|
+
def update_resource(opts = {})
|
29
|
+
resource_class.properties.each do |field_name, property|
|
30
|
+
value = update_from_field(field_name) if fields[field_name]
|
31
|
+
value = update_from_relation(field_name, opts[:related]) if relations[field_name]
|
32
|
+
|
33
|
+
resource.set_value(property.predicate, value) # if value
|
34
|
+
end
|
26
35
|
|
27
|
-
resource
|
36
|
+
resource
|
28
37
|
end
|
29
38
|
|
30
|
-
|
31
|
-
|
39
|
+
##
|
40
|
+
# Push an RDF::Statement into the object
|
41
|
+
#
|
42
|
+
# @param [RDF::Statement, Hash, Array] statement @see RDF::Statement#from
|
43
|
+
# @return [void]
|
44
|
+
def <<(statement)
|
45
|
+
# ActiveTriples::Resource expects: RDF::Statement, Hash, or Array
|
46
|
+
statement = RDF::Statement.from(statement) unless statement.is_a? RDF::Statement
|
47
|
+
|
48
|
+
# Only push statement if the statement's predicate is defined on the class
|
49
|
+
field_name = field_from_predicate(statement.predicate)
|
50
|
+
return unless field_name
|
51
|
+
|
52
|
+
# If the object is a URI, see if it is a retrievable model object
|
53
|
+
value = Ladder::Resource.from_uri(statement.object) if statement.object.is_a? RDF::URI
|
54
|
+
|
55
|
+
# TODO: tidy this code
|
56
|
+
# subject (RDF::Term) - A symbol is converted to an interned Node.
|
57
|
+
# predicate (RDF::URI)
|
58
|
+
# object (RDF::Resource) - if not a Resource, it is coerced to Literal or Node
|
59
|
+
# depending on if it is a symbol or something other than a Term.
|
60
|
+
value = yield if block_given?
|
61
|
+
value ||= statement.object.to_s
|
62
|
+
|
63
|
+
enum = send(field_name)
|
64
|
+
|
65
|
+
if enum.is_a?(Enumerable)
|
66
|
+
enum.send(:push, value) unless enum.include? value
|
67
|
+
else
|
68
|
+
send("#{field_name}=", value)
|
69
|
+
end
|
70
|
+
end
|
32
71
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
#
|
37
|
-
|
72
|
+
##
|
73
|
+
# Retrieve the class for a relation, based on its defined RDF predicate
|
74
|
+
#
|
75
|
+
# @param [RDF::URI] predicate a URI for the RDF::Term
|
76
|
+
# @return [Ladder::Resource, Ladder::File, nil] related class
|
77
|
+
def klass_from_predicate(predicate)
|
78
|
+
field_name = field_from_predicate(predicate)
|
79
|
+
return unless field_name
|
38
80
|
|
39
|
-
|
40
|
-
|
41
|
-
field_name = resource_class.properties.select { |name, term| term.predicate == data.predicate }.keys.first.to_sym
|
81
|
+
relation = relations[field_name]
|
82
|
+
return unless relation
|
42
83
|
|
43
|
-
|
44
|
-
value = data.object.is_a?(RDF::Literal) ? data.object.object : data.object.to_s
|
45
|
-
self.send("#{field_name}=", value)
|
84
|
+
relation.class_name.constantize
|
46
85
|
end
|
47
|
-
end
|
48
86
|
|
49
|
-
|
87
|
+
private
|
88
|
+
|
89
|
+
##
|
90
|
+
# Retrieve the attribute name for a field or relation,
|
91
|
+
# based on its defined RDF predicate
|
92
|
+
#
|
93
|
+
# @param [RDF::URI] predicate a URI for the RDF::Term
|
94
|
+
# @return [String, nil] name for the attribute
|
95
|
+
def field_from_predicate(predicate)
|
96
|
+
defined_prop = resource_class.properties.find { |_field_name, term| term.predicate == predicate }
|
97
|
+
return unless defined_prop
|
98
|
+
|
99
|
+
defined_prop.first
|
100
|
+
end
|
50
101
|
|
51
|
-
|
52
|
-
|
53
|
-
|
102
|
+
##
|
103
|
+
# Update the delegated ActiveTriples::Resource from a field
|
104
|
+
#
|
105
|
+
# @param [String] field_name ActiveModel attribute name for the field
|
106
|
+
# @return [void]
|
107
|
+
def update_from_field(field_name)
|
108
|
+
if fields[field_name].localized?
|
109
|
+
localized_hash = read_attribute(field_name)
|
54
110
|
|
55
111
|
unless localized_hash.nil?
|
56
112
|
localized_hash.map do |lang, value|
|
@@ -59,20 +115,26 @@ module Ladder::Resource
|
|
59
115
|
end
|
60
116
|
end
|
61
117
|
else
|
62
|
-
|
118
|
+
send(field_name)
|
63
119
|
end
|
64
120
|
end
|
65
|
-
|
66
|
-
def update_from_relation(name, opts = {})
|
67
|
-
objects = self.send(name).to_a
|
68
121
|
|
69
|
-
|
122
|
+
##
|
123
|
+
# Update the delegated ActiveTriples::Resource from a relation
|
124
|
+
#
|
125
|
+
# @param [String] field_name ActiveModel attribute name for the relation
|
126
|
+
# @param [Boolean] related whether to include related objects
|
127
|
+
# @return [void]
|
128
|
+
def update_from_relation(field_name, related = false)
|
129
|
+
objects = send(field_name).to_a
|
130
|
+
|
131
|
+
if related || embedded_relations[field_name]
|
70
132
|
# Force autosave of related documents to ensure correct serialization
|
71
|
-
methods.select{|i| i[/autosave_documents/] }.each{|m| send m}
|
133
|
+
methods.select { |i| i[/autosave_documents/] }.each { |m| send m }
|
72
134
|
|
73
135
|
# update inverse relation properties
|
74
|
-
relation_def = relations[
|
75
|
-
objects.each { |object| object.resource.set_value(relation_def.inverse,
|
136
|
+
relation_def = relations[field_name]
|
137
|
+
objects.each { |object| object.resource.set_value(relation_def.inverse, rdf_subject) } if relation_def.inverse
|
76
138
|
objects.map(&:update_resource)
|
77
139
|
else
|
78
140
|
# remove inverse relation properties
|
@@ -81,26 +143,108 @@ module Ladder::Resource
|
|
81
143
|
end
|
82
144
|
end
|
83
145
|
|
84
|
-
|
146
|
+
public
|
147
|
+
|
148
|
+
module ClassMethods
|
149
|
+
##
|
150
|
+
# Define a Mongoid field/relation on the class as well as
|
151
|
+
# an RDF property on the delegated resource
|
152
|
+
#
|
153
|
+
# @see ActiveTriples::Resource#property
|
154
|
+
# @see ActiveTriples::Properties
|
155
|
+
#
|
156
|
+
# @param [String] field_name ActiveModel attribute name for the field
|
157
|
+
# @param [Hash] opts options to pass to Mongoid / ActiveTriples
|
158
|
+
# @return [ActiveTriples::Resource] a modified resource
|
159
|
+
def property(field_name, opts = {})
|
160
|
+
if opts[:class_name]
|
161
|
+
mongoid_opts = { autosave: true, index: true }.merge(opts.except(:predicate, :multivalue))
|
162
|
+
has_and_belongs_to_many(field_name, mongoid_opts) unless relations.keys.include? field_name.to_s
|
163
|
+
else
|
164
|
+
mongoid_opts = { localize: true }.merge(opts.except(:predicate, :multivalue))
|
165
|
+
field(field_name, mongoid_opts) unless fields[field_name.to_s]
|
166
|
+
end
|
167
|
+
|
168
|
+
opts.except!(*mongoid_opts.keys)
|
169
|
+
|
170
|
+
super
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Create a new instance of this class, populated with values
|
175
|
+
# and related objects from a given RDF::Graph for this model.
|
176
|
+
#
|
177
|
+
# By default, the graph will be traversed starting with the first
|
178
|
+
# node that matches the same RDF.type as this class; however, an
|
179
|
+
# optional RDF::Queryable pattern can be provided, @see RDF::Queryable#query
|
180
|
+
#
|
181
|
+
# As nodes are traversed in the graph, the instantiated objects
|
182
|
+
# will be added to a Hash that is passed recursively, in order to
|
183
|
+
# prevent infinite traversal in the case of cyclic graphs.
|
184
|
+
#
|
185
|
+
# @param [RDF::Graph] graph an RDF::Graph to traverse
|
186
|
+
# @param [Hash] objects a keyed Hash of already-created objects in the graph
|
187
|
+
# @param [RDF::Query, RDF::Statement, Array(RDF::Term), Hash] pattern a query pattern
|
188
|
+
# @return [Ladder::Resource, nil] an instance of this class
|
189
|
+
def new_from_graph(graph, objects = {}, pattern = nil)
|
190
|
+
# Default to getting the first object in the graph with the same RDF type as this class
|
191
|
+
pattern ||= [nil, RDF.type, resource_class.type]
|
192
|
+
|
193
|
+
root_subject = graph.query(pattern).first_subject
|
194
|
+
return unless root_subject
|
195
|
+
|
196
|
+
# If the subject is an existing model, just retrieve it
|
197
|
+
new_object = Ladder::Resource.from_uri(root_subject) if root_subject.is_a? RDF::URI
|
198
|
+
new_object ||= new
|
199
|
+
|
200
|
+
# Add object to stack for recursion
|
201
|
+
objects[root_subject] = new_object
|
202
|
+
|
203
|
+
graph.query([root_subject]).each_statement do |statement|
|
204
|
+
next if objects[statement.object]
|
205
|
+
|
206
|
+
# TODO: If the object is a list, process members individually
|
207
|
+
# list = RDF::List.new statement.object, graph
|
208
|
+
# binding.pry unless list.empty?
|
209
|
+
|
210
|
+
# If the object is a BNode, dereference the relation
|
211
|
+
if statement.object.is_a? RDF::Node
|
212
|
+
klass = new_object.klass_from_predicate(statement.predicate)
|
213
|
+
next unless klass
|
214
|
+
|
215
|
+
object = klass.new_from_graph(graph, objects)
|
216
|
+
next unless object
|
217
|
+
|
218
|
+
objects[statement.object] = object
|
219
|
+
new_object.send(:<<, statement) { object }
|
220
|
+
else
|
221
|
+
new_object << statement
|
222
|
+
end
|
223
|
+
end # end each_statement
|
224
|
+
|
225
|
+
new_object
|
226
|
+
end
|
227
|
+
end
|
85
228
|
|
86
|
-
module ClassMethods
|
87
|
-
|
88
229
|
##
|
89
|
-
#
|
230
|
+
# Return a persisted instance of a Ladder::Resource from its
|
231
|
+
# RDF subject URI, without knowing the resource class.
|
232
|
+
#
|
233
|
+
# If there is no persisted instance for the URI, but the class
|
234
|
+
# is identifiable, then return a new instance of that class
|
90
235
|
#
|
91
|
-
# @
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
236
|
+
# @param [RDF::URI] uri RDF subject URI for the resource
|
237
|
+
# @return [Ladder::Resource] a resource instance
|
238
|
+
def self.from_uri(uri)
|
239
|
+
klasses = ActiveTriples::Resource.descendants.select(&:name)
|
240
|
+
klass = klasses.find { |k| uri.to_s.include? k.base_uri.to_s }
|
96
241
|
|
97
|
-
|
98
|
-
|
99
|
-
field(name, localize: true) unless fields[name.to_s]
|
100
|
-
end
|
242
|
+
if klass
|
243
|
+
object_id = uri.to_s.match(/[0-9a-fA-F]{24}/).to_s
|
101
244
|
|
102
|
-
|
245
|
+
# Retrieve the object if it's persisted, otherwise return a new one (eg. embedded)
|
246
|
+
return klass.parent.where(id: object_id).exists? ? klass.parent.find(object_id) : klass.parent.new
|
247
|
+
end
|
103
248
|
end
|
104
249
|
end
|
105
|
-
|
106
|
-
end
|
250
|
+
end
|