ladder 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8707de1da44b6fb2a49b9e1ddf36be993201e812
4
- data.tar.gz: 9629dc901be61cd1bfa50fa6af34b3e667143e87
3
+ metadata.gz: 94262dd2e8eb69badfa00eeb2671a19f8637614c
4
+ data.tar.gz: 0d2b3f22370d591e2c0aa12ccb803156c9712ba6
5
5
  SHA512:
6
- metadata.gz: 21d4977c566c56b2b33c613c015b6db8a34772b9d96b7532edcbcb9579766002c9ee3f567ad1f697671eff87b2c57d3a0ecd2e8966e2ef3f3c3e61f3c1d1dc3b
7
- data.tar.gz: d877c3834d31420eca16bf1f774867caa40ecf3d3460bfcd2bd4dbe09ecbfb4d8e685e42c55606d22fc8bd5fb10c5dfd407c30fc6b002e0d1dc2aa8474227abe
6
+ metadata.gz: f8afcec27c1a5f503137ebbfba804f7392fed957a62058d2d1c2a3632121b72607cfb96bc2ef44ebc55049666fe55bc45620de08f93fc1b2b308303191ae51fe
7
+ data.tar.gz: 2c7025c69534858a30b0c5717bf4575a98f3800f85b92111100f28185b6a891394066eab0bbd75f348658e837d836edcf18b5b58e5b188036878d9366adf4459
data/.gitignore CHANGED
@@ -12,4 +12,7 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
- *.gem
15
+ *.gem
16
+ .rubocop_todo.yml
17
+ .rubocop.yml
18
+ .ruby-version
data/.semver ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 3
4
+ :patch: 2
5
+ :special: ''
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gemspec
3
+ gemspec
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
- # @options={}>>
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
- # @options={}>>
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
- # @options={}>>
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
- # @options={}>>
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 "bundler/gem_tasks"
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 = "ladder"
7
+ spec.name = 'ladder'
8
8
  spec.version = Ladder::VERSION
9
9
  spec.platform = Gem::Platform::RUBY
10
- spec.authors = "MJ Suhonos"
11
- spec.email = "mj@suhonos.ca"
12
- spec.summary = %q{ActiveModel Linked Data framework.}
13
- spec.description = %q{Dynamic framework for Linked Data modelling, persistence, and full-text indexing.}
14
- spec.homepage = "https://github.com/ladder/ladder"
15
- spec.license = "APACHE2"
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(%r{^bin/}) { |f| File.basename(f) }
20
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
- spec.require_paths = ["lib"]
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 "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"
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 "bundler", "~> 1.7"
30
- spec.add_development_dependency "mimemagic", "~> 0.2"
31
- spec.add_development_dependency "pry", "~> 0.10"
32
- spec.add_development_dependency "rspec", "~> 3.1"
33
- spec.add_development_dependency "simplecov", "~> 0.9"
34
- spec.add_development_dependency "wirble", "~> 0.1"
35
- spec.add_development_dependency "yard", "~> 0.8"
36
- end
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
@@ -1,4 +1,4 @@
1
- require "ladder/version"
1
+ require 'ladder/version'
2
2
 
3
3
  module Ladder
4
4
  autoload :File, 'ladder/file'
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::File
5
- extend ActiveSupport::Concern
4
+ module Ladder
5
+ module File
6
+ extend ActiveSupport::Concern
6
7
 
7
- include Mongoid::Document
8
- include ActiveTriples::Identifiable
8
+ include Mongoid::Document
9
+ include ActiveTriples::Identifiable
9
10
 
10
- included do
11
- configure base_uri: RDF::URI.new(LADDER_BASE_URI) / name.underscore.pluralize if defined? LADDER_BASE_URI
11
+ included do
12
+ configure base_uri: RDF::URI.new(LADDER_BASE_URI) / name.underscore.pluralize if defined? LADDER_BASE_URI
12
13
 
13
- store_in collection: "#{ grid.prefix }.files"
14
+ store_in collection: "#{ grid.prefix }.files"
14
15
 
15
- # Define accessor methods for attributes
16
- define_method(:content_type) { read_attribute(:contentType) }
16
+ # Define accessor methods for attributes
17
+ define_method(:content_type) { read_attribute(:contentType) }
17
18
 
18
- grid::File.fields.keys.map(&:to_sym).each do |attr|
19
- define_method(attr) { read_attribute(attr) }
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
- around_save :save_file
23
- end
26
+ attr_accessor :file
24
27
 
25
- attr_accessor :file
26
-
27
- ##
28
- # Output content of object from stored file or readable input
29
- def data
30
- @grid_file ||= self.class.grid.get(id) if persisted?
31
- return @grid_file.data if @grid_file
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
- file.rewind if file.respond_to? :rewind
34
- file.read
35
- end
36
-
37
- ##
38
- # Return an empty ActiveTriples resource for serializing related resources
39
- def update_resource
40
- resource
41
- end
42
-
43
- private
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
- def save_file(&block)
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 : !! @grid_file = self.class.grid.put(file, attributes.symbolize_keys)
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
- module ClassMethods
55
- ##
56
- # Create a namespaced GridFS module for this class
57
- def grid
58
- @grid ||= Mongoid::GridFs.build_namespace_for name
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
@@ -1,56 +1,112 @@
1
1
  require 'mongoid'
2
2
  require 'active_triples'
3
3
 
4
- module Ladder::Resource
5
- autoload :Dynamic, 'ladder/resource/dynamic'
6
- autoload :Serializable, 'ladder/resource/serializable'
4
+ module Ladder
5
+ module Resource
6
+ autoload :Dynamic, 'ladder/resource/dynamic'
7
+ autoload :Serializable, 'ladder/resource/serializable'
7
8
 
8
- extend ActiveSupport::Concern
9
+ extend ActiveSupport::Concern
9
10
 
10
- include Mongoid::Document
11
- include ActiveTriples::Identifiable
12
- include Ladder::Resource::Serializable
11
+ include Mongoid::Document
12
+ include ActiveTriples::Identifiable
13
+ include Ladder::Resource::Serializable
13
14
 
14
- included do
15
- configure base_uri: RDF::URI.new(LADDER_BASE_URI) / name.underscore.pluralize if defined? LADDER_BASE_URI
16
- end
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
- delegate :rdf_label, to: :update_resource
19
+ delegate :rdf_label, to: :update_resource
19
20
 
20
- ##
21
- # Populate resource properties from ActiveModel
22
- def update_resource(opts = {})
23
- resource_class.properties.each do |name, property|
24
- value = update_from_field(name) if fields[name]
25
- value = update_from_relation(name, opts) if relations[name]
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.set_value(property.predicate, value) #if value
36
+ resource
28
37
  end
29
38
 
30
- resource
31
- end
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
- # Push RDF statement into resource
35
- def <<(data)
36
- # ActiveTriples::Resource expects: RDF::Statement, Hash, or Array
37
- data = RDF::Statement.from(data) unless data.is_a? RDF::Statement
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
- # Only push statement if the statement's predicate is defined on the class
40
- if resource_class.properties.values.map(&:predicate).include? data.predicate
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
- # Set the value in Mongoid
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
- private
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
- def update_from_field(name)
52
- if fields[name].localized?
53
- localized_hash = read_attribute(name)
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
- self.send(name)
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
- if opts[:related] or embedded_relations[name]
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[name]
75
- objects.each { |object| object.resource.set_value(relation_def.inverse, self.rdf_subject) } if 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
- public
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
- # Overload ActiveTriples #property
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
- # @see ActiveTriples::Properties
92
- def property(name, opts={})
93
- if class_name = opts[:class_name]
94
- mongoid_opts = {autosave: true, index: true}.merge(opts.except(:predicate, :multivalue))
95
- opts.except! *mongoid_opts.keys
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
- has_and_belongs_to_many(name, mongoid_opts) unless relations.keys.include? name.to_s
98
- else
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
- super
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