Giter VIP home page Giter VIP logo

spira's Introduction

Spira

Gem Version Build Status Coverage Status Code Climate

It's time to breathe life into your linked data.

Need Help? Use our Google Group

If you have any question on how to use Spira, please use the Google Group ruby-rdf.

Synopsis

Spira is a framework for using the information in RDF.rb repositories as model objects. It gives you the ability to work in a resource-oriented way without losing access to statement-oriented nature of linked data, if you so choose. It can be used either to access existing RDF data in a resource-oriented way, or to create a new store of RDF data based on simple defaults.

Example

require 'spira'
require 'rdf/vocab'

class Person < Spira::Base

  configure base_uri: "http://example.org/example/people"

  property :name, predicate: RDF::Vocab::FOAF.name, type: String
  property :age,  predicate: RDF::Vocab::FOAF.age,  type: Integer

end

Spira.repository = RDF::Repository.new

bob = RDF::URI("http://example.org/people/bob").as(Person)
bob.age  = 15
bob.name = "Bob Smith"
bob.save!

bob.each_statement {|s| puts s}
#=> RDF::Statement:0x80abb80c(<http://example.org/example/people/bob> <http://xmlns.com/foaf/0.1/name> "Bob Smith" .)
#=> RDF::Statement:0x80abb8fc(<http://example.org/example/people/bob> <http://xmlns.com/foaf/0.1/age> "15"^^<http://www.w3.org/2001/XMLSchema#integer> .)

Features

  • Extensible validations system
  • Extensible types system
  • Easy to adapt models to existing data
  • Open-world semantics
  • Objects are still RDF.rb-compatible enumerable objects
  • No need to put everything about an object into Spira
  • Easy to use a resource as multiple models

ActiveModel integration

This is a version of Spira that makes use of ActiveModel. The goal of this version is to replace all the internals of Spira with ActiveModel hooks, and thus get rid of superfluous code and increase compatibility with Rails stack. I want it to be a drop-in replacement for ActiveRecord or any other mature ORM solution they use with Ruby on Rails.

Although I've been trying to make the impact of this transition to be as little as possible, there are a few changes that you should be aware of:

  • Read the comments on "new_record?" and "reload" methods. They are key methods in understanding how Spira is working with the repository. Basically, a Spira record is new, if the repository has no statements with this record as subject. This means, that the repository is queried every time you invoke "new_record?". Also note that if Spira.repository is not set, your Spira resource will always be "new". Also note that instantiating a new Spira resource sends a query to the repository, if it is set, but should work just fine even if it's not (until you try to "save" it).
  • Customary Rails' record manipulation methods are preferred now. This means, you should use more habitual "save", "destroy", "update_attributes", etc. instead of the "save!", "destroy!", "update", "update!" and others, as introduced by the original Spira gem.
  • Callbacks are now handled by ActiveModel. Previous ways of defining them are no longer valid. This also introduces the "before_", "after_" and "around_" callbacks as well as their "_validation", "_save", "_update" and "_create" companions for you to enjoy.
  • Validations are also handled by ActiveModel. With all the helper methods you have in ActiveRecord.
  • A spira resource (class) must be defined by inheriting it from Spira::Base. Using "include Spira::Resource" is temporarily broken, but will be back at some point, with improvements and stuff.
  • "after/before_create" callbacks are not called when only the properties of your Spira resource are getting persisted. That is, you may create a "type"-less Spira resource, assign properties to it, then #save it -- "_create" callbacks will not be triggered, because Spira cannot infer a resource definition ("resource - RDF.type - type") for such resource and will only persist its properties. Although this is how the original Spira behaves too, I thought I'd state it explicitly here before you start freaking out.
  • Configuration options "base_uri", "default_vocabulary" are now configured via "configure" method (see the examples below).
  • A couple of (not so) subtle changes:
    1. Global caching is gone. This means that "artist.works.first.artist" (reverse lookup) does not return the original artist, but its copy retrieved from the database.

Getting Started

The easiest way to work with Spira is to install it via Rubygems:

$ sudo gem install spira

Downloads will be available on the github project page.

Defining Model Classes

To use Spira, define model classes for your RDF data. Spira classes include RDF, and thus have access to all RDF::Vocabulary classes and RDF::URI without the RDF:: prefix. For example:

require 'spira'
require 'rdf/vocab'
    
class CD < Spira::Base
  configure base_uri: 'http://example.org/cds'
  property :name,   predicate: RDF::Vocab::DC.title,   type: XSD.string
  property :artist, predicate: RDF::URI.new('http://example.org/vocab/artist'), type: :artist
end

class Artist < Spira::Base
  configure base_uri: 'http://example.org/artists'
  property :name, predicate: RDF::Vocab::DC.title, type: XSD.string
  has_many :cds,  predicate: RDF::URI.new('http://example.org/vocab/published_cd'), type: XSD.string
end

Then define a Spira repository (see Defining Repositories) to use your model classes, in a way more or less similar to any number of ORMs:

Spira.repository = RDF::Repository.new

cd = CD.for("queens-greatest-hits")
cd.name = "Queen's greatest hits"
artist = Artist.for("queen")
artist.name = "Queen"

cd.artist = artist
cd.save!
artist.cds = [cd]
artist.save!

queen = Artist.for('queen')
hits = CD.for 'queens-greatest-hits'
hits.artist == artist == queen

URIs and Blank Nodes

Spira instances have a subject, which is either a URI or a blank node.

A class with a base URI can instantiate with a string (or anything, via to_s), and it will have a URI representation:

Artist.for('queen')

However, a class is not required to have a base URI, and even if it does, it can always access classes with a full URI:

nk = Artist.for(RDF::URI.new('http://example.org/my-hidden-cds/new-kids'))

If you have a URI that you would like to look at as a Spira resource, you can instantiate it from the URI:

RDF::URI.new('http://example.org/my-hidden-cds/new-kids').as(Artist)
# => <Artist @subject=http://example.org/my-hidden-cds/new-kids>

Any call to 'for' with a valid identifier will always return an object with nil fields. It's a way of looking at a given resource, not a closed-world mapping to one.

You can also use blank nodes more or less as you would a URI:

remix_artist = Artist.for(RDF::Node.new)
# => <Artist @subject=#<RDF::Node:0xd1d314(_:g13751060)>>
RDF::Node.new.as(Artist)
# => <Artist @subject=#<RDF::Node:0xd1d314(_:g13751040)>>

Finally, you can create an instance of a Spira projection with #new, and you'll get an instance with a shiny new blank node subject:

formerly_known_as_prince = Artist.new
# => <Artist @subject=#<RDF::Node:0xd1d314(_:g13747140)>>

Class Options

A number of options are available for Spira classes.

base_uri

A class with a base_uri set (either an RDF::URI or a String) will use that URI as a base URI for non-absolute for calls.

Example

CD.for 'queens-greatest-hits' # is the same as...
CD.for RDF::URI.new('http://example.org/cds/queens-greatest-hits')

type

A class with a type set is assigned an RDF.type on creation and saving.

require 'spira'
require 'rdf/vocab'

class Album < Spira::Base
  type RDF::URI.new('http://example.org/types/album')
  property :name,   predicate: RDF::Vocab::DC.title
end

Spira.repository = RDF::Repository.new

rolling_stones = Album.for RDF::URI.new('http://example.org/cds/rolling-stones-hits')
# See RDF.rb at https://ruby-rdf.github.io/rdf/RDF/Enumerable.html for more information about #has_predicate?
rolling_stones.has_predicate?(RDF.type) #=> true
Album.type #=> RDF::URI('http://example.org/types/album')

In addition, one can count the members of a class with a type defined:

Album.count  #=> 1 

It is possible to assign multiple types to a Spira class:

class Man < Spira::Base
  type RDF::URI.new('http://example.org/people/father')
  type RDF::URI.new('http://example.org/people/cop')
end

All assigned types are accessible via "types":

Man.types
# => #<Set: {#<RDF::URI:0xd5ebc0(http://example.org/people/father)>, #<RDF::URI:0xd5e4b8(http://example.org/people/cop)>}>

Also note that "type" actually returns a first type from the list of types.

property

A class declares property members with the property function. See Property Options for more information.

has_many

A class declares list members with the has_many function. See Property Options for more information.

default_vocabulary

A class with a default_vocabulary set will transparently create predicates for defined properties:

class Song < Spira::Base
  configure default_vocabulary: RDF::URI.new('http://example.org/vocab'),
            base_uri: 'http://example.org/songs'
  property :title
  property :author, type: :artist
end

Spira.repository = RDF::Repository.new

dancing_queen = Song.for 'dancing-queen'
dancing_queen.title = "Dancing Queen"
dancing_queen.artist = abba
# See RDF::Enumerable for #has_predicate?
dancing_queen.has_predicate?(RDF::URI.new('http://example.org/vocab/title'))  #=> true
dancing_queen.has_predicate?(RDF::URI.new('http://example.org/vocab/artist')) #=> true

Property Options

Spira classes can have properties that are either singular or a list. For a list, define the property with has_many, for a property with a single item, use property. The semantics are otherwise the same. A has_many property will always return a list, including an empty list for no value. All options for property work for has_many.

property :artist, type: :artist    #=> cd.artist returns a single value
has_many :cds,    type: :cd        #=> artist.cds returns an array

Property always takes a symbol name as a name, and a variable list of options. The supported options are:

  • :type: The type for this property. This can be a Ruby base class, an RDF::XSD entry, or another Spira model class, referenced as a symbol. See Types below. Default: Any
  • :predicate: The predicate to use for this type. This can be any RDF URI. This option is required unless the default_vocabulary has been used.
  • :localized: Indicates if the property is multilingual. See 'Localized Properties'

Localized Properties

A localized property allows to define a value per language. It only works with properties having a single item, ie defined with property.

class Article < Spira::Base
  property :label, localized: true
end

Spira.repository = RDF::Repository.new

# default locale :en
random_article = Article.for 'random-article'
random_article.label = "A label in english"
i18n.locale = :fr
random_article.label = "Un libellé en français"

random_article.label_native
# #=> [#<RDF::Literal:0xdb47c8("A label in english"@en)>, #<RDF::Literal:0xe5c3d8("Un libellé en français"@fr)>]

random_article.label_with_locales
# #=> {:en=>"A label in english", :fr=>"Un libellé en français"}

Types

A property's type can be either a class which includes Spira::Type or a reference to another Spira model class, given as a symbol.

Relations

If the :type of a spira class is the name of another Spira class as a symbol, such as :artist for Artist, Spira will attempt to load the referenced object when the appropriate property is accessed.

In the RDF store, this will be represented by the URI of the referenced object.

Type Classes

A type class includes Spira::Type, and can implement serialization and deserialization functions, and register aliases to themselves if their datatype is usually expressed as a URI. Here is the built-in Spira Integer class:

module Spira::Types
  class Integer

    include Spira::Type

    def self.unserialize(value)
      value.object
    end

    def self.serialize(value)
      RDF::Literal.new(value)
    end

    register_alias RDF::XSD.integer
  end
end

Classes can now use this particular type like so:

class Test < Spira::Base
  property :test1, type: Integer
  property :test2, type: RDF::XSD.integer
end

Spira classes include the Spira::Types namespace, where several default types are implemented:

  • Integer
  • Float
  • Boolean
  • String
  • Any

The default type for a Spira property is Spira::Types::Any, which uses RDF::Literal's automatic boxing/unboxing of XSD types as best it can. See RDF::Literal for more information.

You can implement your own types as well. Your class' serialize method should turn an RDF::Value into a ruby object, and vice versa.

module MyModule
  class MyType
    include Spira::Type
    def self.serialize(value)
      ...
    end

    def self.unserialize(value)
      ...
    end
  end
end

class MyClass < Spira::Base
  property :property1, type: MyModule::MyType
end

Defining Repositories

You can work on any kind of RDF::Repository with Spira:

require 'rdf/ntriples'
require 'rdf/sesame'

class Album < Spira::Base
end

Spira.repository = RDF::Sesame::Repository.new 'some_server'
...

Spira.repository = RDF::Repository.load('some_file.nt')
...

Spira.using_repository(RDF::Repository.load('some_file.nt')) do
   ...
end

Spira.repository is thread-safe, which means that each thread stores its own instance. It allows you to work on multiple repositories at the same time:

threads = []
repositories = [RDF::Repository.new, RDF::Repository.new, RDF::Repository.new]

repositories.each do |repository|
  threads << Thread.new(repository) do |repository|
    Spira.repository = repository

    album = Album.for("http://theperson.com/album/random_name")
    album.year = 1950 + (rand*100).to_i
    album.save!
  end
end

threads.map(&:join)
repositories.map(&:size).join(', ') # 1, 1, 1

Validations

[removed] See the description of ActiveModel::Validations.

Hooks

[removed] See the description of ActiveModel::Callbacks.

Using Model Objects as RDF.rb Objects

All model objects are fully-functional as RDF::Enumerable, RDF::Queryable, and RDF::Mutable. This lets you manipulate objects on the RDF statement level. You can also access attributes that are not defined as properties.

Documentation

https://ruby-rdf.github.io/spira

Change Log

See Release Notes on GitHub

Support

There are a number of ways to ask for help. In declining order of preference:

  • Fork the project and write a failing test, or a pending test for a feature request
  • Ask on the public-rdf-ruby w3c mailing list
  • You can post issues to the Github issue queue
  • (there might one day be a google group or other such support channel, but not yet)

'License'

Spira is free and unemcumbered software released into the public domain. For more information, see the included UNLICENSE file.

Contributing

This repository uses Git Flow to mange development and release activity. All submissions must be on a feature branch based on the develop branch to ease staging and integration.

  • Do your best to adhere to the existing coding conventions and idioms.
  • Don't use hard tabs, and don't leave trailing whitespace on any line.
  • Do document every method you add using [YARD][] annotations. Read the [tutorial][YARD-GS] or just look at the existing code for examples.
  • Don't touch the .gemspec, VERSION or AUTHORS files. If you need to change them, do so on your private branch only.
  • Do feel free to add yourself to the CREDITS file and the corresponding list in the the README. Alphabetical order applies.
  • Do note that in order for us to merge any non-trivial changes (as a rule of thumb, additions larger than about 15 lines of code), we need an explicit public domain dedication on record from you, which you will be asked to agree to on the first commit to a repo within the organization. Note that the agreement applies to all repos in the Ruby RDF organization.

spira's People

Contributors

abrisse avatar bhuga avatar conorsheehan1 avatar cordawyn avatar dsteelma-umd avatar dwbutler avatar gkellogg avatar glappen avatar jperville avatar njh avatar tmaier avatar zhengxu001 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spira's Issues

Make Spira Threadsafe

Spira is currently not threadsafe. Working in parallel on 2 different repositories will fail.

Spira error with rails

NoMethodError in ProjectsController#create

undefined method `call' for <Project:70300670299740 @subject: _:g70300670299680>:Project
Rails.root: /home/ranielli/ruby/prolod01

Application Trace | Framework Trace | Full Trace
app/controllers/projects_controller.rb:17:in `create'
Request

Parameters:

{"utf8"=>"✓",
"authenticity_token"=>"PC8I7nNTHZclFYf67GHu7jLFcYr+TbKxGtczxTgPbWQ=",
"project"=>{"name"=>"ocarro",
"description"=>"carro do ano"},
"commit"=>"Create Project"}

Spira.repository unavailable in Rails controller

I'm getting the following error when I try to reference a Spira::Base in a Rails (3.2.14) controller:

Spira::NoRepositoryError

I'm using the current master version of this gem. I'm currently setting the repository in an initializer. Presumably this error is related to the new repository threadsafe feature, as I can access Spira.repository and my Spira::Base objects just fine from Rails console.

I can get around this by doing (in my controller):

before_filter do
  Spira.repository ||= RDF::DataObjects::Repository.new('sqlite3:test.db')
end

but I don't want to have to do this. Is there some way to make the repository play nice with Rails controllers, or is there a better/recommended place to define my repository where this won't be an issue?

help for use Spira in Controller Rails

I need use with example .

class Tag < Spira::Base
configure :base_uri => "http://www.xpto.com/tags"

property :preferred_label, :predicate => SKOS.prefLabel, :type => XSD.string
property :alternative_labels, :predicate => SKOS.altLabel, :type => XSD.string

end

class TagsController < ApplicationController

def index
@tag = Tag.all
end
end
end

Erro Cannot count a Tag without a reference type URI ...

How to define type later? (on instance level instead of class level)

You have the following sample in your readme:

class Man < Spira::Base
  type RDF::URI.new('http://example.org/people/father')
  type RDF::URI.new('http://example.org/people/cop')
end

So when I understand this correctly, all men instantiated with m = Man.new are cops and fathers.

But what when m is a fire fighter?
Do I need to set up another class ManFireFighter?
Or when he gets retired.
Do I need to get his attributes and instantiate a new class of ManRetiredFireFighterAndGrandfather?

As this does not seem feasible, I ask myself how to set the type on instance level?

Error use save method with Blank Nodes.

I have a class

class Version < Spira::Base

type RDF::URI.new(DOAP.Version)

property :name, :predicate => DOAP.name, :type => XSD.string
property :created, :predicate => DOAP.created, :type => XSD.date

end

version = RDF:Node.new.as(Version)
version.name = ' xpto'
version.save

error

ArgumentError: Cannot create identifier for Version by String without base_uri; an RDF::URI is required.

Calling save in example code throws exception

I can't seem to get any of the examples to work fully; everything is fine until I try to call save! on an object, which gives me

NoMethodError: undefined method `has_subject?' for nil:NilClass
from /home/wstrinz/.rvm/gems/ruby-1.9.3-p429/gems/spira-0.5.0/lib/spira/persistence.rb:285:in `new_record?'

If I call bob.subject it returns the expected uri, but maybe this is something other than the subject that the new_record? method is looking for?

It seems to be the same for the Person, Artist, and CD objects in the examples in the readme. I dug into the code a bit, but I couldn't figure out what was going on since 'subject' seems to be some kind of reserved word and I'm not quite sure how it's supposed to work. Is this just happening because I don't actually have anywhere to save the resource to? I thought it would just stay in memory, but if not maybe a note could be added about this to the examples? I'd be happy to submit a PR if that's the case :)

Resource's properties cannot be named "subject"

I have the following resource definition:

class City
  include Spira::Resource

  @vocabulary = 'http://s.opencalais.com/1/pred'

  base_uri 'http://d.opencalais.com/er/geo/city/ralg-geo1'
  type RDF::URI.new('http://s.opencalais.com/1/type/er/Geo/City')

  property :name,                 type: XSD.string, predicate: predicate_uri('name')
  property :short_name,           type: XSD.string, predicate: predicate_uri('shortname')
  property :contained_by_state,   type: XSD.string, predicate: predicate_uri('containedbystate')
  property :contained_by_country, type: XSD.string, predicate: predicate_uri('containedbycountry')
  property :latitude,             type: XSD.string, predicate: predicate_uri('latitude')
  property :longitude,            type: XSD.string, predicate: predicate_uri('longitude')
  property :subject,              type: RDF::URI,   predicate: predicate_uri('subject')

end

and when I try to get a resource from the repository I get the following exception:

/home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/instance_methods.rb:334:in `attribute_get': undefined method `[]' for nil:NilClass (NoMethodError)
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/dsl.rb:272:in `block in add_accessors'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/instance_methods.rb:59:in `reload'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/instance_methods.rb:42:in `initialize'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/class_methods.rb:90:in `new'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/class_methods.rb:90:in `project'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/class_methods.rb:69:in `for'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/class_methods.rb:202:in `block in each'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/mixin/enumerable.rb:341:in `call'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/mixin/enumerable.rb:341:in `block in each_subject'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:341:in `call'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:341:in `block (4 levels) in query_pattern'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:339:in `each'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:339:in `block (3 levels) in query_pattern'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:336:in `each'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:336:in `block (2 levels) in query_pattern'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:333:in `each'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:333:in `block in query_pattern'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:330:in `each'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/repository.rb:330:in `query_pattern'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/mixin/enumerable.rb:152:in `each'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/mixin/enumerable.rb:152:in `each_statement'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rdf-0.3.11.1/lib/rdf/mixin/enumerable.rb:337:in `each_subject'
    from /home/guido/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/spira-0.0.12/lib/spira/resource/class_methods.rb:201:in `each'
    from test.rb:5:in `each'
    from test.rb:5:in `to_a'
    from test.rb:5:in `<main>'

But when I change the subject property definition and replace "subject" to anything else it works. So it seems that "subject" is reserved in some way.

Forthcoming RDF.rb 1.1 release and use of git-flow

Hi, I'm using git-flow on the other RDF.rb eco-system gems, and have established the current develop branch to prepare for a forthcoming 1.1 release of most of the gems. Unless I hear otherwise, I plan to do the same for Spira.

Git-flow works great for maintaining different branches of a repo and automating most of the feature/prepare/release cycle.

Feature Request: Distinguish between serialized properties and reflections

It would be nice if reflections were reserved for associations, that is, relationships with other Spira::Base objects. Reflections could be defined for :has_many and :has_one relationships, while serialization could be handled elsewhere (though has_many associations would be "serialized" of course).

I've forked the gem and made a few changes (primarily to persistence.rb), to separate these concerns. I've created a :serialize (boolean) flag on Spira::Base.properties values to differentiate properties that should act like arrays vs. singular objects.

I've added a couple of short specs to test this behavior, and all tests are passing.

Reserving reflections for associated Spira::Base objects makes it easier to resolve an object's associations (and those objects' associations, etc.).

Is there any plan to add this separation of concerns to Spira? If not, are you interested in the idea and open to pull requests? Sorry if I'm going about this the wrong way--I'm a GitHub amateur.

link style in docs: github vs yard

I just came accross this issue on yard lsegal/yard#1017
Basically github requires this style of link [changelog](CHANGELOG.md) but yard requires this {file:CHANGELOG.md}

I changed the links in the readme to work on github in pr #53
Now the link works in the github readme, but it's broken in the docs on rubygems.
If you go to the readme on rubygems and follow the link to the changelog, you'll get a not found error for changes.md

Do you think the fix provided in the issue would make sense for spira?
Or should we choose between github or yard style links?

reset_changes in base.rb throwing NilClass error

The reset_changes method is the base.rb is throwing nil:NilClass when trying to clear the @changed_attributes

@changed_attributes.clear

 /Users/mohideen/.rvm/gems/ruby-2.4.0/gems/spira-3.0.0/lib/spira/base.rb:288:in `reset_changes': undefined method `clear' for nil:NilClass (NoMethodError)
        from /Users/mohideen/.rvm/gems/ruby-2.4.0/gems/spira-3.0.0/lib/spira/base.rb:162:in `reload'
        from /Users/mohideen/.rvm/gems/ruby-2.4.0/gems/spira-3.0.0/lib/spira/base.rb:124:in `initialize'
        from /Users/mohideen/.rvm/gems/ruby-2.4.0/gems/spira-3.0.0/lib/spira/persistence.rb:195:in `new'
        from /Users/mohideen/.rvm/gems/ruby-2.4.0/gems/spira-3.0.0/lib/spira/persistence.rb:195:in `project'
        from /Users/mohideen/.rvm/gems/ruby-2.4.0/gems/spira-3.0.0/lib/spira/persistence.rb:176:in `for'
        from /Users/mohideen/.rvm/gems/ruby-2.4.0/gems/spira-3.0.0/lib/rdf/ext/uri.rb:16:in `as'
        from /Users/mohideen/.rvm/gems/ruby-2.4.0/gems/worldcat-discovery-1.2.0/lib/worldcat/discovery/bib.rb:188:in `search'

Context: The problem occurs when RDF URI is requested to be modeled as a class which is an extension of the Spira::Base class.

  1. Bib (tries to model RDF URI (subject) as BibSearchResults): https://github.com/OCLC-Developer-Network/worldcat-discovery-ruby/blob/master/lib/worldcat/discovery/bib.rb#L179
  2. BibSearResults (extends SearchResults): https://github.com/OCLC-Developer-Network/worldcat-discovery-ruby/blob/master/lib/worldcat/discovery/bib_search_results.rb
  3. SearchResults (extends Spira::Base): https://github.com/OCLC-Developer-Network/worldcat-discovery-ruby/blob/master/lib/worldcat/discovery/search_results.rb

The error does not happen if change the following code

@changed_attributes.clear

to

@changed_attributes.clear if changed?

Is this a bug in the code or am I not using this correctly?

Spira seems incompatible with activesupport >= 5.2.0

Hello,

I tried to bundle update one of our existing projects which uses spira. Upgrading activesupport dependency from v5.1.6 to v5.2.0 seems to break spira, as shown in the stacktrace below:

bash-4.2$ bundle exec rspec ./spec/serializers/subject_spec.rb:41
Run options: include {:locations=>{"./spec/serializers/subject_spec.rb"=>[41]}}

SemanticEnrichment::Serializers::Subject
  #load
    returns a Subject (FAILED - 1)

Failures:

  1) SemanticEnrichment::Serializers::Subject#load returns a Subject
     Failure/Error: serializer.load(subject_use.repository, subject_use.hooks.first.to_s)
     NoMethodError:
       undefined method `clear' for nil:NilClass
     # ./.bundle/ruby/gems/spira-2.1.0/lib/spira/base.rb:288:in `reset_changes'
     # ./.bundle/ruby/gems/spira-2.1.0/lib/spira/base.rb:162:in `reload'
     # ./.bundle/ruby/gems/spira-2.1.0/lib/spira/base.rb:124:in `initialize'
     # ./.bundle/ruby/gems/spira-2.1.0/lib/spira/persistence.rb:195:in `new'
     # ./.bundle/ruby/gems/spira-2.1.0/lib/spira/persistence.rb:195:in `project'
     # ./.bundle/ruby/gems/spira-2.1.0/lib/spira/persistence.rb:176:in `for'
...

I traced the problem to Spira::Base#reset_changes, which seems to assume too much about activesupport internals: for example, the @changed_attributes instance variable has been refactored out in activesupport v5.2.0 (it has been replaced with a method which returns a frozen HashWithIndifferentAccess, which cannot be reset anymore, thus breaking Spira::Base#reset_changes).

Can you investigate the issue? Meanwhile, I will add gem 'activesupport', '>= 5.0', '< 5.2' to my Gemfiles.

Spira#count counts subjects - should it count instances?

I'm just getting to grips with Spira and I've found something that strikes me as odd (the mailing list looks a bit quiet so I'm posting here instead). I wonder if this is an expectation set by the OOP/ ActiveRecord world that simply doesn't apply in the graph/ RDF world...

I expect Spira::Base#count to return the count of objects of the relevant type - i.e. the distinct count of "?s a <model_type>", the number of instances. Instead #count returns the count of occurrences of those subjects - i.e. the number of triples where a "?s a <model_type>" is the subject. In OOP terms this is basically like counting the number of attributes which is certainly not what I would expect.

I realise that what might need 'fixing' is in fact my expectations about how objects work in a graph world. If so, I'd be very grateful if someone could elaborate on this.

Nevertheless the apparent congruence between the AR and Spira public APIs leads me to believe that I won't be the only one making this mistake! It might be clearer, for example, if Spira distinguished between a #count and a #count_of_subjects method.

Use of inefficient splat argument methods in Spira codebase

As a follow-up to ruby-rdf/rdf#214, the spira codebase contains at least two use of splat argument method invoked with a potentially large number of statements:

These should be refactored to use RDF::Writable#insertn and RDF::Writable#insertn; the speedup on these (seldom invoked) utility methods should be quite noticeable.

has_many relation usage and properties with multiple ranges

i have an ontology which users can rate different concepts.
lets say
:user :rates :celebrity
:user :rates :movie

so i started to create a user class

class User < Spira::Base
has_many :rates, :predicate => OntDakick.rates, :type => :Celebrity,:Movie #my main issue is here
end

user = User.for("")
user.rates
output is []

.Thats ok by now.Now I'm trying to add a celebrity which user rates.
rating.rates << Celebrity.for("")

But,

rating.rates
output is []

This is my first problem.Second one is how to define predicates with multiple ranges.

Thanks

Object Graph doesn't contain undefined properties

The way Spira currently works it only pulls down information into the object about properties you've defined. Is there any plan to pull down the entire graph?

If it clarifies, https://github.com/ruby-rdf/spira/blob/master/lib/spira/persistence.rb#L382-L391 will pull the entire graph from a repository but only set values for those properties it knows about. There are cases where it would be good for me to have access to the graph for that subject within the object.

error on save

i have these statements

RDF::Statement:0x80789a6c(<http://localhost:3000/knowledgebase/movies/21 http:://localhost:3000/resource/name "Buffy the Vampire Slayer" .)>

RDF::Statement:0x811f1ef0(<http://localhost:3000/knowledgebase/movies/21 http:://localhost:3000/resource/has http://localhost:3000/knowledgebase/media_objects/db1aa137b82019b9 .)>

RDF::Statement:0x83020fd4(<http://localhost:3000/knowledgebase/movies/21 http:://localhost:3000/resource/dakickID "21"^^http://www.w3.org/2001/XMLSchema#integer .)>

when i want to save the movie. it gives me this error.

undefined method `literal?' for #Hash:0x000001062397c0

Any idea?

Thanks

cascade inserting?

Resources with has_many relationships which derived from Spira::Base do cascade inserting?

uninitialized constant RDF::Vocab

Taken from an example in the readme:

require 'spira'
class Person < Spira::Base

  configure :base_uri => "http://example.org/example/people"

  property :name, :predicate => RDF::Vocab::FOAF.name, :type => String
  property :age,  :predicate => RDF::Vocab::FOAF.age,  :type => Integer

end

Spira.repository = RDF::Repository.new

bob = RDF::URI("http://example.org/people/bob").as(Person)
bob.age  = 15
bob.name = "Bob Smith"
bob.save!

bob.each_statement {|s| puts s}

# /home/$user/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/rdf-3.0.5/lib/rdf.rb:67:in 
# `const_missing': uninitialized constant RDF::Vocab (NameError)
# Did you mean?  RDF::VOCABS
#   from test.rb:6:in `<class:Person>'
#   from test.rb:2:in `<main>'

Adding require 'rdf/vocab' solves this issue. It is in the .gemspec file as a development dependency.

I think there could be at least two solutions here:

  1. Update the readme examples to include (pr #53):
    require 'rdf/vocab'
  2. Update lib/spira.rb to include (pr #54):
     require 'rdf/vocab'

What do you think is the best approach?

erro user TYPE ,

require 'spira'
class Project < Spira::Base

configure :base_uri => "http://www.xpto.com/projects"

type RDf::URI.new("http://usefulinc.com/ns/doap#Project")

property :name, :predicate => DOAP.name, :type => XSD.string

end

test = Project.for('xpto')

Error

/home/ranielli/ruby/prolod01/app/models/project.rb:10:in initialize': wrong number of arguments(1 for 0) (ArgumentError) from /home/ranielli/ruby/prolod01/app/models/project.rb:10:innew'
from /home/ranielli/ruby/prolod01/app/models/project.rb:10:in <class:Project>' from /home/ranielli/ruby/prolod01/app/models/project.rb:5:in

'

multiple types in derived resources

I also checked specs in spira about inheritence but it seems not working for me. Here is my structure,

require 'spira'
module Knowledgebase
  class Celebrity < DakickThing

    configure :base_uri => Semantic::Utilities.create_base_individual_uri(DAK_SEM_CONFG["individual_base"], self.name.split("::")[1].pluralize.tableize)
    type Semantic::OntDakick.Celebrity
end
end
require 'spira'
module Knowledgebase
  class DakickThing < OntClass

    type Semantic::OntDakick.DakickThing
end
end
require 'spira'
require 'rdf'
module Knowledgebase
  class OntClass < Spira::Base


    include RDF
    include Semantic::ExternalOntologyUtility
    include Semantic::Utilities


    type RDFS.Resource
    type OWL.Class


  end
end

Here is my unit test

it 'contains derived types' do
    include RDF
    types = Set.new [RDF::OWL.Class, RDF::RDFS.Resource, Semantic::OntDakick.DakickThing, Semantic::OntDakick.Celebrity]
    Knowledgebase::Celebrity.types.should eql types
  end

and here is my output

expected: #<Set: {#<RDF::URI:0x844b7f38(http://www.w3.org/2002/07/owl#Class)>, #<RDF::URI:0x844c794c(http://www.w3.org/2000/01/rdf-schema#Resource)>, #<RDF::URI:0x82fd7528(http:://localhost:3000/resource/DakickThing)>, #<RDF::URI:0x82fdff84(http:://localhost:3000/resource/Celebrity)>}>
     got: #<Set: {#<RDF::URI:0x82fdff84(http:://localhost:3000/resource/Celebrity)>, #<RDF::URI:0x80640d2c(http://xmlns.com/foaf/0.1/Person)>}>

#new_record? is wrong if any data already exists

This issue occurs when connected to Fuseki:

class Article < Spira::Base
end

Article.new.save!
Article.new.new_record? # => false

(This is an attempt to pair down what is a more complex model on my end; hope it conveys the bug.)


Tracing through:

# A resource is considered to be new if the repository
# does not have statements where subject == resource type
def new_record?
  !self.class.repository.has_subject?(subject)
end
CONSTRUCT { _:g13426267119880 ?g13427767297260 ?g13427767296240 . } WHERE { _:g13426267119880 ?g13427767297260 ?g13427767296240 . } 

That blank node acts as a variable, so it matches so long as there is anything in the backing store. In other words, #new_record? produces true so long as any triple exists in the backing store.

Elsewhere:

    def materizalize
      if new_record? && subject.anonymous? && type
        # TODO: doesn't subject.anonymous? imply subject.id == nil ???
        @subject = self.class.id_for(subject.id)
      end
    end

Might be time to resolve that TODO.


This shows in Rails like so:

<%= form_for Article.new do |form| %>
<% end %>

#form_for eventually calls @article.id, which then crashes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.