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.
- data/README.rdoc +188 -0
- data/UNLICENSE +25 -0
- data/VERSION +1 -0
- data/lib/lib/adapters/base.rb +83 -0
- data/lib/lib/adapters/rails.rb +307 -0
- data/lib/lib/adapters/rest.rb +45 -0
- data/lib/lib/adapters/sparql.rb +105 -0
- data/lib/lib/associations/base.rb +95 -0
- data/lib/lib/associations/belongs_to.rb +64 -0
- data/lib/lib/associations/has_and_belongs.rb +17 -0
- data/lib/lib/associations/has_many.rb +147 -0
- data/lib/lib/associations/has_one.rb +17 -0
- data/lib/lib/model/association.rb +59 -0
- data/lib/lib/model/attribute.rb +186 -0
- data/lib/lib/model/base.rb +623 -0
- data/lib/lib/model/output.rb +70 -0
- data/lib/lib/model/property.rb +78 -0
- data/lib/lib/scope/collection.rb +165 -0
- data/lib/lib/scope/condition.rb +132 -0
- data/lib/lib/scope/loader.rb +111 -0
- data/lib/lib/scope/model.rb +129 -0
- data/lib/lib/scope/query.rb +281 -0
- data/lib/lib/util/http.rb +66 -0
- data/lib/lib/util/logger.rb +68 -0
- data/lib/rdf-mapper.rb +15 -0
- metadata +141 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
module RDFMapper
|
2
|
+
class Model
|
3
|
+
class Property
|
4
|
+
|
5
|
+
def initialize(instance, options = {}, *args)
|
6
|
+
@instance = instance
|
7
|
+
@options = options
|
8
|
+
@value = nil
|
9
|
+
@new = true
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Checks if property has default (nil) value.
|
14
|
+
#
|
15
|
+
# @params [Boolean]
|
16
|
+
##
|
17
|
+
def new?
|
18
|
+
@new
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Assigns a new value to the property.
|
23
|
+
#
|
24
|
+
# @param [Object] value
|
25
|
+
# @return [Object]
|
26
|
+
##
|
27
|
+
def replace(value)
|
28
|
+
@new = false
|
29
|
+
if value.kind_of? Array
|
30
|
+
value = value.first
|
31
|
+
end
|
32
|
+
if value.kind_of? RDF::Literal
|
33
|
+
value = value.object
|
34
|
+
end
|
35
|
+
@value = case @options[:type]
|
36
|
+
when :text then value.to_s
|
37
|
+
when :integer then value.to_i
|
38
|
+
when :float then value.to_f
|
39
|
+
when :uri then RDF::URI.new(value.to_s)
|
40
|
+
else value.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# [-]
|
46
|
+
##
|
47
|
+
def object(*args)
|
48
|
+
@value
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# [-]
|
53
|
+
##
|
54
|
+
def to_statements(options = {})
|
55
|
+
{ :subject => @instance.id,
|
56
|
+
:predicate => @options[:type],
|
57
|
+
:object => RDF::Literal.new(@value) }
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Developer-friendly representation of the instance
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
##
|
65
|
+
def inspect #nodoc
|
66
|
+
@value.inspect
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# [-]
|
71
|
+
##
|
72
|
+
def method_missing(symbol, *args, &block)
|
73
|
+
@value.send(symbol, *args, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
end # Property
|
77
|
+
end # Model
|
78
|
+
end # RDFMapper
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module RDFMapper
|
2
|
+
module Scope
|
3
|
+
##
|
4
|
+
# This class contains collections of models. It is primarily used in
|
5
|
+
# search queries (find(:all) queries will yield instances of this
|
6
|
+
# class) and associations.
|
7
|
+
#
|
8
|
+
# It implements most commonly used Array and Enumerable methods.
|
9
|
+
##
|
10
|
+
class Collection
|
11
|
+
|
12
|
+
attr_reader :loader #Temporary
|
13
|
+
|
14
|
+
def initialize(cls, options)
|
15
|
+
@loader = Loader.new(cls, options)
|
16
|
+
@models = []
|
17
|
+
@cls = cls
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Set data adapter for the query and return self. This will override
|
22
|
+
# the default model adapter. It is intended to be used as a chain method:
|
23
|
+
#
|
24
|
+
# Person.find(:all).from(:rest) #=> #<PersonCollection:217132856>
|
25
|
+
# Person.find(:all).from(:rest).length #=> 10
|
26
|
+
#
|
27
|
+
# @param [Symbol] adapter (:rails, :sparql, :rest)
|
28
|
+
# @param [Hash] options options to pass on to the adapter constructor
|
29
|
+
#
|
30
|
+
# @return [self]
|
31
|
+
##
|
32
|
+
def from(adapter, options = {})
|
33
|
+
@loader.from(adapter, options)
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Returns first object of the collection. Note that the
|
39
|
+
# object is not yet loaded at this point.
|
40
|
+
#
|
41
|
+
# @return [Object]
|
42
|
+
##
|
43
|
+
def first
|
44
|
+
at(0)
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Returns first object of the collection. Note that the
|
49
|
+
# object is not yet loaded at this point.
|
50
|
+
#
|
51
|
+
# @return [Object]
|
52
|
+
##
|
53
|
+
def last
|
54
|
+
at(-1)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Returns the object at `index`. Note that the object is
|
59
|
+
# not yet loaded at this point.
|
60
|
+
#
|
61
|
+
# @param [Integer] index
|
62
|
+
# @return [Object]
|
63
|
+
##
|
64
|
+
def [](index)
|
65
|
+
at(index)
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Returns the object at `index`. Note that the object is
|
70
|
+
# not yet loaded at this point.
|
71
|
+
#
|
72
|
+
# @param [Integer] index
|
73
|
+
# @return [Object]
|
74
|
+
##
|
75
|
+
def at(index)
|
76
|
+
@models[index] ||= @loader.get(index)
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Returns true if collection has no objects.
|
81
|
+
#
|
82
|
+
# @param [Boolean]
|
83
|
+
##
|
84
|
+
def empty?
|
85
|
+
length == 0
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Returns the number of objects in a collection.
|
90
|
+
#
|
91
|
+
# @return [Integer]
|
92
|
+
##
|
93
|
+
def length
|
94
|
+
@loader.length
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Calls block once for each object in a collection, passing
|
99
|
+
# that element as a parameter.
|
100
|
+
#
|
101
|
+
# @yield [Object]
|
102
|
+
# @return [self]
|
103
|
+
##
|
104
|
+
def each(&block)
|
105
|
+
items.each { |x| block.call(x) }
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Invokes block once for each object in a collection. Creates
|
111
|
+
# a new array containing the values returned by the block
|
112
|
+
#
|
113
|
+
# @yield [Object]
|
114
|
+
# @return [Array]
|
115
|
+
##
|
116
|
+
def map(&block)
|
117
|
+
items.map { |x| block.call(x) }
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Returns true if collection contains specified object.
|
122
|
+
#
|
123
|
+
# @param [Object] object instance of RDFMapper::Model
|
124
|
+
# @return [Boolean]
|
125
|
+
##
|
126
|
+
def exists?(object)
|
127
|
+
items.include?(object)
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Converts collection into Array.
|
132
|
+
#
|
133
|
+
# @return [Array]
|
134
|
+
##
|
135
|
+
def to_a
|
136
|
+
items
|
137
|
+
end
|
138
|
+
|
139
|
+
alias_method :size, :length
|
140
|
+
alias_method :collect, :map
|
141
|
+
alias_method :slice, :[]
|
142
|
+
alias_method :include?, :exists?
|
143
|
+
|
144
|
+
##
|
145
|
+
# Developer-friendly representation of the instance
|
146
|
+
#
|
147
|
+
# @return [String]
|
148
|
+
##
|
149
|
+
def inspect #nodoc
|
150
|
+
"#<%sCollection:%s>" % [@cls, object_id]
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
##
|
157
|
+
# Loads entire collection (if needed) and returns all objects.
|
158
|
+
##
|
159
|
+
def items #nodoc
|
160
|
+
(0..length-1).map { |x| at(x) }
|
161
|
+
end
|
162
|
+
|
163
|
+
end # Collection
|
164
|
+
end # Scope
|
165
|
+
end # RDFMapper
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module RDFMapper
|
2
|
+
module Scope
|
3
|
+
##
|
4
|
+
# [-]
|
5
|
+
##
|
6
|
+
class Condition
|
7
|
+
|
8
|
+
attr_reader :eq
|
9
|
+
|
10
|
+
def initialize(cls, att, value, eq = '=')
|
11
|
+
@cls, @att, @value, @eq = cls, att, value, eq
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# [-]
|
16
|
+
##
|
17
|
+
def name
|
18
|
+
if @att == :id
|
19
|
+
return @att
|
20
|
+
end
|
21
|
+
unless att = @cls.has?(@att)
|
22
|
+
raise RuntimeError, 'Undefined attribute %s for %s' % [@att, @cls]
|
23
|
+
else
|
24
|
+
att.name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# [-]
|
30
|
+
##
|
31
|
+
def value(required = [])
|
32
|
+
association? ? association(required) : literal
|
33
|
+
end
|
34
|
+
|
35
|
+
alias_method :check, :value
|
36
|
+
|
37
|
+
##
|
38
|
+
# [-]
|
39
|
+
##
|
40
|
+
def to_triples(subject)
|
41
|
+
to_statements(subject).map do |statement|
|
42
|
+
[ statement[:subject], statement[:predicate], statement[:object] ]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# [-]
|
48
|
+
##
|
49
|
+
def to_statements(subject)
|
50
|
+
if association?
|
51
|
+
object = association.id
|
52
|
+
rdf_type = association.to_triples(:short => true)
|
53
|
+
else
|
54
|
+
object = RDF::Query::Variable.new
|
55
|
+
object.bind(value, eq)
|
56
|
+
rdf_type = []
|
57
|
+
end
|
58
|
+
rdf_type + [{
|
59
|
+
:subject => subject,
|
60
|
+
:predicate => @cls.has?(@att).type,
|
61
|
+
:object => object
|
62
|
+
}]
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Developer-friendly representation of the instance.
|
67
|
+
#
|
68
|
+
# @return [String]
|
69
|
+
##
|
70
|
+
def inspect #nodoc
|
71
|
+
"#<Condition:(%s%s%s)>" % [name, eq, value]
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
##
|
78
|
+
# [-]
|
79
|
+
##
|
80
|
+
def association(required = [], value = nil) #nodoc
|
81
|
+
if value.nil?
|
82
|
+
value = @value
|
83
|
+
end
|
84
|
+
if value.kind_of? Array or value.kind_of? RDFMapper::Scope::Collection
|
85
|
+
return value.map do |item|
|
86
|
+
association(required, item)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
unless value.kind_of? RDFMapper::Model
|
90
|
+
att = @cls.associations[name]
|
91
|
+
value = att.model.find(value.to_s)
|
92
|
+
end
|
93
|
+
required.each do |att|
|
94
|
+
if value[att].nil?
|
95
|
+
raise RuntimeError, 'Expected %s to have %s' % [value, att] if value.reload[att].nil?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
value
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# [-]
|
103
|
+
##
|
104
|
+
def literal(value = nil) #nodoc
|
105
|
+
if value.nil?
|
106
|
+
value = @value
|
107
|
+
end
|
108
|
+
if value.kind_of? Array
|
109
|
+
return value.map do |item|
|
110
|
+
literal(item)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
if value.kind_of? RDF::Literal
|
114
|
+
value.object
|
115
|
+
elsif value.kind_of? RDF::URI
|
116
|
+
value.to_s
|
117
|
+
else
|
118
|
+
value
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Return the association name that has `name_or_key` as foreign key or name.
|
124
|
+
##
|
125
|
+
def association? #nodoc
|
126
|
+
att = @cls.associations[name]
|
127
|
+
not (att.nil? or att.property?)
|
128
|
+
end
|
129
|
+
|
130
|
+
end # Condition
|
131
|
+
end # Scope
|
132
|
+
end # RDFMapper
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module RDFMapper
|
2
|
+
module Scope
|
3
|
+
##
|
4
|
+
# Loader is responsible for loading and updating model attributes. An instance
|
5
|
+
# of Loader is assigned to each search query and association.
|
6
|
+
##
|
7
|
+
class Loader
|
8
|
+
|
9
|
+
def initialize(cls, options = {})
|
10
|
+
@conditions = Query.new(cls, options)
|
11
|
+
@objects = []
|
12
|
+
@cls = cls
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Checks if model ID is specified within conditions. Returns
|
17
|
+
# RDF::URI if found, nil otherwise.
|
18
|
+
#
|
19
|
+
# @return [RDF::URI]
|
20
|
+
# @return [nil]
|
21
|
+
##
|
22
|
+
def has_id?
|
23
|
+
@conditions[:id].nil? ? nil : RDF::URI.new(@conditions[:id])
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Sets data adapter for this loader. This will override default model adapter.
|
28
|
+
#
|
29
|
+
# @param [Symbol] adapter (:rails, :sparql, :rest)
|
30
|
+
# @param [Hash] options options to pass on to the adapter constructor
|
31
|
+
# @return [Object] adapter instance
|
32
|
+
##
|
33
|
+
def from(adapter, options = {})
|
34
|
+
@adapter = RDFMapper::Adapters.register(adapter, @cls, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Returns the number of loaded objects.
|
39
|
+
#
|
40
|
+
# @return [Integer]
|
41
|
+
##
|
42
|
+
def length
|
43
|
+
load.length
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Creates a new 'scoped' instance of RDFMapper::Model.
|
48
|
+
#
|
49
|
+
# @param [Integer] index
|
50
|
+
# @return [Object]
|
51
|
+
##
|
52
|
+
def get(index)
|
53
|
+
instance = @cls.new(@objects[index])
|
54
|
+
RDFMapper::Scope.apply(instance, self, index)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Updates an existing 'scoped' instance of RDFMapper::Model
|
59
|
+
# (sets ID and attributes).
|
60
|
+
#
|
61
|
+
# @param [Integer] index
|
62
|
+
# @param [Object] instance
|
63
|
+
# @return [Object]
|
64
|
+
##
|
65
|
+
def update(index, instance = nil) #nodoc
|
66
|
+
atts = load[index]
|
67
|
+
if atts.nil?
|
68
|
+
return instance.send(:nil!)
|
69
|
+
end
|
70
|
+
instance.send(:id=, atts[:id])
|
71
|
+
instance.attributes = atts
|
72
|
+
instance
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Developer-friendly representation of the instance
|
77
|
+
#
|
78
|
+
# @return [String]
|
79
|
+
##
|
80
|
+
def inspect #nodoc
|
81
|
+
"#<%sLoader:%s>" % [@cls, object_id]
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
##
|
87
|
+
# Returns adapter class to be used when loading. It's either the
|
88
|
+
# default model adapter or the one explicitly specified via `from`.
|
89
|
+
##
|
90
|
+
def adapter #nodoc
|
91
|
+
@adapter || @cls.adapter
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Loads and returns objects. Objects are 'cached' and not reloaded
|
96
|
+
# upon subsequent requests.
|
97
|
+
##
|
98
|
+
def load #nodoc
|
99
|
+
if @loaded
|
100
|
+
return @objects
|
101
|
+
end
|
102
|
+
if adapter.nil?
|
103
|
+
raise RuntimeError, "No adapter specified for %s" % @cls
|
104
|
+
end
|
105
|
+
@loaded = true
|
106
|
+
@objects = adapter.load(@conditions)
|
107
|
+
end
|
108
|
+
|
109
|
+
end # Loader
|
110
|
+
end # Scope
|
111
|
+
end # RDFMapper
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module RDFMapper
|
2
|
+
module Scope
|
3
|
+
|
4
|
+
require 'lib/scope/loader'
|
5
|
+
require 'lib/scope/collection'
|
6
|
+
require 'lib/scope/query'
|
7
|
+
require 'lib/scope/condition'
|
8
|
+
|
9
|
+
|
10
|
+
def self.apply(instance, loader, index)
|
11
|
+
instance.extend(Model)
|
12
|
+
instance.send(:scoped!, loader, index)
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Extension of RDFMapper::Model that implements lazy-loading. All models
|
17
|
+
# in collections and search queries will implement this module by default.
|
18
|
+
##
|
19
|
+
module Model
|
20
|
+
|
21
|
+
##
|
22
|
+
# Returns instance's unique ID (as RDF::URI).
|
23
|
+
##
|
24
|
+
def id
|
25
|
+
super || @loader.has_id? || load.id
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Set data adapter for the query and return self. This will override
|
30
|
+
# the default model adapter. It is intended to be used as a chain method:
|
31
|
+
#
|
32
|
+
# Person.find(:first).from(:rails) #=> #<Person:217132856>
|
33
|
+
# Person.find(:first).from(:rails).name #=> 'John'
|
34
|
+
#
|
35
|
+
# @param [Symbol] adapter (:rails, :sparql, :rest)
|
36
|
+
# @param [Hash] options options to pass on to the adapter constructor
|
37
|
+
#
|
38
|
+
# @return [self]
|
39
|
+
##
|
40
|
+
def from(adapter, options = {})
|
41
|
+
@loader.from(adapter, options)
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# In addition to the original method, preloads the model.
|
47
|
+
##
|
48
|
+
def [](name)
|
49
|
+
super || (@loaded ? nil : load[name])
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Returns a hash of all the attributes with their names as keys and
|
54
|
+
# the attributes' values as values. If model is unloaded, it will
|
55
|
+
# preload it before returning its attributes.
|
56
|
+
#
|
57
|
+
# @return [Hash] all attributes of an instance (name => value)
|
58
|
+
##
|
59
|
+
def attributes
|
60
|
+
check_for_nil_error
|
61
|
+
@loaded ? super : load.attributes
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Checks if the instance is `nil`. Will load the instance to find it out.
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
##
|
69
|
+
def nil?
|
70
|
+
@nil or (not @loaded and load.nil?)
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Developer-friendly representation of the instance.
|
75
|
+
#
|
76
|
+
# @return [String]
|
77
|
+
##
|
78
|
+
def inspect #nodoc
|
79
|
+
"#<Scoped|%s:%s>" % [self.class, object_id]
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
##
|
86
|
+
# Sets Loader instance and object's index in a collection. Called
|
87
|
+
# automatically when lazy-loading module is applied.
|
88
|
+
##
|
89
|
+
def scoped!(loader, index) #nodoc
|
90
|
+
@loader, @index = loader, index
|
91
|
+
@loaded, @nil = false, false
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Flags the instance as `nil`. Returns self.
|
97
|
+
##
|
98
|
+
def nil!
|
99
|
+
@nil = true
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# In addition to the original method, preloads the model.
|
105
|
+
##
|
106
|
+
def get_attribute(name, *args)
|
107
|
+
@loaded ? super : load.send(name, *args)
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Loads the model.
|
112
|
+
##
|
113
|
+
def load
|
114
|
+
check_for_nil_error
|
115
|
+
return self if @loaded
|
116
|
+
@loaded = true
|
117
|
+
@loader.update(@index, self)
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Checks if the instance is `nil`. Raises an error if true.
|
122
|
+
##
|
123
|
+
def check_for_nil_error
|
124
|
+
raise RuntimeError, 'Instance %s is NIL' % self if @nil
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end # Scope
|
129
|
+
end # RDFMapper
|