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 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