rdf-mapper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|