rdf-mapper 0.0.1

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.
@@ -0,0 +1,623 @@
1
+ module RDFMapper
2
+
3
+ require 'lib/model/association'
4
+ require 'lib/model/output'
5
+ require 'lib/model/attribute'
6
+ require 'lib/model/property'
7
+
8
+ ##
9
+ # [-]
10
+ ##
11
+ class Model
12
+
13
+ class << self
14
+
15
+ alias_method :original_name, :name #nodoc
16
+
17
+ ##
18
+ # Sets or returns model's namespace. It is intended to operate as a shortcut:
19
+ # model and its attributes will calculate their RDF type and predicates
20
+ # automatically. The following two examples produce identical models:
21
+ #
22
+ # class Person < RDFMapper::Model
23
+ # namespace 'http://example.org/schema#'
24
+ # attribute :name
25
+ # attribute :age
26
+ # end
27
+ #
28
+ # class Person < RDFMapper::Model
29
+ # type 'http://example.org/schema#Person'
30
+ # attribute :name, :predicate => 'http://example.org/schema#name'
31
+ # attribute :age, :predicate => 'http://example.org/schema#age'
32
+ # end
33
+ #
34
+ # Person.type #=> 'http://xmlns.com/foaf/0.1/Person'
35
+ #
36
+ # @overload namespace(value)
37
+ # Sets model's namespace
38
+ # @param [RDF::Vocabulary, RDF::URI, String] value
39
+ #
40
+ # @overload namespace
41
+ # Returns model's namespace
42
+ # @param [nil]
43
+ #
44
+ # @see type
45
+ # @return [RDF::Vocabulary]
46
+ ##
47
+ def namespace(value = nil, options = {})
48
+ @ns = options[:name] || 'myrdf'
49
+ case value
50
+ when NilClass
51
+ @namespace
52
+ when RDF::Vocabulary
53
+ @namespace = value
54
+ else
55
+ @namespace = RDF::Vocabulary.new(value.to_s)
56
+ end
57
+ end
58
+
59
+ def ns
60
+ @ns || 'myrdf'
61
+ end
62
+
63
+ ##
64
+ # Sets or returns model's RDF type
65
+ #
66
+ # class Company < RDFMapper::Model
67
+ # type RDF::URI.new('http://example.org/schema#Company')
68
+ # end
69
+ #
70
+ # class Person < RDFMapper::Model
71
+ # type 'http://example.org/schema#Person'
72
+ # end
73
+ #
74
+ # Company.type #=> #<RDF::URI(http://example.org/schema#Company)>
75
+ # Person.type #=> #<RDF::URI(http://example.org/schema#Person)>
76
+ #
77
+ # @overload type(value)
78
+ # Sets model's RDF type
79
+ # @param [RDF::URI, String] value
80
+ #
81
+ # @overload type
82
+ # @param [nil]
83
+ #
84
+ # @see namespace
85
+ # @return [RDF::URI]
86
+ ##
87
+ def type(value = nil)
88
+ unless value.nil?
89
+ return @type = RDF::URI.new(value.to_s)
90
+ end
91
+ unless @type.nil?
92
+ return @type
93
+ end
94
+ (nil == namespace) == true ? nil : namespace[name]
95
+ end
96
+
97
+ ##
98
+ # Returns model's name without modules. Original class name is stored
99
+ # as 'original_name'
100
+ #
101
+ # module TestModule
102
+ # class Person < RDFMapper::Model; end
103
+ # end
104
+ #
105
+ # Person.name => 'Person'
106
+ # Person.original_name => 'TestModule::Person'
107
+ #
108
+ # @return [String]
109
+ ##
110
+ def name
111
+ original_name.split('::').last
112
+ end
113
+
114
+ ##
115
+ # Sets or returns model's connection adapter.
116
+ #
117
+ # @overload adapter(instance)
118
+ # Sets model's connection adapter
119
+ # @param [Symbol] name adapter name (`:rails`, `:rest` or `:sparql`)
120
+ # @param [Hash] options options to pass on to the adapter constructor
121
+ #
122
+ # @overload adapter
123
+ # Returns model's connection adapter
124
+ #
125
+ # @return [Object] an instance of RDFMapper adapter
126
+ ##
127
+ def adapter(name = nil, options = {})
128
+ return @adapter if name.nil?
129
+ @adapter = RDFMapper::Adapters.register(name, self, options)
130
+ end
131
+
132
+ ##
133
+ # Returns a model that subclassed {RDFMapper::Model} and has specified
134
+ # URI as its rdf:type
135
+ #
136
+ # class Person < RDFMapper::Model
137
+ # type 'http://example.org/schema#Person'
138
+ # end
139
+ #
140
+ # class Company < RDFMapper::Model
141
+ # namespace 'http://example.org/schema#'
142
+ # end
143
+ #
144
+ # RDFMapper::Model['http://example.org/schema#Person'] #=> Person
145
+ # RDFMapper::Model['http://example.org/schema#Company'] #=> Company
146
+ # RDFMapper::Model['http://unknown-url.com/'] #=> nil
147
+ #
148
+ # @param [String] URI
149
+ # @param [RDF::URI] URI
150
+ #
151
+ # @return [Object] an RDFMapper model
152
+ ##
153
+ def [](uri)
154
+ return nil if uri.nil?
155
+ @@subclasses.select do |model|
156
+ model.type.to_s == uri.to_s
157
+ end.first
158
+ end
159
+
160
+ ##
161
+ # Returns RDFMapper::Attribute that is assigned to the specified name.
162
+ # Accepts symbol, string, RDF::URI as a parameter. Value is optional
163
+ # and is used for associations.
164
+ #
165
+ # class Person < RDFMapper::Model
166
+ # namespace 'http://example.org/schema#'
167
+ # attribute :name, :type => :text
168
+ # has_many :contacts, :predicate => 'http://example.org/schema#has'
169
+ # has_many :friends, :predicate => 'http://example.org/schema#has'
170
+ # end
171
+ #
172
+ # Person.has?(:name) #=> #<RDFMapper::Model::Attribute>
173
+ # Person.has?('http://example.org/schema#name') #=> #<RDFMapper::Model::Attribute>
174
+ # Person.has?('http://example.org/schema#unknown') #=> nil
175
+ #
176
+ # Person.has?('http://example.org/schema#has', Contact) #=> #<RDFMapper::Model::Attribute>
177
+ # Person.has?('http://example.org/schema#has', Contact.new) #=> #<RDFMapper::Model::Attribute>
178
+ # Person.has?(nil, Contact) #=> #<RDFMapper::Model::Attribute>
179
+ #
180
+ # @param [Symbol, RDF::URI, String] name
181
+ # @param [Object] value
182
+ #
183
+ # @return [RDFMapper::Attribute]
184
+ # @return [nil] if attribute was not found
185
+ ##
186
+ def has?(name, value = nil)
187
+ if name.kind_of? String
188
+ return has?(RDF::URI.new(name), value)
189
+ end
190
+ if name.kind_of? Symbol
191
+ return attributes[name]
192
+ end
193
+ attributes.values.select do |att|
194
+ att.matches?(name, value)
195
+ end.first
196
+ end
197
+
198
+ ##
199
+ # Returns the association name for the supplied predicate and / or value
200
+ # @see has?
201
+ #
202
+ # @param [Symbol, RDF::URI, String] name
203
+ # @param [Object] value
204
+ #
205
+ # @return [Symbol]
206
+ # @return [nil] if attribute was not found
207
+ ##
208
+ def symbol(name, value = nil)
209
+ att = has?(name, value)
210
+ att.nil? ? nil : att.name
211
+ end
212
+
213
+ ##
214
+ # Returns a hash of all attributes with their names as keys and
215
+ # RDFMapper::Attribute instances as values.
216
+ #
217
+ # @return [Hash]
218
+ ##
219
+ def attributes
220
+ @attributes ||= {}
221
+ end
222
+
223
+ ##
224
+ # Returns a hash of all properties with their names as keys and
225
+ # RDFMapper::Attribute instances as values.
226
+ #
227
+ # @return [Hash]
228
+ ##
229
+ def properties
230
+ Hash[attributes.select { |name, att| att.property? }]
231
+ end
232
+
233
+ ##
234
+ # Returns a hash of all associations with their names as keys and
235
+ # RDFMapper::Attribute instances as values.
236
+ #
237
+ # @return [Hash]
238
+ ##
239
+ def associations
240
+ Hash[attributes.reject { |name, att| att.property? }]
241
+ end
242
+
243
+ ##
244
+ # Defines an attribute within a model.
245
+ #
246
+ # @param [Symbol] name attribute name
247
+ # @param [Symbol] options[:type] attribute type (:text, :uri, :integer, :float)
248
+ # @param [RDF::URI, String] options[:predicate] RDF predicate
249
+ #
250
+ # @return [Object] instance of RDFMapper::Model::Attribute
251
+ ##
252
+ def attribute(name, options = {})
253
+ attributes[name.to_sym] = Attribute.new(self, name.to_sym, options)
254
+ class_eval <<-EOF
255
+ def #{name}(*args, &block)
256
+ get_attribute(:#{name}, *args, &block)
257
+ end
258
+ def #{name}=(value)
259
+ set_attribute(:#{name}, value)
260
+ end
261
+ EOF
262
+ end
263
+
264
+ ##
265
+ # Creates an object and saves it via the assigned adapter.
266
+ # The resulting object is returned whether the object was saved
267
+ # successfully to the database or not.
268
+ #
269
+ # @param [Hash] attributes attributes of the new object
270
+ # @param [RDF::URI, String] id object's ID
271
+ #
272
+ # @return [Object] instance of RDFMapper::Model
273
+ # @return [nil] if save was unsuccessful
274
+ ##
275
+ def create(attributes, id)
276
+ new(attributes).save(id)
277
+ end
278
+
279
+ ##
280
+ # Find operates similarly to Rails' ActiveRecord::Base.find function. It has
281
+ # the same four retrieval approaches:
282
+ #
283
+ # * Find by id -- This can either be a specific id, a list of ids, or
284
+ # an array of ids ([5, 6, 10]).
285
+ #
286
+ # * Find first -- This will return the first record matched by the
287
+ # options used. These options can either be specific conditions or
288
+ # merely an order. If no record can be matched, `nil` is returned.
289
+ # Use Model.find(:first, *args) or its shortcut Model.first(*args).
290
+ #
291
+ # * Find last - This will return the last record matched by the options
292
+ # used. These options can either be specific conditions or merely an
293
+ # order. If no record can be matched, `nil` is returned. Use
294
+ # Model.find(:last, *args) or its shortcut Model.last(*args).
295
+ #
296
+ # * Find all - This will return all the records matched by the options
297
+ # used. If no records are found, an empty array is returned. Use
298
+ # Model.find(:all, *args) or its shortcut Model.all(*args).
299
+ ##
300
+ def find(*args)
301
+ options = args.last.is_a?(::Hash) ? args.pop : {}
302
+ case args.first
303
+ when :first then find_every(options.merge(:limit => 1)).first
304
+ when :last then find_every(options).last
305
+ when :all then find_every(options)
306
+ else find_from_ids(args, options)
307
+ end
308
+ end
309
+
310
+ ##
311
+ # Either finds or creates an object with the specified ID.
312
+ #
313
+ # @param [RDF::URI, String] id object's ID
314
+ # @param [Hash] attributes attributes of the new object
315
+ #
316
+ # @return [Object] instance of RDFMapper::Model
317
+ # @return [nil] if save was unsuccessful
318
+ ##
319
+ def find_or_create(id, attributes = {})
320
+ instance = find(id)
321
+ instance.nil? ? create(id, attributes) : instance
322
+ end
323
+
324
+ ##
325
+ # A convenience wrapper for find(:first, *args). You can pass in
326
+ # all the same arguments to this method as you can to find(:first).
327
+ #
328
+ # @see find
329
+ ##
330
+ def first(*args)
331
+ find(:first, *args)
332
+ end
333
+
334
+ ##
335
+ # A convenience wrapper for find(:last, *args). You can pass in
336
+ # all the same arguments to this method as you can to find(:last).
337
+ #
338
+ # @see find
339
+ ##
340
+ def last(*args)
341
+ find(:last, *args)
342
+ end
343
+
344
+ ##
345
+ # This is an alias for find(:all). You can pass in all the same
346
+ # arguments to this method as you can to find(:all).
347
+ #
348
+ # @see find
349
+ ##
350
+ def all(*args)
351
+ find(:all, *args)
352
+ end
353
+
354
+
355
+ private
356
+
357
+ ##
358
+ # Returns an Array of instances that match specified conditions.
359
+ # Note that they are not loaded until they are accessed (lazy loading)
360
+ ##
361
+ def find_every(options) #nodoc
362
+ RDFMapper::Scope::Collection.new(self, options)
363
+ end
364
+
365
+ ##
366
+ # Returns instances with specified IDs. Depending on the number
367
+ # of IDs it returns either an Array or a single instance (or nil
368
+ # if nothing was found)
369
+ ##
370
+ def find_from_ids(ids, options) #nodoc
371
+ unless ids.kind_of?(Array)
372
+ ids = [ids]
373
+ end
374
+ options[:conditions] ||= { }
375
+ options[:conditions][:id] = ids
376
+ result = find_every(options)
377
+ case ids.size
378
+ when 0 then []
379
+ when 1 then result.first
380
+ else result
381
+ end
382
+ end
383
+
384
+ ##
385
+ # Keeps track of all models that subclass RDFMapper::Model
386
+ ##
387
+ def inherited(subclass) #nodoc
388
+ @@subclasses ||= []
389
+ @@subclasses << subclass
390
+ end
391
+
392
+ end
393
+
394
+ include RDFMapper::Logger
395
+
396
+ ##
397
+ # Creates a new instance of a model with specified attributes.
398
+ # Note that attributes include properties as well as associations.
399
+ # It also accepts URIs in addition to symbols:
400
+ #
401
+ # class Company << RDFMapper::Model
402
+ # namespace 'http://myschema.com/#'
403
+ # has_many :people
404
+ # end
405
+ #
406
+ # class Person << RDFMapper::Model
407
+ # namespace 'http://myschema.com/#'
408
+ # attribute :name, :type => text
409
+ # belongs_to :company, :predicate => 'http://myschema.com/#employer'
410
+ # end
411
+ #
412
+ # The following two examples create identical models:
413
+ #
414
+ # Person.new(:name => 'John')
415
+ # Person.new('http://myschema.com/#name' => 'John')
416
+ #
417
+ # And so do the following two examples:
418
+ #
419
+ # @company = Company.new(:name => 'MyCo Inc.')
420
+ #
421
+ # Person.new(:company => @company)
422
+ # Person.new('http://myschema.com/#employer' => @company)
423
+ #
424
+ # @param [Hash] attributes attributes of the new object
425
+ # @return [Object] instance of RDFMapper::Model
426
+ ##
427
+ def initialize(attributes = {})
428
+ @arbitrary = {}
429
+ @attributes = {}
430
+ @id = nil
431
+
432
+ self.class.attributes.map do |name, att|
433
+ @attributes[name] = att.value(self)
434
+ end
435
+
436
+ self.attributes = attributes
437
+ yield self if block_given?
438
+ end
439
+
440
+ ##
441
+ # Returns objects's unique ID.
442
+ #
443
+ # @return [RDF::URI] object's ID
444
+ ##
445
+ def id(*args)
446
+ @id.nil? ? nil : @id.dup
447
+ end
448
+
449
+ ##
450
+ # Compares instances based on their IDs.
451
+ #
452
+ # @return [Boolean]
453
+ ##
454
+ def ==(other)
455
+ (other.nil? or other.id.nil?) ? false : (id == other.id)
456
+ end
457
+
458
+ alias_method :eql?, :==
459
+ alias_method :equal?, :==
460
+
461
+ ##
462
+ # Returns the value of the attribute identified by `name` after it
463
+ # has been typecast (for example, "2004-12-12" is cast to a date
464
+ # object, like Date.new(2004, 12, 12)). (Alias for the private
465
+ # get_attribute method).
466
+ #
467
+ # @param [Symbol, String, RDF::URI] name attribute name or predicate URI
468
+ # @return [Object] instance of a property or an association
469
+ ##
470
+ def [](name)
471
+ unless name.kind_of? Symbol
472
+ name = self.class.symbol(name)
473
+ end
474
+ name.nil? ? nil : get_attribute(name)
475
+ end
476
+
477
+ ##
478
+ # Updates the attribute identified by `name` with the specified
479
+ # value. (Alias for the private set_attribute method).
480
+ #
481
+ #
482
+ # @param [Symbol, String, RDF::URI] name attribute name or predicate URI
483
+ # @param [Object] value new value of the attribute
484
+ #
485
+ # @return [Object] instance of a property or an association
486
+ ##
487
+ def []=(name, value)
488
+ unless name.kind_of? Symbol
489
+ name = self.class.symbol(name)
490
+ end
491
+ name.nil? ? nil : set_attribute(name, value)
492
+ end
493
+
494
+ ##
495
+ # Returns a hash of all the attributes with their names as keys and
496
+ # the attributes' values as values.
497
+ #
498
+ # @return [Hash] all attributes of an instance (name => value)
499
+ ##
500
+ def attributes(*args)
501
+ @attributes.merge(@arbitrary)
502
+ end
503
+
504
+ ##
505
+ # Allows you to set all the attributes at once by passing in a hash
506
+ # with keys matching attribute names or RDF predicates.
507
+ #
508
+ # @param [Hash] attributes object's new attributes
509
+ # @return [Hash] hash of all attributes (name => value)
510
+ ##
511
+ def attributes=(hash)
512
+ return unless hash.kind_of? Hash
513
+ hash.nil? ? nil : hash.each { |name, value| self[name] = value }
514
+ end
515
+
516
+ ##
517
+ # Checks whether the model originated from or was saved to
518
+ # a data source (in other word, whether it has RDF ID).
519
+ #
520
+ # @return [Boolean]
521
+ ##
522
+ def new?
523
+ id.nil?
524
+ end
525
+
526
+ alias_method :new_record?, :==
527
+
528
+ ##
529
+ # Saves the instance. If the model is new, a record gets created
530
+ # via the specified adapter (ID must be supplied in this case),
531
+ # otherwise the existing record gets updated.
532
+ #
533
+ # @param [RDF::URI, String] id object's ID
534
+ # @return [Object] self
535
+ # @return [nil] if save was unsuccessful
536
+ ##
537
+ def save(id = nil)
538
+ # Raise error if adapter is unspecified
539
+ check_for_adapter
540
+
541
+ if new? and id.nil?
542
+ raise RuntimeError, 'Save failed. ID must be specified'
543
+ end
544
+ if new?
545
+ self.id = id
546
+ end
547
+ self.attributes = self.class.adapter.save(self)
548
+ self
549
+ end
550
+
551
+ ##
552
+ # [-]
553
+ ##
554
+ def reload
555
+ # Raise error if adapter is unspecified
556
+ check_for_adapter
557
+
558
+ if id.nil?
559
+ raise RuntimeError, 'Reload failed. Model has no ID'
560
+ end
561
+
562
+ self.attributes = self.class.adapter.reload(self)
563
+ self
564
+ end
565
+
566
+ ##
567
+ # Developer-friendly representation of the instance.
568
+ #
569
+ # @return [String]
570
+ ##
571
+ def inspect #nodoc
572
+ "#<%s:%s>" % [self.class, object_id]
573
+ end
574
+
575
+
576
+ private
577
+
578
+ ##
579
+ # Raises an error if adapter is undefined.
580
+ ##
581
+ def check_for_adapter #nodoc
582
+ if self.class.adapter.nil?
583
+ raise RuntimeError, 'Save failed. Model adapter is undefined'
584
+ end
585
+ end
586
+
587
+ ##
588
+ # Sets ID of this object (must be RDF::URI or a String).
589
+ ##
590
+ def id=(value) #nodoc
591
+ @id = RDF::URI.new(value.to_s)
592
+ end
593
+
594
+ ##
595
+ # Returns the value of an attribute identified by `name` after it
596
+ # has been typecast (for example, "2004-12-12" is cast to a date
597
+ # object, like Date.new(2004, 12, 12)).
598
+ ##
599
+ def get_attribute(name, *args, &block) #nodoc
600
+ if @attributes.key?(name)
601
+ @attributes[name].object(*args, &block)
602
+ else
603
+ @arbitrary[name]
604
+ end
605
+ end
606
+
607
+ ##
608
+ # Updates the attribute identified by `name` with the specified value.
609
+ ##
610
+ def set_attribute(name, value) #nodoc
611
+ if name == :id
612
+ return nil
613
+ end
614
+ if @attributes.key?(name)
615
+ @attributes[name].replace(value)
616
+ else
617
+ @arbitrary[name] = value
618
+ end
619
+ end
620
+
621
+ end # Model
622
+ end # RDFMapper
623
+
@@ -0,0 +1,70 @@
1
+ module RDFMapper
2
+ class Model
3
+
4
+ ##
5
+ # RDF XML representaion of the instance.
6
+ #
7
+ # @todo. Not implemented
8
+ #
9
+ # @param [Hash] options [TODO]
10
+ # @return [String]
11
+ ##
12
+ def to_xml(options = {})
13
+ RDF::Writer.for(:xml).buffer({ :declaration => false }) do |writer|
14
+ if self.class.namespace
15
+ writer.namespace!(self.class.namespace, self.class.ns)
16
+ end
17
+ to_triples.each do |triple|
18
+ writer << triple
19
+ end
20
+ end
21
+ end
22
+
23
+ ##
24
+ # [-]
25
+ ##
26
+ def to_triples(options = {})
27
+ to_statements(options).map do |statement|
28
+ [ statement[:subject], statement[:predicate], statement[:object] ]
29
+ end
30
+ end
31
+
32
+ ##
33
+ # options[:short] - class declaration only
34
+ # options[:full] - include associations
35
+ ##
36
+ def to_statements(options = {})
37
+ if options[:full]
38
+ atts = attribute_statements(options)
39
+ elsif options[:short]
40
+ return type_statement
41
+ else
42
+ atts = attribute_statements
43
+ end
44
+ type_statement + atts
45
+ end
46
+
47
+ private
48
+
49
+ ##
50
+ # [-]
51
+ ##
52
+ def attribute_statements(options = {})
53
+ @attributes.map do |name, att|
54
+ att.to_statements(options)
55
+ end.flatten.compact
56
+ end
57
+
58
+ def associations_statements
59
+
60
+ end
61
+
62
+ def type_statement
63
+ [{ :subject => id,
64
+ :predicate => RDF.type,
65
+ :object => self.class.type }]
66
+ end
67
+
68
+ end # Model
69
+ end # RDFMapper
70
+