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
data/README.rdoc
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
= RDFMapper -- Object-relation mapping for RDF data
|
2
|
+
|
3
|
+
RDFMapper is an ORM[http://en.wikipedia.org/wiki/Object-relational_mapping]
|
4
|
+
written in Ruby that is designed to play nicely with RDF data.
|
5
|
+
|
6
|
+
== Features
|
7
|
+
|
8
|
+
- 100% Ruby code based on a slim & smart {RDF.rb}[http://rdf.rubyforge.org/] library
|
9
|
+
- All the usual Rails methods: find, create, belongs_to, has_many -- you name it
|
10
|
+
- Built with performance in mind: all objects are lazy-loaded by default
|
11
|
+
- Supports REST, SPARQL and ActiveRecord as RDF data sources
|
12
|
+
- Supports XML, N-Triples and JSON out of the box
|
13
|
+
|
14
|
+
== Installation
|
15
|
+
|
16
|
+
The prefered method of installing RDFMapper is through its gem file (requires
|
17
|
+
RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl]):
|
18
|
+
|
19
|
+
% [sudo] gem install rdf-mapper
|
20
|
+
|
21
|
+
The latest version of RDFMapper can be found at
|
22
|
+
|
23
|
+
* http://github.com/42cities/rdf-mapper
|
24
|
+
|
25
|
+
|
26
|
+
== Contribute
|
27
|
+
|
28
|
+
Please note that RDFMapper in under heavy development right now, it's not yet
|
29
|
+
production safe. Any contribution (bug tickets, code patches) is more than
|
30
|
+
welcome. Email us at team@42cities.com or submit a ticket on
|
31
|
+
GitHub[http://github.com/42cities/rdf-mapper/issues]
|
32
|
+
|
33
|
+
|
34
|
+
= 5-minute crash course
|
35
|
+
|
36
|
+
=== Idea behind RDF models
|
37
|
+
|
38
|
+
Models in RDFMapper are essentially RDF nodes that have an ID and at least one triple
|
39
|
+
with an rdf:type predicate. Consider the following example:
|
40
|
+
|
41
|
+
<http://example.org/people/237643> rdf:type <http://www.example.org/schema#Person>
|
42
|
+
<http://example.org/people/237643> example:name "John Smith"
|
43
|
+
<http://example.org/people/237643> example:age "27"^^xsd:integer
|
44
|
+
|
45
|
+
This set of triples defines a node (with an ID of <http://example.org/people/237643>)
|
46
|
+
that has three 'attributes': `example:name`, `example:age`, and `rdf:type`. Now `rdf:type`
|
47
|
+
predicate tells us that there's a class (<http://www.example.org/schema#Person>)
|
48
|
+
with more or less predefined behavior. And our node (<http://example.org/people/237643>)
|
49
|
+
is an instance of that class. We could replicate the same logic in Ruby:
|
50
|
+
|
51
|
+
class Person
|
52
|
+
attr_accessor :id
|
53
|
+
attr_accessor :name
|
54
|
+
attr_accessor :age
|
55
|
+
end
|
56
|
+
|
57
|
+
person = Person.new
|
58
|
+
person.id = "http://example.org/people/237643"
|
59
|
+
person.name = "John Smith"
|
60
|
+
person.age = 27
|
61
|
+
|
62
|
+
That's essentially what RDFMapper does. It accepts RDF triples (XML or N-triples),
|
63
|
+
creates instances, assigns attributes and binds models together (via Rails-like
|
64
|
+
belongs_to and has_many associations).
|
65
|
+
|
66
|
+
|
67
|
+
=== Defining a model
|
68
|
+
|
69
|
+
Before you start working with RDFMapper, you need to define at least one model. The only
|
70
|
+
required setting is its namespace (think XML namespace) or type (think rdf:type). If you
|
71
|
+
specify the namespace, it will be used by the model itself (to figure out its rdf:type)
|
72
|
+
and by its attributes (to figure out RDF predicates).
|
73
|
+
|
74
|
+
class Person < RDFMapper::Model
|
75
|
+
namespace "http://example.org/#"
|
76
|
+
attribute :name, :type => :text
|
77
|
+
attribute :homepage, :type => :uri, :predicate => 'http://xmlns.com/foaf/0.1/homepage'
|
78
|
+
end
|
79
|
+
|
80
|
+
Person.namespace #=> #<RDF::Vocabulary(http://example.org/#)>
|
81
|
+
Person.type #=> #<RDF::URI(http://example.org/#Person)>
|
82
|
+
|
83
|
+
Person.name.type #=> #<RDF::URI(http://example.org/#name")>
|
84
|
+
Person.homepage.type #=> #<RDF::URI(http://xmlns.com/foaf/0.1/homepage)>
|
85
|
+
|
86
|
+
For more information on {RDF::URI}[http://rdf.rubyforge.org/RDF/URI.html],
|
87
|
+
{RDF::Vocabulary}[http://rdf.rubyforge.org/RDF/Vocabulary.html] and other
|
88
|
+
classes within RDF namespace, refer to {RDF.rb documentation}[http://rdf.rubyforge.org/].
|
89
|
+
|
90
|
+
|
91
|
+
=== Defining the data source
|
92
|
+
|
93
|
+
By this moment you can work with RDFMapper models with no additional settings.
|
94
|
+
However, if you want to load, save and search for your objects, you need to
|
95
|
+
specify their data source. RDFMapper comes with 3 different flavors of data
|
96
|
+
sources: REST, SPARQL and Rails.
|
97
|
+
|
98
|
+
* SPARQL [read-only] -- the standard for RDF data.
|
99
|
+
RDFMapper will query specified SPARQL server over HTTP using standard SPARQL
|
100
|
+
syntax. Currently it supports only a few functions (no subqueries, updates,
|
101
|
+
aggregates, etc.)
|
102
|
+
|
103
|
+
* REST [read-only] -- good old HTTP-based data
|
104
|
+
storage. It assumes that an object's ID (which is an URI) is the place to
|
105
|
+
look when you want to get object's properties. For example, if an object has
|
106
|
+
an ID `http://example.org/people/237643`, RDFMapper will download data from
|
107
|
+
this address and parse any RDF triples it finds along the way.
|
108
|
+
|
109
|
+
* Rails [read/write] -- gets the data from an
|
110
|
+
ActiveRecord model (that is Rails model). This adapter assumes an RDFMapper
|
111
|
+
model has a 'mirror' ActiveRecord model with the same attributes and
|
112
|
+
associations.
|
113
|
+
|
114
|
+
Assigning data source to a model is easy:
|
115
|
+
|
116
|
+
class Person < RDFMapper::Model
|
117
|
+
adapter :rails # There should be a `Person` class that subclasses ActiveRecord::Base
|
118
|
+
end
|
119
|
+
|
120
|
+
class Person < RDFMapper
|
121
|
+
adapter :rails, :class_name => 'Employee' # ActiveRecord::Base model is called `Employee`
|
122
|
+
end
|
123
|
+
|
124
|
+
class Person < RDFMapper
|
125
|
+
adapter :sparql, {
|
126
|
+
:server => 'http://some-sparql-server.com'
|
127
|
+
:headers => { 'API-Key' => '89d7sfd9sfs' }
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
=== Searching
|
133
|
+
|
134
|
+
If you search objects by an ID, it's up to the adapter (REST, SPARQL, or Rails) to
|
135
|
+
decide what type of ID it requires (an URI, a database column or something else).
|
136
|
+
Check out the documentation for each adapter to see how works.
|
137
|
+
|
138
|
+
Person.all #=> #<PersonCollection:23784623>
|
139
|
+
Person.find('132987') #=> #<Person:217132856>
|
140
|
+
Person.find(:all, :conditions => { :name => 'John' }) #=> #<PersonCollection:32462387>
|
141
|
+
|
142
|
+
Note, the objects above are not loaded. RDFMapper will load them once you
|
143
|
+
access an attribute of a collection or an object. The following 3 objects are
|
144
|
+
loaded instantly, since RDFMapper needs to figure out what their attributes are
|
145
|
+
(in this case `nil?`, `name` and `length`).
|
146
|
+
|
147
|
+
Person.find('132987').nil? #=> false
|
148
|
+
Person.find('132987').name #=> "John"
|
149
|
+
Person.find(:all, :conditions => { :name => 'John' }).length #=> 3
|
150
|
+
|
151
|
+
You should take extra care when dealing with lazy-loaded models, since
|
152
|
+
exceptions may occur when a model is not found:
|
153
|
+
|
154
|
+
Person.find('132987') #=> #<Person:217132856>
|
155
|
+
Person.find('132987').name #=> NoMethodError: undefined method `name' for nil:NilClass
|
156
|
+
|
157
|
+
Instead, you should first check if a model exists:
|
158
|
+
|
159
|
+
@person = Person.find('132987')
|
160
|
+
@person.name unless @person.nil?
|
161
|
+
|
162
|
+
|
163
|
+
=== Working with attributes
|
164
|
+
|
165
|
+
Attributes in RDFMapper work just as you would expect them to work with just one
|
166
|
+
small exception. Since any attribute of a model is essentially an RDF triple, you
|
167
|
+
can access attributes by their predicates as well:
|
168
|
+
|
169
|
+
class Person < RDFMapper::Model
|
170
|
+
namespace "http://example.org/#"
|
171
|
+
attribute :name, :type => :text
|
172
|
+
attribute :homepage, :type => :uri, :predicate => 'http://xmlns.com/foaf/0.1/homepage'
|
173
|
+
end
|
174
|
+
|
175
|
+
instance = Person.new
|
176
|
+
instance.name #=> "John Smith"
|
177
|
+
instance[:name] #=> "John Smith"
|
178
|
+
instance['http://example.org/#name'] #=> "John Smith"
|
179
|
+
instance.homepage #=> #<RDF::URI(http://johnsmith.com/")>
|
180
|
+
instance['http://xmlns.com/foaf/0.1/homepage'] #=> #<RDF::URI(http://johnsmith.com/")>
|
181
|
+
|
182
|
+
|
183
|
+
That's pretty much all you need to know. Go try and let us know what you think!
|
184
|
+
|
185
|
+
== License
|
186
|
+
|
187
|
+
RDFMapper is free and unencumbered public domain software. For more information,
|
188
|
+
see http://unlicense.org or the accompanying UNLICENSE file.
|
data/UNLICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, contact Alex Serebryakov [serebryakov@gmail.com]
|
25
|
+
or visit <http://unlicense.org/>
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module RDFMapper
|
2
|
+
module Adapters
|
3
|
+
|
4
|
+
autoload :Rails, 'lib/adapters/rails'
|
5
|
+
autoload :REST, 'lib/adapters/rest'
|
6
|
+
autoload :SPARQL, 'lib/adapters/sparql'
|
7
|
+
|
8
|
+
##
|
9
|
+
# Instantiates and returns an instance of an adapter.
|
10
|
+
#
|
11
|
+
# @param [Symbol] name (:rails, :sparql, :rest)
|
12
|
+
# @param [Object] cls subclass of RDFMapper::Model
|
13
|
+
# @param [Hash] options options to pass on to the adapter constructor
|
14
|
+
#
|
15
|
+
# @return [Object] instance of an adapter
|
16
|
+
##
|
17
|
+
def self.register(name, cls, options = {})
|
18
|
+
self[name].new(cls, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Returns adapter's class based on specified `name` (:rails, :sparql, :rest)
|
23
|
+
#
|
24
|
+
# @return [Object]
|
25
|
+
##
|
26
|
+
def self.[](name)
|
27
|
+
case name
|
28
|
+
when :rails then Rails
|
29
|
+
when :sparql then SPARQL
|
30
|
+
when :rest then REST
|
31
|
+
else raise NameError, 'Adapter `%s` not recognized' % value.inspect
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Parent class for all adapters. Contains default constructor method
|
37
|
+
# and interface methods that each adapter should override.
|
38
|
+
##
|
39
|
+
class Base
|
40
|
+
|
41
|
+
##
|
42
|
+
# All adapters implement Logger
|
43
|
+
##
|
44
|
+
include RDFMapper::Logger
|
45
|
+
|
46
|
+
##
|
47
|
+
# Adapter implementation should override this method
|
48
|
+
##
|
49
|
+
def load(query)
|
50
|
+
raise NotImplementedError, 'Expected adapter to override `load`'
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Adapter implementation should override this method
|
55
|
+
##
|
56
|
+
def save(instance)
|
57
|
+
raise NotImplementedError, 'Expected adapter to override `save`'
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Adapter implementation should override this method
|
62
|
+
##
|
63
|
+
def reload(instance)
|
64
|
+
raise NotImplementedError, 'Expected adapter to override `save`'
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Adapter implementation should override this method
|
69
|
+
##
|
70
|
+
def update(instance)
|
71
|
+
raise NotImplementedError, 'Expected adapter to override `save`'
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Adapter implementation should override this method
|
76
|
+
##
|
77
|
+
def create(instance)
|
78
|
+
raise NotImplementedError, 'Expected adapter to override `save`'
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,307 @@
|
|
1
|
+
module RDFMapper
|
2
|
+
module Adapters
|
3
|
+
##
|
4
|
+
# [-]
|
5
|
+
##
|
6
|
+
class Rails < Base
|
7
|
+
|
8
|
+
##
|
9
|
+
# [-]
|
10
|
+
##
|
11
|
+
def initialize(cls, options = {})
|
12
|
+
@rdf, @options = cls, options
|
13
|
+
@options[:skip] ||= []
|
14
|
+
@options[:substitute] ||= { }
|
15
|
+
@options[:substitute][:id] ||= :uid
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
##
|
20
|
+
# [-]
|
21
|
+
##
|
22
|
+
def load(query)
|
23
|
+
@rdf.associations.values.select do |assoc|
|
24
|
+
assoc.belongs_to?
|
25
|
+
end.map do |assoc|
|
26
|
+
assoc.name
|
27
|
+
end.reject do |name|
|
28
|
+
@options[:skip].include?(name)
|
29
|
+
end.each do |name|
|
30
|
+
query.include!(name)
|
31
|
+
end
|
32
|
+
Query.new(query, @options).find
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# [-]
|
37
|
+
##
|
38
|
+
def save(instance)
|
39
|
+
if instance[:rails_id].nil?
|
40
|
+
obj = instance.class.find(instance.id.to_s).from(:rails)
|
41
|
+
instance[:rails_id] = obj.rails_id unless obj.nil?
|
42
|
+
end
|
43
|
+
if instance[:rails_id].nil?
|
44
|
+
create(instance)
|
45
|
+
else
|
46
|
+
update(instance)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# [-]
|
52
|
+
##
|
53
|
+
def reload(instance)
|
54
|
+
query = RDFMapper::Scope::Query.new(instance.class, :conditions => { :id => instance.id })
|
55
|
+
Query.new(query, @options).find.first
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# [-]
|
60
|
+
##
|
61
|
+
def update(instance)
|
62
|
+
query = RDFMapper::Scope::Query.new(instance.class, :conditions => instance.attributes)
|
63
|
+
Query.new(query, @options).update
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# [-]
|
68
|
+
##
|
69
|
+
def create(instance)
|
70
|
+
query = RDFMapper::Scope::Query.new(instance.class, :conditions => instance.attributes)
|
71
|
+
Query.new(query, @options).create
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def check_for_rails_id(instance)
|
78
|
+
end
|
79
|
+
|
80
|
+
class Query
|
81
|
+
|
82
|
+
include RDFMapper::Logger
|
83
|
+
|
84
|
+
def initialize(query, options = {})
|
85
|
+
@query, @options = query, options
|
86
|
+
@rails = (@options[:class_name] || @query.cls.to_s.demodulize).constantize
|
87
|
+
setup_replacements
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# [-]
|
92
|
+
##
|
93
|
+
def update
|
94
|
+
record = @rails.update(@query[:rails_id], save_options)
|
95
|
+
record_attributes(record)
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# [-]
|
100
|
+
##
|
101
|
+
def create
|
102
|
+
record = @rails.create(save_options)
|
103
|
+
record_attributes(record)
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# [-]
|
108
|
+
##
|
109
|
+
def find
|
110
|
+
@query.check(:rails_id)
|
111
|
+
#
|
112
|
+
debug 'Searching for %s with %s' % [@rails, @query.inspect]
|
113
|
+
debug 'Query: %s' % find_options.inspect
|
114
|
+
#
|
115
|
+
@rails.find(:all, find_options).map do |record|
|
116
|
+
record_attributes(record)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
##
|
124
|
+
# [-]
|
125
|
+
##
|
126
|
+
def record_attributes(record)
|
127
|
+
record_id = [:id, :rails_id].map do |name|
|
128
|
+
[name, record_value(record, name)]
|
129
|
+
end
|
130
|
+
record_props = record_properties(record)
|
131
|
+
record_assoc = record_associations(record)
|
132
|
+
Hash[record_id + record_props + record_assoc]
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# [-]
|
137
|
+
##
|
138
|
+
def save_options
|
139
|
+
Hash[@query.to_a.map do |condition|
|
140
|
+
name = @replace[condition.name]
|
141
|
+
[name, validate(condition.value)]
|
142
|
+
end.reject do |name, value|
|
143
|
+
name.nil? or value.nil?
|
144
|
+
end]
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Substitutes names of those attributes specified in `options[:substitute]`
|
149
|
+
# and raises a runtime error for attributes that could not be found in the
|
150
|
+
# database. Returns an object which can then be used with ActiveRecord::Base.find
|
151
|
+
##
|
152
|
+
def find_options #nodoc
|
153
|
+
{ :conditions => SQL.new(@query, @replace).to_a,
|
154
|
+
:order => @query.order,
|
155
|
+
:limit => @query.limit,
|
156
|
+
:offset => @query.offset,
|
157
|
+
:include => @query.include
|
158
|
+
}.delete_if { |name, value| value.nil? }
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# [-]
|
163
|
+
##
|
164
|
+
def setup_replacements #nodoc
|
165
|
+
@replace = default_replacements
|
166
|
+
@query.flatten.map do |condition|
|
167
|
+
# Original RDF name
|
168
|
+
rdf_name = condition.name
|
169
|
+
# Expected name in the DB
|
170
|
+
expected_name = @replace[rdf_name] || rdf_name
|
171
|
+
# Silently ignore attributes that are not in the DB
|
172
|
+
rails_name = activerecord_attribute?(expected_name)
|
173
|
+
@replace[rdf_name] = rails_name unless rails_name.nil?
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# [-]
|
179
|
+
##
|
180
|
+
def default_replacements
|
181
|
+
@options[:substitute].merge({ :rails_id => :id })
|
182
|
+
end
|
183
|
+
|
184
|
+
##
|
185
|
+
# [-]
|
186
|
+
##
|
187
|
+
def record_properties(record) #nodoc
|
188
|
+
@query.cls.properties.keys.map do |name|
|
189
|
+
value = record_value(record, name)
|
190
|
+
[name, value]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# [-]
|
196
|
+
##
|
197
|
+
def record_associations(record) #nodoc
|
198
|
+
@query.include.map do |name|
|
199
|
+
value = record_value(record, name)
|
200
|
+
value = value.nil? ? nil : value[@replace[:id]]
|
201
|
+
[name, value]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# [-]
|
207
|
+
##
|
208
|
+
def record_value(record, rdf_name) #nodoc
|
209
|
+
name = default_replacements[rdf_name] || rdf_name
|
210
|
+
unless record.respond_to?(name)
|
211
|
+
nil
|
212
|
+
else
|
213
|
+
record.send(name)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# [-]
|
219
|
+
##
|
220
|
+
def activerecord_attribute?(name) #nodoc
|
221
|
+
activerecord_property?(name) || activerecord_association?(name)
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# [-]
|
226
|
+
##
|
227
|
+
def activerecord_association?(name) #nodoc
|
228
|
+
reflection = @rails.reflections[name.to_sym]
|
229
|
+
if reflection.nil? or not reflection.belongs_to?
|
230
|
+
return nil
|
231
|
+
end
|
232
|
+
reflection.primary_key_name.to_sym
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# [-]
|
237
|
+
##
|
238
|
+
def activerecord_property?(name) #nodoc
|
239
|
+
@rails.column_names.include?(name.to_s) ? name.to_sym : nil
|
240
|
+
end
|
241
|
+
|
242
|
+
class SQL
|
243
|
+
|
244
|
+
def initialize(query, replace)
|
245
|
+
@query, @replace = query, replace
|
246
|
+
@text, @values = [], []
|
247
|
+
|
248
|
+
@query.to_a.map do |condition|
|
249
|
+
if condition.kind_of?(query.class)
|
250
|
+
add_query(condition)
|
251
|
+
else
|
252
|
+
add_condition(condition)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def add_query(query)
|
258
|
+
child = SQL.new(query, @replace)
|
259
|
+
unless child.text.empty?
|
260
|
+
@text.push("(%s)" % child.text)
|
261
|
+
@values.push(*child.values)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def add_condition(condition)
|
266
|
+
name = @replace[condition.name]
|
267
|
+
if name.nil?
|
268
|
+
return nil
|
269
|
+
end
|
270
|
+
if condition.value.kind_of?(Array)
|
271
|
+
@text << "%s IN (?)" % name
|
272
|
+
else
|
273
|
+
@text << "%s %s ?" % [name, condition.eq]
|
274
|
+
end
|
275
|
+
@values << validate(condition.value)
|
276
|
+
end
|
277
|
+
|
278
|
+
def validate(value) #nodoc
|
279
|
+
if value.kind_of? Array
|
280
|
+
return value.map do |item|
|
281
|
+
validate(item)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
if value.kind_of? RDFMapper::Model
|
285
|
+
value[:rails_id]
|
286
|
+
else
|
287
|
+
value
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def text
|
292
|
+
@text.join(' %s ' % @query.modifier)
|
293
|
+
end
|
294
|
+
|
295
|
+
def values
|
296
|
+
@values
|
297
|
+
end
|
298
|
+
|
299
|
+
def to_a
|
300
|
+
[text] + values
|
301
|
+
end
|
302
|
+
|
303
|
+
end # SQL
|
304
|
+
end # Query
|
305
|
+
end # Rails
|
306
|
+
end # Adapters
|
307
|
+
end # RDFMapper
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RDFMapper
|
2
|
+
module Adapters
|
3
|
+
##
|
4
|
+
# Not yet implemented
|
5
|
+
##
|
6
|
+
class REST < Base
|
7
|
+
|
8
|
+
##
|
9
|
+
# @todo. Not implemented
|
10
|
+
##
|
11
|
+
def load(query)
|
12
|
+
raise NotImplementedError, 'REST adapter is not yet implemented'
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# @todo. Not implemented
|
17
|
+
##
|
18
|
+
def save(instance)
|
19
|
+
raise NotImplementedError, 'REST adapter is not yet implemented'
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# @todo. Not implemented
|
24
|
+
##
|
25
|
+
def reload(instance)
|
26
|
+
raise NotImplementedError, 'REST adapter is not yet implemented'
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# @todo. Not implemented
|
31
|
+
##
|
32
|
+
def update(instance)
|
33
|
+
raise NotImplementedError, 'REST adapter is not yet implemented'
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# @todo. Not implemented
|
38
|
+
##
|
39
|
+
def create(instance)
|
40
|
+
raise NotImplementedError, 'REST adapter is not yet implemented'
|
41
|
+
end
|
42
|
+
|
43
|
+
end # REST
|
44
|
+
end # Adapters
|
45
|
+
end # RDFMapper
|