spira 0.0.12 → 0.5.0

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/AUTHORS CHANGED
@@ -1,3 +1,5 @@
1
1
  * Ben Lavender <blavender@gmail.com>
2
2
  * Arto Bendiken <arto@bendiken.net>
3
3
  * Nicholas J Humfrey <njh@aelius.com>
4
+ * Slava Kravchenko <https://github.com/cordawyn>
5
+ * Aymeric Brisse <https://github.com/abrisse>
data/CHANGES.md CHANGED
@@ -1,4 +1,7 @@
1
- # Changelog for Spira <http://github.com/datagraph/spira>
1
+ # Changelog for Spira <http://github.com/rdf-ruby/spira>
2
+
3
+ ## 0.3.0
4
+ * General updates to bring up to date.
2
5
 
3
6
  ## 0.0.12
4
7
  * Implemented #validate, #validate! (refactored from #save!)
@@ -33,7 +36,7 @@
33
36
  ## 0.0.8
34
37
  * Remove type checking for repository addition. More power in return for
35
38
  slightly more difficult error messages.
36
- * Repositories added via klass, *arg are now only instantiated on first use
39
+ * Repositories added via klass, \*arg are now only instantiated on first use
37
40
  instead of immediately.
38
41
  * RDF::URI#as, RDF::Node#as, Resource.for, and Resource#new can now all accept
39
42
  a block, which yields the new instance and saves it after the block.
@@ -46,7 +49,7 @@
46
49
  permitted.
47
50
 
48
51
  ## 0.0.7
49
- * Added Resource.[], an alias for Resource.for
52
+ * Added Resource.\[\], an alias for Resource.for
50
53
  * Resource.each now correctly raises an exception when a repository isn't found
51
54
 
52
55
  ## 0.0.6
@@ -1,9 +1,7 @@
1
- # Spira
1
+ # Spira [![Build Status](https://travis-ci.org/ruby-rdf/spira.png?branch=master)](http://travis-ci.org/ruby-rdf/spira)
2
2
 
3
3
  It's time to breathe life into your linked data.
4
4
 
5
- ---
6
-
7
5
  ## Synopsis
8
6
  Spira is a framework for using the information in [RDF.rb][] repositories as model
9
7
  objects. It gives you the ability to work in a resource-oriented way without
@@ -17,11 +15,9 @@ A changelog is available in the {file:CHANGES.md} file.
17
15
 
18
16
  ### Example
19
17
 
20
- class Person
21
-
22
- include Spira::Resource
18
+ class Person < Spira::Base
23
19
 
24
- base_uri "http://example.org/example/people"
20
+ configure :base_uri => "http://example.org/example/people"
25
21
 
26
22
  property :name, :predicate => FOAF.name, :type => String
27
23
  property :age, :predicate => FOAF.age, :type => Integer
@@ -48,6 +44,49 @@ A changelog is available in the {file:CHANGES.md} file.
48
44
  * No need to put everything about an object into Spira
49
45
  * Easy to use a resource as multiple models
50
46
 
47
+ ## ActiveModel integration
48
+
49
+ This is a version of Spira that makes use of ActiveModel. The goal of this version is
50
+ to replace all the internals of Spira with ActiveModel hooks, and thus get rid of
51
+ superfluous code and increase compatibility with Rails stack. I want it to be
52
+ a drop-in replacement for ActiveRecord or any other mature ORM solution they use
53
+ with Ruby on Rails.
54
+
55
+ Although I've been trying to make the impact of this transition to be as little
56
+ as possible, there are a few changes that you should be aware of:
57
+
58
+ * Read the comments on "new_record?" and "reload" methods. They are key methods in
59
+ understanding how Spira is working with the repository. Basically, a Spira record
60
+ is new, if the repository has no statements with this record as subject. This means,
61
+ that *the repository is queried every time you invoke "new_record?"*.
62
+ Also note that if Spira.repository is not set, your Spira resource will always be "new".
63
+ Also note that instantiating a new Spira resource sends a query to the repository,
64
+ if it is set, but should work just fine even if it's not (until you try to "save" it).
65
+ * Customary Rails' record manipulation methods are preferred now.
66
+ This means, you should use more habitual "save", "destroy", "update_attributes", etc.
67
+ instead of the "save!", "destroy!", "update", "update!" and others, as introduced
68
+ by the original Spira gem.
69
+ * Callbacks are now handled by ActiveModel. Previous ways of defining them are
70
+ no longer valid. This also introduces the "before_", "after_" and "around_" callbacks
71
+ as well as their "_validation", "_save", "_update" and "_create" companions for you to enjoy.
72
+ * Validations are also handled by ActiveModel. With all the helper methods you have in
73
+ ActiveRecord.
74
+ * A spira resource (class) must be defined by *inheriting* it from Spira::Base.
75
+ Using "include Spira::Resource" is *temporarily* broken, but will be back at some point,
76
+ with improvements and stuff.
77
+ * "after/before_create" callbacks are *not* called when only the properties of your
78
+ Spira resource are getting persisted. That is, you may create a "type"-less Spira resource,
79
+ assign properties to it, then #save it -- "_create" callbacks will not be triggered,
80
+ because Spira cannot infer a resource definition ("resource - RDF.type - type")
81
+ for such resource and will only persist its properties.
82
+ Although this is how the original Spira behaves too, I thought I'd state it
83
+ explicitly here before you start freaking out.
84
+ * Configuration options "base_uri", "default_vocabulary" and "repository_name" are
85
+ now configured via "configure" method (see the examples below).
86
+ * A couple of (not so) subtle changes:
87
+ 1) Global caching is gone. This means that "artist.works.first.artist" (reverse lookup)
88
+ does not return the original artist, but its copy retrieved from the database.
89
+
51
90
  ## Getting Started
52
91
 
53
92
  The easiest way to work with Spira is to install it via Rubygems:
@@ -64,16 +103,14 @@ without the `RDF::` prefix. For example:
64
103
 
65
104
  require 'spira'
66
105
 
67
- class CD
68
- include Spira::Resource
69
- base_uri 'http://example.org/cds'
106
+ class CD < Spira::Base
107
+ configure :base_uri => 'http://example.org/cds'
70
108
  property :name, :predicate => DC.title, :type => XSD.string
71
109
  property :artist, :predicate => URI.new('http://example.org/vocab/artist'), :type => :artist
72
110
  end
73
111
 
74
- class Artist
75
- include Spira::Resource
76
- base_uri 'http://example.org/artists'
112
+ class Artist < Spira::Base
113
+ configure :base_uri => 'http://example.org/artists'
77
114
  property :name, :predicate => DC.title, :type => XSD.string
78
115
  has_many :cds, :predicate => URI.new('http://example.org/vocab/published_cd'), :type => XSD.string
79
116
  end
@@ -147,8 +184,7 @@ Example
147
184
 
148
185
  A class with a `type` set is assigned an `RDF.type` on creation and saving.
149
186
 
150
- class Album
151
- include Spira::Resource
187
+ class Album < Spira::Base
152
188
  type URI.new('http://example.org/types/album')
153
189
  property :name, :predicate => DC.title
154
190
  end
@@ -162,6 +198,21 @@ In addition, one can count the members of a class with a `type` defined:
162
198
 
163
199
  Album.count #=> 1
164
200
 
201
+
202
+ It is possible to assign multiple types to a Spira class:
203
+
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
208
+
209
+ All assigned types are accessible via "types":
210
+
211
+ Man.types #=> #<Set: {#<RDF::URI:0xd5ebc0(http://example.org/people/father)>, #<RDF::URI:0xd5e4b8(http://example.org/people/cop)>}>
212
+
213
+ Also note that "type" actually returns a first type from the list of types.
214
+
215
+
165
216
  #### property
166
217
 
167
218
  A class declares property members with the `property` function. See `Property Options` for more information.
@@ -174,10 +225,9 @@ A class declares list members with the `has_many` function. See `Property Optio
174
225
 
175
226
  A class with a `default_vocabulary` set will transparently create predicates for defined properties:
176
227
 
177
- class Song
178
- include Spira::Resource
179
- default_vocabulary URI.new('http://example.org/vocab')
180
- base_uri 'http://example.org/songs'
228
+ class Song < Spira::Base
229
+ configure :default_vocabulary => URI.new('http://example.org/vocab'),
230
+ :base_uri => 'http://example.org/songs'
181
231
  property :title
182
232
  property :author, :type => :artist
183
233
  end
@@ -189,22 +239,17 @@ A class with a `default_vocabulary` set will transparently create predicates for
189
239
  dancing_queen.has_predicate?(RDF::URI.new('http://example.org/vocab/title')) #=> true
190
240
  dancing_queen.has_predicate?(RDF::URI.new('http://example.org/vocab/artist')) #=> true
191
241
 
192
- #### default_source
242
+ #### repository_name
193
243
 
194
244
  Provides this class with a default repository to use instead of the `:default`
195
245
  repository if one is not set.
196
246
 
197
- class Song
198
- default_source :songs
247
+ class Song < Spira::Base
248
+ configure :repository_name => :songs
199
249
  end
200
250
 
201
251
  See 'Defining Repositories' for more information.
202
252
 
203
- #### validate
204
-
205
- Provides the name of a function which does some sort of validation. See
206
- 'Validations' for more information.
207
-
208
253
  ### Property Options
209
254
 
210
255
  Spira classes can have properties that are either singular or a list. For a
@@ -256,16 +301,15 @@ is usually expressed as a URI. Here is the built-in Spira Integer class:
256
301
  RDF::Literal.new(value)
257
302
  end
258
303
 
259
- register_alias XSD.integer
304
+ register_alias RDF::XSD.integer
260
305
  end
261
306
  end
262
307
 
263
308
  Classes can now use this particular type like so:
264
309
 
265
- class Test
266
- include Spira::Resource
310
+ class Test < Spira::Base
267
311
  property :test1, :type => Integer
268
- property :test2, :type => XSD.integer
312
+ property :test2, :type => RDF::XSD.integer
269
313
  end
270
314
 
271
315
  Spira classes include the Spira::Types namespace, where several default types
@@ -297,8 +341,7 @@ turn an RDF::Value into a ruby object, and vice versa.
297
341
  end
298
342
  end
299
343
 
300
- class MyClass
301
- include Spira::Resource
344
+ class MyClass < Spira::Base
302
345
  property :property1, :type => MyModule::MyType
303
346
  end
304
347
 
@@ -311,8 +354,12 @@ You can define multiple repositories with Spira, and use more than one at a time
311
354
  Spira.add_repository! :cds, RDF::Sesame::Repository.new 'some_server'
312
355
  Spira.add_repository! :albums, RDF::Repository.load('some_file.nt')
313
356
 
314
- CD.repository = :cds
315
- Album.repository = :albums
357
+ class CD < Spira::Base
358
+ configure :repository_name => :cds
359
+ end
360
+ class Album < Spira::Base
361
+ configure :repository_name => :albums
362
+ end
316
363
 
317
364
  Objects can reference each other cross-repository.
318
365
 
@@ -323,94 +370,23 @@ If no repository has been specified, the `:default` repository will be used.
323
370
  Artist.repository == repo #=> true
324
371
 
325
372
  Classes can specify a default repository to use other than `:default` with the
326
- `default_source` function:
373
+ `repository_name` function:
327
374
 
328
- class Song
329
- default_source :songs
375
+ class Song < Spira::Base
376
+ configure :repository_name => :songs
330
377
  end
331
378
 
332
379
  Song.repository #=> nil, won't use :default
333
380
 
334
381
  ## Validations
335
382
 
336
- You may declare any number of validation functions with the `validate` function.
337
- Before saving, each referenced validation will be run, and the instance's
338
- {Spira::Errors} object will be populated with any errors. You can use the
339
- built in `assert` and assert helpers such as `assert_set` and
340
- `asssert_numeric`.
341
-
342
-
343
- class CD
344
- validate :is_real_music
345
- def is_real_music
346
- assert(artist.name != "Nickelback", :artist, "cannot be Nickelback")
347
- end
348
-
349
- validate :track_count_numeric
350
- def track_count_numeric
351
- assert_numeric(track_count)
352
- end
353
- end
354
-
355
- dancing_queen.artist = nickelback
356
- dancing_queen.save! #=> ValidationError
357
- dancing_queen.errors.each #=> ["artist cannot be Nickelback"]
358
-
359
- dancing_queen.artist = abba
360
- dancing_queen.save! #=> true
383
+ [removed]
384
+ See the description of ActiveModel::Validations.
361
385
 
362
386
  ## Hooks
363
387
 
364
- Spira supports `before_create`, `after_create`, `after_update`, `before_save`,
365
- `after_save`, and `before_destroy` hooks:
366
-
367
- class CD
368
- def before_save
369
- self.publisher = 'No publisher set' if self.publisher.nil?
370
- end
371
- end
372
-
373
- The `after_update` hook only fires on the `update` method, not simple property
374
- accessors (to allow you to easily set properties in these without going into a
375
- recursive loop):
376
-
377
- class CD
378
- def after_update
379
- self.artist = 'Queen' # every artist should be Queen!
380
- end
381
- end
382
-
383
- # ...snip ...
384
- dancing_queen.artist
385
- #=> "ABBA"
386
- dancing_queen.name = "Dancing Queen"
387
- dancing_queen.artist
388
- #=> "ABBA"
389
- dancing_queen.update(:name => "Dancing Queen")
390
- dancing_queen.artist
391
- #=> "Queen"
392
-
393
- ## Inheritance
394
-
395
- You can extend Spira resources without a problem:
396
-
397
- class BoxedSet < CD
398
- include Spira::Resource
399
- property cd_count, :predicate => CD.count, :type => Integer
400
- end
401
-
402
- You can also make Spira modules and include them into other classes:
403
-
404
- module Media
405
- include Spira::Resource
406
- property :format, :predicate => Media.format
407
- end
408
-
409
- class CD
410
- include Spira::Resource
411
- include Media
412
- end
413
-
388
+ [removed]
389
+ See the description of ActiveModel::Callbacks.
414
390
 
415
391
  ## Using Model Objects as RDF.rb Objects
416
392
 
@@ -427,16 +403,11 @@ There are a number of ways to ask for help. In declining order of preference:
427
403
  * You can post issues to the Github issue queue
428
404
  * (there might one day be a google group or other such support channel, but not yet)
429
405
 
430
- ## Authors, Development, and License
431
-
432
- #### Authors
433
- * Ben Lavender <blavender@gmail.com>
434
-
435
- #### 'License'
406
+ ## 'License'
436
407
  Spira is free and unemcumbered software released into the public
437
408
  domain. For more information, see the included UNLICENSE file.
438
409
 
439
- #### Contributing
410
+ ## Contributing
440
411
  Fork it on Github and go. Please make sure you're kosher with the UNLICENSE
441
412
  file before contributing.
442
413
 
@@ -0,0 +1,37 @@
1
+ module RDF
2
+ class URI
3
+ ##
4
+ # Create a projection of this URI as the given Spira::Resource class.
5
+ # Equivalent to `klass.for(self, *args)`
6
+ #
7
+ # @example Instantiating a URI as a Spira Resource
8
+ # RDF::URI('http://example.org/person/bob').as(Person)
9
+ # @param [Class] klass
10
+ # @param [*Any] args Any arguments to pass to klass.for
11
+ # @yield [self] Executes a given block and calls `#save!`
12
+ # @yieldparam [self] self The newly created instance
13
+ # @return [Klass] An instance of klass
14
+ def as(klass, *args, &block)
15
+ raise ArgumentError, "#{klass} is not a Spira resource" unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Base)
16
+ klass.for(self, *args, &block)
17
+ end
18
+ end
19
+
20
+ class Node
21
+ ##
22
+ # Create a projection of this Node as the given Spira::Resource class.
23
+ # Equivalent to `klass.for(self, *args)`
24
+ #
25
+ # @example Instantiating a blank node as a Spira Resource
26
+ # RDF::Node.new.as(Person)
27
+ # @param [Class] klass
28
+ # @param [*Any] args Any arguments to pass to klass.for
29
+ # @yield [self] Executes a given block and calls `#save!`
30
+ # @yieldparam [self] self The newly created instance
31
+ # @return [Klass] An instance of klass
32
+ def as(klass, *args)
33
+ raise ArgumentError, "#{klass} is not a Spira resource" unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Base)
34
+ klass.for(self, *args)
35
+ end
36
+ end
37
+ end
data/lib/spira.rb CHANGED
@@ -1,6 +1,8 @@
1
- require 'rdf'
2
- require 'promise'
3
- require 'spira/exceptions'
1
+ require "rdf"
2
+ require "rdf/ext/uri"
3
+ require "promise"
4
+ require "spira/exceptions"
5
+ require "spira/utils"
4
6
 
5
7
  ##
6
8
  # Spira is a framework for building projections of RDF data into Ruby classes.
@@ -9,66 +11,59 @@ require 'spira/exceptions'
9
11
  # @see http://rdf.rubyforge.org
10
12
  # @see http://github.com/bhuga/spira
11
13
  # @see Spira::Resource
12
- module Spira
13
14
 
14
- ##
15
- # The list of repositories available for Spira resources
16
- #
17
- # @see http://rdf.rubyforge.org/RDF/Repository.html
18
- # @return [Hash{Symbol => RDF::Repository}]
19
- # @private
20
- def repositories
21
- settings[:repositories] ||= {}
15
+ module RDF
16
+ class Repository
22
17
  end
23
- module_function :repositories
18
+ end
19
+
20
+ module Spira
21
+
22
+ autoload :Base, 'spira/base'
23
+ autoload :Type, 'spira/type'
24
+ autoload :Types, 'spira/types'
25
+ autoload :VERSION, 'spira/version'
24
26
 
25
27
  ##
26
28
  # The list of all property types available for Spira resources
27
- #
29
+ #
28
30
  # @see Spira::Types
29
31
  # @return [Hash{Symbol => Spira::Type}]
30
32
  def types
31
- settings[:types] ||= {}
33
+ @types ||= {}
32
34
  end
33
35
  module_function :types
34
36
 
35
- ##
36
- # A thread-local hash for storing settings. Used by Resource classes.
37
- #
38
- # @see Spira::Resource
39
- # @see Spira.repositories
40
- # @see Spira.types
41
- def settings
42
- Thread.current[:spira] ||= {}
43
- end
44
- module_function :settings
45
-
46
37
  ##
47
38
  # Add a repository to Spira's list of repositories.
48
39
  #
49
40
  # @overload add_repository(name, repo)
50
- # @param [Symbol] name The name of this repository
51
- # @param [RDF::Repository] repo An RDF::Repository
41
+ # @param [Symbol] name The name of this repository
42
+ # @param [RDF::Repository] repo
43
+ #
52
44
  # @overload add_repository(name, klass, *args)
53
- # @param [Symbol] name The name of this repository
54
- # @param [RDF::Repository, Class] repo A Class that inherits from RDF::Repository
55
- # @param [*Object] The list of arguments to instantiate the class
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
+ #
56
51
  # @example Adding an ntriples file as a repository
57
52
  # Spira.add_repository(:default, RDF::Repository.load('http://datagraph.org/jhacker/foaf.nt'))
58
53
  # @example Adding an empty repository to be instantiated on use
59
54
  # Spira.add_repository(:default, RDF::Repository)
60
55
  # @return [Void]
61
- # @see RDF::Repository
62
56
  def add_repository(name, klass, *args)
63
- repositories[name] = case klass
57
+ repositories[name] =
58
+ case klass
64
59
  when Class
65
60
  promise { klass.new(*args) }
66
61
  else
67
62
  klass
68
- end
69
- if (name == :default) && settings[:repositories][name].nil?
70
- warn "WARNING: Adding nil default repository"
71
- end
63
+ end
64
+ if (name == :default) && repository(name).nil?
65
+ warn "WARNING: Adding nil default repository"
66
+ end
72
67
  end
73
68
  alias_method :add_repository!, :add_repository
74
69
  module_function :add_repository, :add_repository!
@@ -91,10 +86,23 @@ module Spira
91
86
  # @return [Void]
92
87
  # @private
93
88
  def clear_repositories!
94
- settings[:repositories] = {}
89
+ @repositories = {}
95
90
  end
96
91
  module_function :clear_repositories!
97
92
 
93
+
94
+ private
95
+
96
+ ##
97
+ # The list of repositories available for Spira resources
98
+ #
99
+ # @see http://rdf.rubyforge.org/RDF/Repository.html
100
+ # @return [Hash{Symbol => RDF::Repository}]
101
+ def repositories
102
+ @repositories ||= {}
103
+ end
104
+ module_function :repositories
105
+
98
106
  ##
99
107
  # Alias a property type to another. This allows a range of options to be
100
108
  # specified for a property type which all reference one Spira::Type
@@ -102,55 +110,8 @@ module Spira
102
110
  # @param [Any] new The new symbol or reference
103
111
  # @param [Any] original The type the new symbol should refer to
104
112
  # @return [Void]
105
- # @private
106
113
  def type_alias(new, original)
107
- types[new] = original
114
+ types[new] = original
108
115
  end
109
116
  module_function :type_alias
110
-
111
- autoload :Resource, 'spira/resource'
112
- autoload :Base, 'spira/base'
113
- autoload :Type, 'spira/type'
114
- autoload :Types, 'spira/types'
115
- autoload :Errors, 'spira/errors'
116
- autoload :VERSION, 'spira/version'
117
-
118
- end
119
-
120
- module RDF
121
- class URI
122
- ##
123
- # Create a projection of this URI as the given Spira::Resource class.
124
- # Equivalent to `klass.for(self, *args)`
125
- #
126
- # @example Instantiating a URI as a Spira Resource
127
- # RDF::URI('http://example.org/person/bob').as(Person)
128
- # @param [Class] klass
129
- # @param [*Any] args Any arguments to pass to klass.for
130
- # @yield [self] Executes a given block and calls `#save!`
131
- # @yieldparam [self] self The newly created instance
132
- # @return [Klass] An instance of klass
133
- def as(klass, *args, &block)
134
- raise ArgumentError, "#{klass} is not a Spira resource" unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Resource)
135
- klass.for(self, *args, &block)
136
- end
137
- end
138
-
139
- class Node
140
- ##
141
- # Create a projection of this Node as the given Spira::Resource class.
142
- # Equivalent to `klass.for(self, *args)`
143
- #
144
- # @example Instantiating a blank node as a Spira Resource
145
- # RDF::Node.new.as(Person)
146
- # @param [Class] klass
147
- # @param [*Any] args Any arguments to pass to klass.for
148
- # @yield [self] Executes a given block and calls `#save!`
149
- # @yieldparam [self] self The newly created instance
150
- # @return [Klass] An instance of klass
151
- def as(klass, *args)
152
- raise ArgumentError, "#{klass} is not a Spira resource" unless klass.is_a?(Class) && klass.ancestors.include?(Spira::Resource)
153
- klass.for(self, *args)
154
- end
155
- end
156
117
  end