spira 0.5.0 → 0.7

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YmI4NjdiNmExYjA4YTc4MjQ0MzhiM2E0MmE3MWNmY2NhNDkwZGI0OA==
5
+ data.tar.gz: !binary |-
6
+ NTMzZGYyOWNjOWY0ODQ3ZGVhNjVkYjNhMDZjZTljN2ExMGU3Mzc1ZQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MDJlZjY1MjZiNDNiOGMyYTE0YzJiYzU5MzBjMWEyY2I2NmE1NjBlNmY2Y2Jk
10
+ NDhhZjQxYTFkZDIzNTllMjdkNzgxMzI2OTdhZmM4M2I1OTIxY2RlYjNjZDhh
11
+ ZGE0OGU3NzQ0OTMyNzg1ZDI1Y2U4OWEyN2ZlMjk4ZjI0NTE1MjA=
12
+ data.tar.gz: !binary |-
13
+ ZTRhNWRkMGFmZDdhYWVmMzk1YjNmMWMyMzYzZjYxOWNkYTY3NTUyMTUyYzdm
14
+ ODE4NWM1YTFhNmZiYjRjN2YyMDYxYmZjZGQyY2VkZTA1NTU0NDAzYjA5ZGU5
15
+ NDc0YzU2NWFlNzNjZjA2MTJlMzRhMjUyYzhkNjIzYzIzMmZhMzM=
data/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  It's time to breathe life into your linked data.
4
4
 
5
+ ## Need Help? Use our Google Group
6
+
7
+ If you have any question on how to use Spira, please use the [Google Group ruby-rdf](https://groups.google.com/forum/#!forum/ruby-rdf).
8
+
5
9
  ## Synopsis
6
10
  Spira is a framework for using the information in [RDF.rb][] repositories as model
7
11
  objects. It gives you the ability to work in a resource-oriented way without
@@ -15,29 +19,32 @@ A changelog is available in the {file:CHANGES.md} file.
15
19
 
16
20
  ### Example
17
21
 
18
- class Person < Spira::Base
22
+ ```ruby
23
+ class Person < Spira::Base
19
24
 
20
- configure :base_uri => "http://example.org/example/people"
21
-
22
- property :name, :predicate => FOAF.name, :type => String
23
- property :age, :predicate => FOAF.age, :type => Integer
25
+ configure :base_uri => "http://example.org/example/people"
24
26
 
25
- end
27
+ property :name, :predicate => FOAF.name, :type => String
28
+ property :age, :predicate => FOAF.age, :type => Integer
29
+
30
+ end
31
+
32
+ Spira.repository = RDF::Repository.new
26
33
 
27
- bob = RDF::URI("http://example.org/people/bob").as(Person)
28
- bob.age = 15
29
- bob.name = "Bob Smith"
30
- bob.save!
34
+ bob = RDF::URI("http://example.org/people/bob").as(Person)
35
+ bob.age = 15
36
+ bob.name = "Bob Smith"
37
+ bob.save!
38
+
39
+ bob.each_statement {|s| puts s}
40
+ #=> RDF::Statement:0x80abb80c(<http://example.org/example/people/bob> <http://xmlns.com/foaf/0.1/name> "Bob Smith" .)
41
+ #=> RDF::Statement:0x80abb8fc(<http://example.org/example/people/bob> <http://xmlns.com/foaf/0.1/age> "15"^^<http://www.w3.org/2001/XMLSchema#integer> .)
42
+ ```
31
43
 
32
- bob.each_statement {|s| puts s}
33
- #=> RDF::Statement:0x80abb80c(<http://example.org/example/people/bob> <http://xmlns.com/foaf/0.1/name> "Bob Smith" .)
34
- #=> RDF::Statement:0x80abb8fc(<http://example.org/example/people/bob> <http://xmlns.com/foaf/0.1/age> "15"^^<http://www.w3.org/2001/XMLSchema#integer> .)
35
-
36
44
  ### Features
37
45
 
38
46
  * Extensible validations system
39
47
  * Extensible types system
40
- * Easy to use multiple data sources
41
48
  * Easy to adapt models to existing data
42
49
  * Open-world semantics
43
50
  * Objects are still RDF.rb-compatible enumerable objects
@@ -81,7 +88,7 @@ as possible, there are a few changes that you should be aware of:
81
88
  for such resource and will only persist its properties.
82
89
  Although this is how the original Spira behaves too, I thought I'd state it
83
90
  explicitly here before you start freaking out.
84
- * Configuration options "base_uri", "default_vocabulary" and "repository_name" are
91
+ * Configuration options "base_uri", "default_vocabulary" are
85
92
  now configured via "configure" method (see the examples below).
86
93
  * A couple of (not so) subtle changes:
87
94
  1) Global caching is gone. This means that "artist.works.first.artist" (reverse lookup)
@@ -101,35 +108,41 @@ To use Spira, define model classes for your RDF data. Spira classes include
101
108
  RDF, and thus have access to all `RDF::Vocabulary` classes and `RDF::URI`
102
109
  without the `RDF::` prefix. For example:
103
110
 
104
- require 'spira'
105
-
106
- class CD < Spira::Base
107
- configure :base_uri => 'http://example.org/cds'
108
- property :name, :predicate => DC.title, :type => XSD.string
109
- property :artist, :predicate => URI.new('http://example.org/vocab/artist'), :type => :artist
110
- end
111
+ ```ruby
112
+ require 'spira'
111
113
 
112
- class Artist < Spira::Base
113
- configure :base_uri => 'http://example.org/artists'
114
- property :name, :predicate => DC.title, :type => XSD.string
115
- has_many :cds, :predicate => URI.new('http://example.org/vocab/published_cd'), :type => XSD.string
116
- end
117
-
118
- Then use your model classes, in a way more or less similar to any number of ORMs:
119
-
120
- cd = CD.for("queens-greatest-hits")
121
- cd.name = "Queen's greatest hits"
122
- artist = Artist.for("queen")
123
- artist.name = "Queen"
124
-
125
- cd.artist = artist
126
- cd.save!
127
- artist.cds = [cd]
128
- artist.save!
129
-
130
- queen = Artist.for('queen')
131
- hits = CD.for 'queens-greatest-hits'
132
- hits.artist == artist == queen
114
+ class CD < Spira::Base
115
+ configure :base_uri => 'http://example.org/cds'
116
+ property :name, :predicate => DC.title, :type => XSD.string
117
+ property :artist, :predicate => URI.new('http://example.org/vocab/artist'), :type => :artist
118
+ end
119
+
120
+ class Artist < Spira::Base
121
+ configure :base_uri => 'http://example.org/artists'
122
+ property :name, :predicate => DC.title, :type => XSD.string
123
+ has_many :cds, :predicate => URI.new('http://example.org/vocab/published_cd'), :type => XSD.string
124
+ end
125
+ ```
126
+
127
+ Then define a Spira repository (see [Defining Repositories](#defining-repositories)) to use your model classes, in a way more or less similar to any number of ORMs:
128
+
129
+ ```ruby
130
+ Spira.repository = RDF::Repository.new
131
+
132
+ cd = CD.for("queens-greatest-hits")
133
+ cd.name = "Queen's greatest hits"
134
+ artist = Artist.for("queen")
135
+ artist.name = "Queen"
136
+
137
+ cd.artist = artist
138
+ cd.save!
139
+ artist.cds = [cd]
140
+ artist.save!
141
+
142
+ queen = Artist.for('queen')
143
+ hits = CD.for 'queens-greatest-hits'
144
+ hits.artist == artist == queen
145
+ ```
133
146
 
134
147
  ### URIs and Blank Nodes
135
148
 
@@ -138,17 +151,23 @@ Spira instances have a subject, which is either a URI or a blank node.
138
151
  A class with a base URI can instantiate with a string (or anything, via to_s),
139
152
  and it will have a URI representation:
140
153
 
141
- Artist.for('queen')
154
+ ```ruby
155
+ Artist.for('queen')
156
+ ```
142
157
 
143
158
  However, a class is not required to have a base URI, and even if it does, it
144
159
  can always access classes with a full URI:
145
160
 
146
- nk = Artist.for(RDF::URI.new('http://example.org/my-hidden-cds/new-kids'))
161
+ ```ruby
162
+ nk = Artist.for(RDF::URI.new('http://example.org/my-hidden-cds/new-kids'))
163
+ ```
147
164
 
148
165
  If you have a URI that you would like to look at as a Spira resource, you can instantiate it from the URI:
149
166
 
150
- RDF::URI.new('http://example.org/my-hidden-cds/new-kids').as(Artist)
151
- # => <Artist @subject=http://example.org/my-hidden-cds/new-kids>
167
+ ```ruby
168
+ RDF::URI.new('http://example.org/my-hidden-cds/new-kids').as(Artist)
169
+ # => <Artist @subject=http://example.org/my-hidden-cds/new-kids>
170
+ ```
152
171
 
153
172
  Any call to 'for' with a valid identifier will always return an object with nil
154
173
  fields. It's a way of looking at a given resource, not a closed-world mapping
@@ -156,16 +175,20 @@ to one.
156
175
 
157
176
  You can also use blank nodes more or less as you would a URI:
158
177
 
159
- remix_artist = Artist.for(RDF::Node.new)
160
- # => <Artist @subject=#<RDF::Node:0xd1d314(_:g13751060)>>
161
- RDF::Node.new.as(Artist)
162
- # => <Artist @subject=#<RDF::Node:0xd1d314(_:g13751040)>>
178
+ ```ruby
179
+ remix_artist = Artist.for(RDF::Node.new)
180
+ # => <Artist @subject=#<RDF::Node:0xd1d314(_:g13751060)>>
181
+ RDF::Node.new.as(Artist)
182
+ # => <Artist @subject=#<RDF::Node:0xd1d314(_:g13751040)>>
183
+ ```
163
184
 
164
185
  Finally, you can create an instance of a Spira projection with #new, and you'll
165
186
  get an instance with a shiny new blank node subject:
166
187
 
167
- formerly_known_as_prince = Artist.new
168
- # => <Artist @subject=#<RDF::Node:0xd1d314(_:g13747140)>>
188
+ ```ruby
189
+ formerly_known_as_prince = Artist.new
190
+ # => <Artist @subject=#<RDF::Node:0xd1d314(_:g13747140)>>
191
+ ```
169
192
 
170
193
  ### Class Options
171
194
 
@@ -177,38 +200,50 @@ A class with a `base_uri` set (either an `RDF::URI` or a `String`) will
177
200
  use that URI as a base URI for non-absolute `for` calls.
178
201
 
179
202
  Example
180
- CD.for 'queens-greatest-hits' # is the same as...
181
- CD.for RDF::URI.new('http://example.org/cds/queens-greatest-hits')
203
+ ```ruby
204
+ CD.for 'queens-greatest-hits' # is the same as...
205
+ CD.for RDF::URI.new('http://example.org/cds/queens-greatest-hits')
206
+ ```
182
207
 
183
208
  #### type
184
209
 
185
210
  A class with a `type` set is assigned an `RDF.type` on creation and saving.
186
211
 
187
- class Album < Spira::Base
188
- type URI.new('http://example.org/types/album')
189
- property :name, :predicate => DC.title
190
- end
212
+ ```ruby
213
+ class Album < Spira::Base
214
+ type URI.new('http://example.org/types/album')
215
+ property :name, :predicate => DC.title
216
+ end
191
217
 
192
- rolling_stones = Album.for RDF::URI.new('http://example.org/cds/rolling-stones-hits')
193
- # See RDF.rb at http://rdf.rubyforge.org/RDF/Enumerable.html for more information about #has_predicate?
194
- rolling_stones.has_predicate?(RDF.type) #=> true
195
- Album.type #=> RDF::URI('http://example.org/types/album')
218
+ Spira.repository = RDF::Repository.new
219
+
220
+ rolling_stones = Album.for RDF::URI.new('http://example.org/cds/rolling-stones-hits')
221
+ # See RDF.rb at http://rdf.rubyforge.org/RDF/Enumerable.html for more information about #has_predicate?
222
+ rolling_stones.has_predicate?(RDF.type) #=> true
223
+ Album.type #=> RDF::URI('http://example.org/types/album')
224
+ `
196
225
 
197
226
  In addition, one can count the members of a class with a `type` defined:
198
227
 
199
- Album.count #=> 1
228
+ ```ruby
229
+ Album.count #=> 1
230
+ ```
200
231
 
201
232
 
202
233
  It is possible to assign multiple types to a Spira class:
203
234
 
204
- class Man < Spira::Base
205
- type RDF::URI.new('http://example.org/people/father')
206
- type RDF::URI.new('http://example.org/people/cop')
207
- end
235
+ ```ruby
236
+ class Man < Spira::Base
237
+ type RDF::URI.new('http://example.org/people/father')
238
+ type RDF::URI.new('http://example.org/people/cop')
239
+ end
240
+ ```
208
241
 
209
242
  All assigned types are accessible via "types":
210
243
 
211
- Man.types #=> #<Set: {#<RDF::URI:0xd5ebc0(http://example.org/people/father)>, #<RDF::URI:0xd5e4b8(http://example.org/people/cop)>}>
244
+ ```ruby
245
+ Man.types #=> #<Set: {#<RDF::URI:0xd5ebc0(http://example.org/people/father)>, #<RDF::URI:0xd5e4b8(http://example.org/people/cop)>}>
246
+ ```
212
247
 
213
248
  Also note that "type" actually returns a first type from the list of types.
214
249
 
@@ -225,30 +260,23 @@ A class declares list members with the `has_many` function. See `Property Optio
225
260
 
226
261
  A class with a `default_vocabulary` set will transparently create predicates for defined properties:
227
262
 
228
- class Song < Spira::Base
229
- configure :default_vocabulary => URI.new('http://example.org/vocab'),
230
- :base_uri => 'http://example.org/songs'
231
- property :title
232
- property :author, :type => :artist
233
- end
263
+ ```ruby
264
+ class Song < Spira::Base
265
+ configure :default_vocabulary => URI.new('http://example.org/vocab'),
266
+ :base_uri => 'http://example.org/songs'
267
+ property :title
268
+ property :author, :type => :artist
269
+ end
234
270
 
235
- dancing_queen = Song.for 'dancing-queen'
236
- dancing_queen.title = "Dancing Queen"
237
- dancing_queen.artist = abba
238
- # See RDF::Enumerable for #has_predicate?
239
- dancing_queen.has_predicate?(RDF::URI.new('http://example.org/vocab/title')) #=> true
240
- dancing_queen.has_predicate?(RDF::URI.new('http://example.org/vocab/artist')) #=> true
271
+ Spira.repository = RDF::Repository.new
241
272
 
242
- #### repository_name
243
-
244
- Provides this class with a default repository to use instead of the `:default`
245
- repository if one is not set.
246
-
247
- class Song < Spira::Base
248
- configure :repository_name => :songs
249
- end
250
-
251
- See 'Defining Repositories' for more information.
273
+ dancing_queen = Song.for 'dancing-queen'
274
+ dancing_queen.title = "Dancing Queen"
275
+ dancing_queen.artist = abba
276
+ # See RDF::Enumerable for #has_predicate?
277
+ dancing_queen.has_predicate?(RDF::URI.new('http://example.org/vocab/title')) #=> true
278
+ dancing_queen.has_predicate?(RDF::URI.new('http://example.org/vocab/artist')) #=> true
279
+ ```
252
280
 
253
281
  ### Property Options
254
282
 
@@ -268,6 +296,32 @@ Property always takes a symbol name as a name, and a variable list of options.
268
296
  **Types** below. Default: `Any`
269
297
  * `:predicate`: The predicate to use for this type. This can be any RDF URI.
270
298
  This option is required unless the `default_vocabulary` has been used.
299
+ * `:localized`: Indicates if the property is multilingual. See 'Localized Properties'
300
+
301
+ #### Localized Properties
302
+
303
+ A localized property allows to define a value per language. It only works with
304
+ properties having a single item, ie defined with `property`.
305
+
306
+ ```ruby
307
+ class Article < Spira::Base
308
+ property :label, :localized => true
309
+ end
310
+
311
+ Spira.repository = RDF::Repository.new
312
+
313
+ # default locale :en
314
+ random_article = Article.for 'random-article'
315
+ random_article.label = "A label in english"
316
+ i18n.locale = :fr
317
+ random_article.label = "Un libellé en français"
318
+
319
+ random_article.label_native
320
+ # #=> [#<RDF::Literal:0xdb47c8("A label in english"@en)>, #<RDF::Literal:0xe5c3d8("Un libellé en français"@fr)>]
321
+
322
+ random_article.label_with_locales
323
+ # #=> {:en=>"A label in english", :fr=>"Un libellé en français"}
324
+ ```
271
325
 
272
326
  ### Types
273
327
 
@@ -288,29 +342,33 @@ A type class includes Spira::Type, and can implement serialization and
288
342
  deserialization functions, and register aliases to themselves if their datatype
289
343
  is usually expressed as a URI. Here is the built-in Spira Integer class:
290
344
 
291
- module Spira::Types
292
- class Integer
293
-
294
- include Spira::Type
295
-
296
- def self.unserialize(value)
297
- value.object
298
- end
299
-
300
- def self.serialize(value)
301
- RDF::Literal.new(value)
302
- end
303
-
304
- register_alias RDF::XSD.integer
305
- end
345
+ ```ruby
346
+ module Spira::Types
347
+ class Integer
348
+
349
+ include Spira::Type
350
+
351
+ def self.unserialize(value)
352
+ value.object
353
+ end
354
+
355
+ def self.serialize(value)
356
+ RDF::Literal.new(value)
306
357
  end
307
358
 
359
+ register_alias RDF::XSD.integer
360
+ end
361
+ end
362
+ ```
363
+
308
364
  Classes can now use this particular type like so:
309
365
 
310
- class Test < Spira::Base
311
- property :test1, :type => Integer
312
- property :test2, :type => RDF::XSD.integer
313
- end
366
+ ```ruby
367
+ class Test < Spira::Base
368
+ property :test1, :type => Integer
369
+ property :test2, :type => RDF::XSD.integer
370
+ end
371
+ ```
314
372
 
315
373
  Spira classes include the Spira::Types namespace, where several default types
316
374
  are implemented:
@@ -328,55 +386,67 @@ The default type for a Spira property is `Spira::Types::Any`, which uses
328
386
  You can implement your own types as well. Your class' serialize method should
329
387
  turn an RDF::Value into a ruby object, and vice versa.
330
388
 
331
- module MyModule
332
- class MyType
333
- include Spira::Type
334
- def self.serialize(value)
335
- ...
336
- end
337
-
338
- def self.unserialize(value)
339
- ...
340
- end
341
- end
389
+ ```ruby
390
+ module MyModule
391
+ class MyType
392
+ include Spira::Type
393
+ def self.serialize(value)
394
+ ...
342
395
  end
343
396
 
344
- class MyClass < Spira::Base
345
- property :property1, :type => MyModule::MyType
397
+ def self.unserialize(value)
398
+ ...
346
399
  end
400
+ end
401
+ end
402
+
403
+ class MyClass < Spira::Base
404
+ property :property1, :type => MyModule::MyType
405
+ end
406
+ ```
347
407
 
348
408
  ## Defining Repositories
349
409
 
350
- You can define multiple repositories with Spira, and use more than one at a time:
410
+ You can work on any kind of RDF::Repository with Spira:
351
411
 
352
- require 'rdf/ntriples'
353
- require 'rdf/sesame'
354
- Spira.add_repository! :cds, RDF::Sesame::Repository.new 'some_server'
355
- Spira.add_repository! :albums, RDF::Repository.load('some_file.nt')
412
+ ```ruby
413
+ require 'rdf/ntriples'
414
+ require 'rdf/sesame'
356
415
 
357
- class CD < Spira::Base
358
- configure :repository_name => :cds
359
- end
360
- class Album < Spira::Base
361
- configure :repository_name => :albums
362
- end
416
+ class Album < Spira::Base
417
+ end
363
418
 
364
- Objects can reference each other cross-repository.
419
+ Spira.repository = RDF::Sesame::Repository.new 'some_server'
420
+ ...
365
421
 
366
- If no repository has been specified, the `:default` repository will be used.
422
+ Spira.repository = RDF::Repository.load('some_file.nt')
423
+ ...
367
424
 
368
- repo = RDF::Repository.new
369
- Spira.add_repository! :default, repo
370
- Artist.repository == repo #=> true
425
+ Spira.using_repository(RDF::Repository.load('some_file.nt')) do
426
+ ...
427
+ end
428
+ ```
371
429
 
372
- Classes can specify a default repository to use other than `:default` with the
373
- `repository_name` function:
430
+ Spira.repository is thread-safe, which means that each thread stores its own instance.
431
+ It allows you to work on multiple repositories at the same time:
374
432
 
375
- class Song < Spira::Base
376
- configure :repository_name => :songs
377
- end
433
+ ```ruby
434
+ threads = []
435
+ repositories = [RDF::Repository.new, RDF::Repository.new, RDF::Repository.new]
436
+
437
+ repositories.each do |repository|
438
+ threads << Thread.new(repository) do |repository|
439
+ Spira.repository = repository
440
+
441
+ album = Album.for("http://theperson.com/album/random_name")
442
+ album.year = 1950 + (rand*100).to_i
443
+ album.save!
444
+ end
445
+ end
378
446
 
379
- Song.repository #=> nil, won't use :default
447
+ threads.map(&:join)
448
+ repositories.map(&:size).join(', ') # 1, 1, 1
449
+ ```
380
450
 
381
451
  ## Validations
382
452
 
@@ -408,8 +478,19 @@ Spira is free and unemcumbered software released into the public
408
478
  domain. For more information, see the included UNLICENSE file.
409
479
 
410
480
  ## Contributing
411
- Fork it on Github and go. Please make sure you're kosher with the UNLICENSE
412
- file before contributing.
481
+ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange development and release activity. All submissions _must_ be on a feature branch based on the _develop_ branch to ease staging and integration.
482
+
483
+ * Do your best to adhere to the existing coding conventions and idioms.
484
+ * Don't use hard tabs, and don't leave trailing whitespace on any line.
485
+ * Do document every method you add using [YARD][] annotations. Read the
486
+ [tutorial][YARD-GS] or just look at the existing code for examples.
487
+ * Don't touch the `.gemspec`, `VERSION` or `AUTHORS` files. If you need to
488
+ change them, do so on your private branch only.
489
+ * Do feel free to add yourself to the `CREDITS` file and the corresponding
490
+ list in the the `README`. Alphabetical order applies.
491
+ * Do note that in order for us to merge any non-trivial changes (as a rule
492
+ of thumb, additions larger than about 15 lines of code), we need an
493
+ explicit [public domain dedication][PDD] on record from you.
413
494
 
414
495
  [public-rdf-ruby w3c mailing list]: http://lists.w3.org/Archives/Public/public-rdf-ruby/
415
- [RDF.rb]: http://rdf.rubyforge.org
496
+ [RDF.rb]: http://rubygems.org/gems/rdf
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.0
data/lib/spira/base.rb CHANGED
@@ -323,6 +323,35 @@ module Spira
323
323
  end
324
324
  end
325
325
 
326
+ ## Localized properties functions
327
+
328
+ def merge_localized_property(name, arg)
329
+ values = read_attribute("#{name}_native")
330
+ values.delete_if { |s| s.language == I18n.locale }
331
+ values << serialize_localized_property(arg, I18n.locale)
332
+ values
333
+ end
334
+
335
+ def serialize_localized_property(value, locale)
336
+ RDF::Literal.new(value, :language => locale)
337
+ end
338
+
339
+ def unserialize_localized_properties(values, locale)
340
+ v = values.detect { |s| s.language == locale || s.simple? }
341
+ v && v.object
342
+ end
343
+
344
+ def hash_localized_properties(values)
345
+ values.inject({}) do |out, v|
346
+ out[v.language] = v.object
347
+ out
348
+ end
349
+ end
350
+
351
+ def serialize_hash_localized_properties(values)
352
+ values.map { |lang, property| RDF::Literal.new(property, :language => lang) }
353
+ end
354
+
326
355
  # Build a Ruby value from an RDF value.
327
356
  def build_value(node, type)
328
357
  klass = classize_resource(type)
@@ -3,22 +3,13 @@ module Spira
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  module ClassMethods
6
- ##
7
- # Repository name for this class
8
- #
9
- # @return [Symbol]
10
- def repository_name
11
- # should be redefined in children, if required
12
- # see also Spira::Resource.configure :repository option
13
- :default
14
- end
15
6
 
16
7
  ##
17
8
  # The current repository for this class
18
9
  #
19
10
  # @return [RDF::Repository, nil]
20
11
  def repository
21
- Spira.repository(repository_name)
12
+ Spira.repository || raise(NoRepositoryError)
22
13
  end
23
14
 
24
15
  ##
@@ -233,11 +224,6 @@ module Spira
233
224
  when identifier.respond_to?(:to_uri) && !identifier.is_a?(RDF::URI)
234
225
  id_for(identifier.to_uri)
235
226
  # see comment with #to_uri above, this might be a fragment
236
- when identifier.is_a?(Addressable::URI)
237
- id_for(RDF::URI.intern(identifier))
238
- # This is a #to_s or a URI fragment with a base uri. We'll treat them the same.
239
- # FIXME: when #/ makes it into RDF.rb proper, this can all be wrapped
240
- # into the one case statement above.
241
227
  else
242
228
  uri = identifier.is_a?(RDF::URI) ? identifier : RDF::URI.intern(identifier.to_s)
243
229
  case
@@ -377,13 +363,10 @@ module Spira
377
363
  # This resource will block if the underlying repository
378
364
  # blocks the next time it accesses attributes.
379
365
  #
380
- # If repository is not defined, the attributes are just not set,
381
- # instead of raising a Spira::NoRepositoryError.
382
- #
383
366
  # NB: "props" argument is ignored, it is handled in Base
384
367
  #
385
368
  def reload(props = {})
386
- sts = self.class.repository && self.class.repository.query(:subject => subject)
369
+ sts = self.class.repository.query(:subject => subject)
387
370
  self.class.properties.each do |name, options|
388
371
  name = name.to_s
389
372
  if sts
@@ -7,7 +7,6 @@ module Spira
7
7
  # Configuration options for the Spira::Resource:
8
8
  #
9
9
  # @params[Hash] options
10
- # :repository_name :: name of the repository to use
11
10
  # :base_uri :: base URI to be used for the resource
12
11
  # :default_vocabulary :: default vocabulary to use for the properties
13
12
  # defined for this resource
@@ -16,8 +15,7 @@ module Spira
16
15
  #
17
16
  def configure(options = {})
18
17
  singleton_class.class_eval do
19
- { :repository_name => options[:repository_name],
20
- :base_uri => options[:base_uri],
18
+ { :base_uri => options[:base_uri],
21
19
  :default_vocabulary => options[:default_vocabulary]
22
20
  }.each do |name, value|
23
21
  # redefine reader methods only when required,
@@ -80,17 +78,23 @@ module Spira
80
78
  # @see Spira::Type
81
79
  # @return [Void]
82
80
  def property(name, opts = {})
83
- unset_has_many(name)
84
- predicate = predicate_for(opts[:predicate], name)
85
- type = type_for(opts[:type])
86
- properties[name] = HashWithIndifferentAccess.new(:predicate => predicate, :type => type)
81
+ if opts.delete(:localized)
82
+ raise 'Only Spira::Types::Any properties can accept the :localized option' unless type_for(opts[:type]) == Spira::Types::Any
83
+ define_localized_property_methods(name, opts)
84
+ has_many "#{name}_native", opts.merge(:type => Spira::Types::Native)
85
+ else
86
+ unset_has_many(name)
87
+ predicate = predicate_for(opts[:predicate], name)
88
+ type = type_for(opts[:type])
89
+ properties[name] = HashWithIndifferentAccess.new(:predicate => predicate, :type => type)
87
90
 
88
- define_attribute_method name
89
- define_method "#{name}=" do |arg|
90
- write_attribute name, arg
91
- end
92
- define_method name do
93
- read_attribute name
91
+ define_attribute_method name
92
+ define_method "#{name}=" do |arg|
93
+ write_attribute name, arg
94
+ end
95
+ define_method name do
96
+ read_attribute name
97
+ end
94
98
  end
95
99
  end
96
100
 
@@ -120,7 +124,6 @@ module Spira
120
124
  end
121
125
  end
122
126
 
123
-
124
127
  private
125
128
 
126
129
  # Unset a has_many relation if it exists. Allow to redefine the cardinality of a relation in a subClass
@@ -134,6 +137,32 @@ module Spira
134
137
  end
135
138
  end
136
139
 
140
+ ##
141
+ # Create the localized specific getter/setter for a given property
142
+ #
143
+ # @private
144
+ def define_localized_property_methods(name, opts)
145
+ define_method "#{name}=" do |arg|
146
+ new_value = merge_localized_property(name, arg)
147
+ write_attribute "#{name}_native", new_value
148
+ end
149
+
150
+ define_method name do
151
+ value = read_attribute("#{name}_native")
152
+ unserialize_localized_properties(value, I18n.locale)
153
+ end
154
+
155
+ define_method "#{name}_with_locales" do
156
+ value = read_attribute("#{name}_native")
157
+ hash_localized_properties(value)
158
+ end
159
+
160
+ define_method "#{name}_with_locales=" do |arg|
161
+ value = serialize_hash_localized_properties(arg)
162
+ write_attribute "#{name}_native", value
163
+ end
164
+ end
165
+
137
166
  ##
138
167
  # Determine the predicate for a property based on the given predicate, name, and default vocabulary
139
168
  #
data/lib/spira.rb CHANGED
@@ -12,11 +12,6 @@ require "spira/utils"
12
12
  # @see http://github.com/bhuga/spira
13
13
  # @see Spira::Resource
14
14
 
15
- module RDF
16
- class Repository
17
- end
18
- end
19
-
20
15
  module Spira
21
16
 
22
17
  autoload :Base, 'spira/base'
@@ -35,73 +30,51 @@ module Spira
35
30
  module_function :types
36
31
 
37
32
  ##
38
- # Add a repository to Spira's list of repositories.
39
- #
40
- # @overload add_repository(name, repo)
41
- # @param [Symbol] name The name of this repository
42
- # @param [RDF::Repository] repo
33
+ # The RDF::Repository used (reader)
43
34
  #
44
- # @overload add_repository(name, klass, *args)
45
- # @param [Symbol] name The name of this repository
46
- # @param [Class] klass
47
- # A Class that inherits from `RDF::Repository`
48
- # @param [Array] args
49
- # The list of arguments to instantiate the class
50
- #
51
- # @example Adding an ntriples file as a repository
52
- # Spira.add_repository(:default, RDF::Repository.load('http://datagraph.org/jhacker/foaf.nt'))
53
- # @example Adding an empty repository to be instantiated on use
54
- # Spira.add_repository(:default, RDF::Repository)
55
- # @return [Void]
56
- def add_repository(name, klass, *args)
57
- repositories[name] =
58
- case klass
59
- when Class
60
- promise { klass.new(*args) }
61
- else
62
- klass
63
- end
64
- if (name == :default) && repository(name).nil?
65
- warn "WARNING: Adding nil default repository"
66
- end
35
+ # @return [RDF::Repository]
36
+ # @see RDF::Repository
37
+ def repository
38
+ Thread.current[:repository]
67
39
  end
68
- alias_method :add_repository!, :add_repository
69
- module_function :add_repository, :add_repository!
40
+ module_function :repository
70
41
 
71
42
  ##
72
- # The RDF::Repository for the named repository
43
+ # The RDF::Repository used (reader)
73
44
  #
74
- # @param [Symbol] name The name of the repository
75
45
  # @return [RDF::Repository]
76
46
  # @see RDF::Repository
77
- def repository(name)
78
- repositories[name]
47
+ def repository=(repository)
48
+ Thread.current[:repository] = repository
79
49
  end
80
- module_function :repository
50
+ module_function :repository=
81
51
 
82
52
  ##
83
- # Clear all repositories from Spira's knowledge. Use it if you want, but
53
+ # Clear the repository from Spira's knowledge. Use it if you want, but
84
54
  # it's really here for testing.
85
55
  #
86
56
  # @return [Void]
87
57
  # @private
88
- def clear_repositories!
89
- @repositories = {}
58
+ def clear_repository!
59
+ Spira.repository = nil
90
60
  end
91
- module_function :clear_repositories!
92
-
93
-
94
- private
61
+ module_function :clear_repository!
95
62
 
96
- ##
97
- # The list of repositories available for Spira resources
63
+ # Execute a block on a specific repository
98
64
  #
99
- # @see http://rdf.rubyforge.org/RDF/Repository.html
100
- # @return [Hash{Symbol => RDF::Repository}]
101
- def repositories
102
- @repositories ||= {}
65
+ # @param [RDF::Repository] repository the repository to work on
66
+ # @param [Symbol] name the repository name
67
+ # @yield the block with the instructions while using the repository
68
+ def using_repository(repo)
69
+ old_repository = Spira.repository
70
+ Spira.repository = repo
71
+ yield if block_given?
72
+ ensure
73
+ Spira.repository = old_repository
103
74
  end
104
- module_function :repositories
75
+ module_function :using_repository
76
+
77
+ private
105
78
 
106
79
  ##
107
80
  # Alias a property type to another. This allows a range of options to be
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spira
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
5
- prerelease:
4
+ version: '0.7'
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ben Lavender
@@ -10,196 +9,186 @@ autorequire:
10
9
  bindir:
11
10
  - bin
12
11
  cert_chain: []
13
- date: 2013-03-01 00:00:00.000000000 Z
12
+ date: 2013-12-09 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
- name: rdf-spec
15
+ name: bundler
16
+ type: :development
17
17
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
18
  requirements:
20
19
  - - ~>
21
20
  - !ruby/object:Gem::Version
22
21
  version: '1.0'
23
- type: :development
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
25
  - - ~>
29
26
  - !ruby/object:Gem::Version
30
27
  version: '1.0'
31
28
  - !ruby/object:Gem::Dependency
32
- name: rspec
29
+ name: rdf-spec
30
+ type: :development
33
31
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
32
  requirements:
36
33
  - - ~>
37
34
  - !ruby/object:Gem::Version
38
- version: 2.12.0
35
+ version: '1.1'
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '1.1'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
39
44
  type: :development
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ~>
48
+ - !ruby/object:Gem::Version
49
+ version: 2.14.0
40
50
  prerelease: false
41
51
  version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
52
  requirements:
44
53
  - - ~>
45
54
  - !ruby/object:Gem::Version
46
- version: 2.12.0
55
+ version: 2.14.0
47
56
  - !ruby/object:Gem::Dependency
48
57
  name: yard
58
+ type: :development
49
59
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
60
  requirements:
52
61
  - - ~>
53
62
  - !ruby/object:Gem::Version
54
63
  version: 0.8.3
55
- type: :development
56
64
  prerelease: false
57
65
  version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
66
  requirements:
60
67
  - - ~>
61
68
  - !ruby/object:Gem::Version
62
69
  version: 0.8.3
63
70
  - !ruby/object:Gem::Dependency
64
71
  name: redcarpet
72
+ type: :development
65
73
  requirement: !ruby/object:Gem::Requirement
66
- none: false
67
74
  requirements:
68
75
  - - ~>
69
76
  - !ruby/object:Gem::Version
70
77
  version: 2.2.2
71
- type: :development
72
78
  prerelease: false
73
79
  version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
80
  requirements:
76
81
  - - ~>
77
82
  - !ruby/object:Gem::Version
78
83
  version: 2.2.2
79
84
  - !ruby/object:Gem::Dependency
80
85
  name: guard
86
+ type: :development
81
87
  requirement: !ruby/object:Gem::Requirement
82
- none: false
83
88
  requirements:
84
89
  - - ~>
85
90
  - !ruby/object:Gem::Version
86
91
  version: 1.2.3
87
- type: :development
88
92
  prerelease: false
89
93
  version_requirements: !ruby/object:Gem::Requirement
90
- none: false
91
94
  requirements:
92
95
  - - ~>
93
96
  - !ruby/object:Gem::Version
94
97
  version: 1.2.3
95
98
  - !ruby/object:Gem::Dependency
96
99
  name: guard-rspec
100
+ type: :development
97
101
  requirement: !ruby/object:Gem::Requirement
98
- none: false
99
102
  requirements:
100
103
  - - ~>
101
104
  - !ruby/object:Gem::Version
102
105
  version: 1.1.0
103
- type: :development
104
106
  prerelease: false
105
107
  version_requirements: !ruby/object:Gem::Requirement
106
- none: false
107
108
  requirements:
108
109
  - - ~>
109
110
  - !ruby/object:Gem::Version
110
111
  version: 1.1.0
111
112
  - !ruby/object:Gem::Dependency
112
113
  name: guard-ctags-bundler
114
+ type: :development
113
115
  requirement: !ruby/object:Gem::Requirement
114
- none: false
115
116
  requirements:
116
117
  - - ~>
117
118
  - !ruby/object:Gem::Version
118
119
  version: 0.1.1
119
- type: :development
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
- none: false
123
122
  requirements:
124
123
  - - ~>
125
124
  - !ruby/object:Gem::Version
126
125
  version: 0.1.1
127
126
  - !ruby/object:Gem::Dependency
128
127
  name: rdf
128
+ type: :runtime
129
129
  requirement: !ruby/object:Gem::Requirement
130
- none: false
131
130
  requirements:
132
131
  - - ~>
133
132
  - !ruby/object:Gem::Version
134
- version: '1.0'
135
- type: :runtime
133
+ version: '1.1'
136
134
  prerelease: false
137
135
  version_requirements: !ruby/object:Gem::Requirement
138
- none: false
139
136
  requirements:
140
137
  - - ~>
141
138
  - !ruby/object:Gem::Version
142
- version: '1.0'
139
+ version: '1.1'
143
140
  - !ruby/object:Gem::Dependency
144
141
  name: rdf-isomorphic
142
+ type: :runtime
145
143
  requirement: !ruby/object:Gem::Requirement
146
- none: false
147
144
  requirements:
148
145
  - - ~>
149
146
  - !ruby/object:Gem::Version
150
- version: '1.0'
151
- type: :runtime
147
+ version: '1.1'
152
148
  prerelease: false
153
149
  version_requirements: !ruby/object:Gem::Requirement
154
- none: false
155
150
  requirements:
156
151
  - - ~>
157
152
  - !ruby/object:Gem::Version
158
- version: '1.0'
153
+ version: '1.1'
159
154
  - !ruby/object:Gem::Dependency
160
155
  name: promise
156
+ type: :runtime
161
157
  requirement: !ruby/object:Gem::Requirement
162
- none: false
163
158
  requirements:
164
159
  - - ~>
165
160
  - !ruby/object:Gem::Version
166
161
  version: 0.3.0
167
- type: :runtime
168
162
  prerelease: false
169
163
  version_requirements: !ruby/object:Gem::Requirement
170
- none: false
171
164
  requirements:
172
165
  - - ~>
173
166
  - !ruby/object:Gem::Version
174
167
  version: 0.3.0
175
168
  - !ruby/object:Gem::Dependency
176
169
  name: activemodel
170
+ type: :runtime
177
171
  requirement: !ruby/object:Gem::Requirement
178
- none: false
179
172
  requirements:
180
173
  - - ~>
181
174
  - !ruby/object:Gem::Version
182
175
  version: '3'
183
- type: :runtime
184
176
  prerelease: false
185
177
  version_requirements: !ruby/object:Gem::Requirement
186
- none: false
187
178
  requirements:
188
179
  - - ~>
189
180
  - !ruby/object:Gem::Version
190
181
  version: '3'
191
182
  - !ruby/object:Gem::Dependency
192
183
  name: activesupport
184
+ type: :runtime
193
185
  requirement: !ruby/object:Gem::Requirement
194
- none: false
195
186
  requirements:
196
187
  - - ~>
197
188
  - !ruby/object:Gem::Version
198
189
  version: '3'
199
- type: :runtime
200
190
  prerelease: false
201
191
  version_requirements: !ruby/object:Gem::Requirement
202
- none: false
203
192
  requirements:
204
193
  - - ~>
205
194
  - !ruby/object:Gem::Version
@@ -215,62 +204,62 @@ files:
215
204
  - AUTHORS
216
205
  - README.md
217
206
  - UNLICENSE
218
- - lib/rdf/ext/uri.rb
219
- - lib/spira/association_reflection.rb
220
- - lib/spira/base.rb
207
+ - VERSION
208
+ - lib/spira.rb
209
+ - lib/spira/validations.rb
210
+ - lib/spira/resource.rb
211
+ - lib/spira/types.rb
221
212
  - lib/spira/exceptions.rb
222
- - lib/spira/persistence.rb
223
213
  - lib/spira/reflections.rb
224
- - lib/spira/resource.rb
225
- - lib/spira/serialization.rb
226
- - lib/spira/type.rb
227
- - lib/spira/types/any.rb
228
- - lib/spira/types/boolean.rb
229
- - lib/spira/types/date.rb
230
- - lib/spira/types/dateTime.rb
214
+ - lib/spira/version.rb
215
+ - lib/spira/validations/uniqueness.rb
216
+ - lib/spira/base.rb
217
+ - lib/spira/association_reflection.rb
218
+ - lib/spira/utils.rb
219
+ - lib/spira/types/native.rb
231
220
  - lib/spira/types/decimal.rb
232
- - lib/spira/types/float.rb
221
+ - lib/spira/types/string.rb
222
+ - lib/spira/types/nonPositiveInteger.rb
233
223
  - lib/spira/types/gYear.rb
234
- - lib/spira/types/int.rb
235
- - lib/spira/types/integer.rb
236
- - lib/spira/types/long.rb
237
- - lib/spira/types/native.rb
238
224
  - lib/spira/types/negativeInteger.rb
225
+ - lib/spira/types/date.rb
226
+ - lib/spira/types/boolean.rb
239
227
  - lib/spira/types/nonNegativeInteger.rb
240
- - lib/spira/types/nonPositiveInteger.rb
228
+ - lib/spira/types/float.rb
241
229
  - lib/spira/types/positiveInteger.rb
242
- - lib/spira/types/string.rb
230
+ - lib/spira/types/any.rb
231
+ - lib/spira/types/integer.rb
232
+ - lib/spira/types/int.rb
243
233
  - lib/spira/types/uri.rb
244
- - lib/spira/types.rb
245
- - lib/spira/utils.rb
246
- - lib/spira/validations/uniqueness.rb
247
- - lib/spira/validations.rb
248
- - lib/spira/version.rb
249
- - lib/spira.rb
250
- homepage: http://spira.rubyforge.org
234
+ - lib/spira/types/dateTime.rb
235
+ - lib/spira/types/long.rb
236
+ - lib/spira/serialization.rb
237
+ - lib/spira/type.rb
238
+ - lib/spira/persistence.rb
239
+ - lib/rdf/ext/uri.rb
240
+ homepage: http://ruby-rdf.github.io/spira/
251
241
  licenses:
252
242
  - Public Domain
243
+ metadata: {}
253
244
  post_install_message:
254
245
  rdoc_options: []
255
246
  require_paths:
256
247
  - lib
257
248
  required_ruby_version: !ruby/object:Gem::Requirement
258
- none: false
259
249
  requirements:
260
250
  - - ! '>='
261
251
  - !ruby/object:Gem::Version
262
252
  version: 1.9.2
263
253
  required_rubygems_version: !ruby/object:Gem::Requirement
264
- none: false
265
254
  requirements:
266
255
  - - ! '>='
267
256
  - !ruby/object:Gem::Version
268
257
  version: '0'
269
258
  requirements: []
270
259
  rubyforge_project: spira
271
- rubygems_version: 1.8.25
260
+ rubygems_version: 2.1.11
272
261
  signing_key:
273
- specification_version: 3
262
+ specification_version: 4
274
263
  summary: A framework for using the information in RDF.rb repositories as model objects.
275
264
  test_files: []
276
265
  has_rdoc: yard