dbd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +8 -0
  6. data/Guardfile +7 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +97 -0
  9. data/Rakefile +1 -0
  10. data/dbd.gemspec +30 -0
  11. data/docs/rationale.md +17 -0
  12. data/docs/stories/001_create_a_fact.txt +15 -0
  13. data/docs/stories/002_create_a_facts_collection.txt +14 -0
  14. data/docs/stories/003_create_a_fact_origin.txt +15 -0
  15. data/docs/stories/004_create_fact_origins_collection.txt +8 -0
  16. data/docs/stories/005_CSV_export_the_graph.txt +18 -0
  17. data/docs/stories/006_refactor_fact_origin_to_provenance_fact.txt +20 -0
  18. data/docs/stories/007_rename_property_to_predicate.txt +6 -0
  19. data/docs/stories/008_testing_different_ruby_versions.txt +7 -0
  20. data/docs/stories/009_build_and_store_resources_with_provenance.txt +38 -0
  21. data/docs/stories/010_provenance_fact_properties_from_provenance_ontology.txt +10 -0
  22. data/docs/test.rb +32 -0
  23. data/lib/dbd.rb +13 -0
  24. data/lib/dbd/errors.rb +11 -0
  25. data/lib/dbd/fact.rb +182 -0
  26. data/lib/dbd/fact/collection.rb +60 -0
  27. data/lib/dbd/fact/id.rb +19 -0
  28. data/lib/dbd/fact/subject.rb +21 -0
  29. data/lib/dbd/graph.rb +47 -0
  30. data/lib/dbd/helpers/ordered_set_collection.rb +86 -0
  31. data/lib/dbd/helpers/uuid.rb +33 -0
  32. data/lib/dbd/provenance_fact.rb +76 -0
  33. data/lib/dbd/provenance_resource.rb +54 -0
  34. data/lib/dbd/rdf.rb +9 -0
  35. data/lib/dbd/repo.rb +8 -0
  36. data/lib/dbd/repo/neo4j_repo.rb +4 -0
  37. data/lib/dbd/repo/neo4j_repo/base.rb +55 -0
  38. data/lib/dbd/resource.rb +117 -0
  39. data/lib/dbd/version.rb +3 -0
  40. data/spec/factories/fact.rb +76 -0
  41. data/spec/factories/provenance_fact.rb +34 -0
  42. data/spec/factories/provenance_resource.rb +16 -0
  43. data/spec/factories/resource.rb +17 -0
  44. data/spec/lib/dbd/fact/collection_spec.rb +236 -0
  45. data/spec/lib/dbd/fact/id_spec.rb +19 -0
  46. data/spec/lib/dbd/fact/subject_spec.rb +19 -0
  47. data/spec/lib/dbd/fact_spec.rb +217 -0
  48. data/spec/lib/dbd/graph_spec.rb +214 -0
  49. data/spec/lib/dbd/helpers/ordered_set_collection_spec.rb +88 -0
  50. data/spec/lib/dbd/helpers/uuid_spec.rb +15 -0
  51. data/spec/lib/dbd/provenance_fact_spec.rb +108 -0
  52. data/spec/lib/dbd/provenance_resource_spec.rb +77 -0
  53. data/spec/lib/dbd/rdf_base_spec.rb +39 -0
  54. data/spec/lib/dbd/repo/neo4j_repo/base_spec.rb +85 -0
  55. data/spec/lib/dbd/repo/neo4j_repo/performance_spec.rb +40 -0
  56. data/spec/lib/dbd/resource_spec.rb +166 -0
  57. data/spec/spec_helper.rb +19 -0
  58. metadata +272 -0
@@ -0,0 +1,76 @@
1
+ module Dbd
2
+
3
+ ##
4
+ # ProvenanceFact
5
+ #
6
+ # ProvenanceFact is derived from Fact and behaves very similar.
7
+ #
8
+ # The ProvenanceFacts with same subject form a ProvenanceResource and
9
+ # this is used as the target for the provenance_subject of a Fact.
10
+ #
11
+ # The provenance_subject of a ProvenanceFact itself is empty, so the
12
+ # usage of provenance_subject is not recursive on this level (this
13
+ # allows efficient single pass loading in an underlying database).
14
+ #
15
+ # In the serialisation of the fact stream, the presence or absence of a
16
+ # provenance_subject marks the difference between a (base) Fact and a
17
+ # ProvenanceFact.
18
+ #
19
+ # The predicates in a ProvenanceFact would typically come from a defined
20
+ # "provenance ontology". An experimental example of a provenance ontology
21
+ # is built-up on https://data.vandenabeele.com/ontologies/provenance.
22
+ class ProvenanceFact < Fact
23
+
24
+ ##
25
+ # Builds a new ProvenanceFact.
26
+ #
27
+ # @param [Hash{Symbol => Object}] options
28
+ # @option options [Fact::Subject] :subject (new_subject) Optional: the subject for the ProvenanceFact
29
+ # @option options [String] :predicate Required: the subject for the ProvenanceFact
30
+ # @option options [String] :object Required: the object for the ProvenanceFact
31
+ def initialize(options)
32
+ validate_provenance_subject(options)
33
+ super
34
+ end
35
+
36
+ ##
37
+ # Executes the required update in used_provenance_subjects.
38
+ #
39
+ # For a ProvenanceFact, there is no provenance_subject, so
40
+ # pointless to mark it in used_provenance_subjects hash.
41
+ def update_used_provenance_subjects(h)
42
+ # Do nothing (override the behaviour from super).
43
+ end
44
+
45
+ ##
46
+ # Validates the presence or absence of provenance_subject.
47
+ #
48
+ # Here, in the derived ProvenanceFact, it must not be present.
49
+ # @param [#nil?] provenance_subject
50
+ # Return [Boolean]
51
+ def provenance_subject_valid?(provenance_subject)
52
+ provenance_subject.nil?
53
+ end
54
+
55
+ ##
56
+ # Builds duplicate with the subject set.
57
+ #
58
+ # @param [Subject] subject_arg
59
+ # @return [ProvenanceFact] the duplicate fact
60
+ def dup_with_subject(subject_arg)
61
+ self.class.new(
62
+ subject: subject_arg, # from arg
63
+ predicate: predicate,
64
+ object: object)
65
+ end
66
+
67
+ private
68
+
69
+ ##
70
+ # Validate that provenance_subject is not set here.
71
+ def validate_provenance_subject(options)
72
+ raise ProvenanceError if options[:provenance_subject]
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,54 @@
1
+ module Dbd
2
+ ##
3
+ # A ProvenanceResource is derived from a Resource, specifically
4
+ # for a Provenance (does not have and does need a provenance_subject)
5
+ class ProvenanceResource < Resource
6
+
7
+ ##
8
+ # Build a new ProvenanceResource.
9
+ #
10
+ # The subject can be either given as an argument or a new (random)
11
+ # subject is automatically set (see Resource for details).
12
+ #
13
+ # A provenance_subject may not be given here.
14
+ # @option options [Fact::Subject] :subject (new_subject) Optional: the subject for the resource
15
+ def initialize(options = {})
16
+ super
17
+ end
18
+
19
+ ##
20
+ # Add a ProvenanceFact.
21
+ #
22
+ # * if it has no subject, the subject is set in a duplicate provenance_fact
23
+ # * if is has the same subject as the resource, added unchanged.
24
+ # * if it has a different subject, a SubjectError is raised.
25
+ def <<(provenance_fact)
26
+ # TODO: check the type of the provenance_fact (ProvenanceFact)
27
+ super
28
+ end
29
+
30
+ private
31
+
32
+ ##
33
+ # Should not be called in ProvenanceResource subclass.
34
+ def provenance_subject
35
+ raise RuntimeError, "provenance_subject should not be called in ProvenanceResource."
36
+ end
37
+
38
+ ##
39
+ # Validate that provenance_subject is not set here.
40
+ def validate_provenance_subject
41
+ raise ProvenanceError if @provenance_subject
42
+ end
43
+
44
+ ##
45
+ # Check provenance_subject, which should be nil here
46
+ # @param [ProvenanceFact] provenance_fact
47
+ # @return [ProvenanceFact] with validated nil on provenance_subject
48
+ def check_or_set_provenance(provenance_fact)
49
+ raise ProvenanceError if provenance_fact.provenance_subject
50
+ provenance_fact
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,9 @@
1
+ # from the rdf gem (developed by rdf-ruby account on github)
2
+ require 'rdf'
3
+
4
+ module Dbd
5
+ ##
6
+ # To allow some tests of the *rdf* gem.
7
+ module RdfBase
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ require 'dbd/repo/neo4j_repo.rb'
2
+
3
+ module Dbd
4
+ ##
5
+ # To allow some tests of Neo4j
6
+ module Repo
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ # from neopgraphy gem
2
+ require 'neography'
3
+
4
+ require 'dbd/repo/neo4j_repo/base.rb'
@@ -0,0 +1,55 @@
1
+ module Dbd
2
+ module Repo
3
+ module Neo4jRepo
4
+ ##
5
+ # To allow some tests on the Neo4j Neography gem.
6
+ class Base
7
+
8
+ def initialize
9
+ @neo = Neography::Rest.new
10
+ end
11
+
12
+ def create_node(hash)
13
+ @neo.create_node(hash)
14
+ end
15
+
16
+ def batch(*args)
17
+ @neo.batch(*args)
18
+ end
19
+
20
+ def create_relationship(p, s, o)
21
+ @neo.create_relationship(p, s, o)
22
+ end
23
+
24
+ def list_node_indexes
25
+ @neo.list_node_indexes
26
+ end
27
+
28
+ def create_node_index(name, type, provider)
29
+ @neo.create_node_index(name, type, provider)
30
+ end
31
+
32
+ def add_node_to_index(index, key, value, node)
33
+ @neo.add_node_to_index(index, key, value, node)
34
+ end
35
+
36
+ def get_node_index(index, key, value)
37
+ @neo.get_node_index(index, key, value)
38
+ end
39
+
40
+ def execute_query(query_string)
41
+ @neo.execute_query(query_string)
42
+ end
43
+
44
+ def get_root
45
+ @neo.get_root
46
+ end
47
+
48
+ def load_node(uri)
49
+ Neography::Node.load(uri)
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,117 @@
1
+ require 'dbd/helpers/ordered_set_collection'
2
+
3
+ module Dbd
4
+ ##
5
+ # A Resource is a collection of facts that have the same subject.
6
+ #
7
+ # In the real-world this is a mainly an "instance" about which all
8
+ # facts are giving information (e.g. a conference, a person, a
9
+ # bicycle, ...). More generally this can also be used to describe
10
+ # classes and other concepts in the software system.
11
+ #
12
+ # A new (random) subject is generated for a resource. In Dbd,
13
+ # a subject is a random uuid (like a oid), not a meaningful URI
14
+ # as it is in RDF.
15
+ #
16
+ # A provenance_subject is a required field in the options hash.
17
+ # Practically, first a ProvenanceResource will be created and the
18
+ # subject of that will be used as provenance_subject for the
19
+ # Resources that are associated with it.
20
+ #
21
+ # During build-up of a Fact, the subject and the provenance_subject
22
+ # can be nil. These will then be set in a local duplicate when the
23
+ # Fact is added (with '<<') to a resource.
24
+ class Resource
25
+
26
+ include Helpers::OrderedSetCollection
27
+
28
+ attr_reader :subject
29
+
30
+ ##
31
+ # @return [Fact::Subject] a new (random) Resource subject
32
+ def self.new_subject
33
+ Fact.new_subject
34
+ end
35
+
36
+ ##
37
+ # Build a new resource.
38
+ #
39
+ # By default, a new (random) subject is generated for a resource.
40
+ # Optionally, an explicit subject can be given in the options parameter
41
+ # (this is best created with the new_subject class method for forward
42
+ # compatibility).
43
+ #
44
+ # The provenance_subject argument is required. This will typically be
45
+ # taken from an earlier created ProvenanceResource.
46
+ # @param [Hash{Symbol => Object}] options
47
+ # @option options [Fact::Subject] :provenance_subject (required) the subject of the provenance resource for this resource
48
+ # @option options [Fact::Subject] :subject (new_subject) Optional: the subject for the resource
49
+ def initialize(options)
50
+ @subject = options[:subject] || self.class.new_subject
51
+ @provenance_subject = options[:provenance_subject]
52
+ validate_provenance_subject
53
+ super()
54
+ end
55
+
56
+ ##
57
+ # Add a fact.
58
+ #
59
+ # * if it has no subject, the subject is set in a duplicate fact
60
+ # * if is has the same subject as the resource, added unchanged.
61
+ # * if it has a different subject, a SubjectError is raised.
62
+ # * if it has no provenance_subject, the provenance_subject is set in a duplicate fact
63
+ # * if is has the same provenance_subject as the resource, added unchanged.
64
+ # * if it has a different provenance_subject, a ProvenanceError is raised.
65
+ def <<(fact)
66
+ # TODO: check the type of the fact (Fact)
67
+ super(check_or_set_subject_and_provenance(fact))
68
+ end
69
+
70
+ ##
71
+ # Getter for provenance_subject.
72
+ #
73
+ # Will be overridden in the ProvenanceResource subclass.
74
+ def provenance_subject
75
+ @provenance_subject
76
+ end
77
+
78
+ private
79
+
80
+ def check_or_set_subject_and_provenance(element)
81
+ with_subject = check_or_set_subject(element)
82
+ check_or_set_provenance(with_subject)
83
+ end
84
+
85
+ def check_or_set_subject(element)
86
+ if element.subject
87
+ if element.subject == @subject
88
+ return element
89
+ else
90
+ raise SubjectError,
91
+ "self.subject is #{subject} and element.subject is #{element.subject}"
92
+ end
93
+ else
94
+ element.dup_with_subject(@subject)
95
+ end
96
+ end
97
+
98
+ # this will be overriden in the ProvenanceResource sub_class
99
+ def check_or_set_provenance(element)
100
+ if element.provenance_subject
101
+ if element.provenance_subject == @provenance_subject
102
+ return element
103
+ else
104
+ raise ProvenanceError,
105
+ "self.provenance_subject is #{provenance_subject} and element.provenance_subject is #{element.provenance_subject}"
106
+ end
107
+ else
108
+ element.dup_with_provenance_subject(@provenance_subject)
109
+ end
110
+ end
111
+
112
+ def validate_provenance_subject
113
+ raise ProvenanceError if @provenance_subject.nil?
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,3 @@
1
+ module Dbd
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,76 @@
1
+ module Factories
2
+ module Fact
3
+
4
+ def self.factory_for
5
+ ::Dbd::Fact
6
+ end
7
+
8
+ def self.new_subject
9
+ factory_for.new_subject
10
+ end
11
+
12
+ def self.fact_1(provenance_subject = nil)
13
+ factory_for.new(
14
+ provenance_subject: provenance_subject,
15
+ predicate: "http://example.org/test/name",
16
+ object: "Gandhi")
17
+ end
18
+
19
+ def self.fact_2_with_subject(provenance_subject = nil)
20
+ factory_for.new(
21
+ provenance_subject: provenance_subject,
22
+ subject: new_subject,
23
+ predicate: "http://example.org/test/name",
24
+ object: "Mandela")
25
+ end
26
+
27
+ def self.fact_3_with_subject(provenance_subject = nil)
28
+ factory_for.new(
29
+ provenance_subject: provenance_subject,
30
+ subject: new_subject,
31
+ predicate: "http://example.org/test/name",
32
+ object: "King")
33
+ end
34
+
35
+ def self.data_fact(provenance_subject = nil, subject = nil)
36
+ factory_for.new(
37
+ provenance_subject: provenance_subject,
38
+ subject: subject,
39
+ predicate: "http://example.org/test/name",
40
+ object: "Aung San Suu Kyi")
41
+ end
42
+
43
+ def self.data_fact_EU(provenance_subject = nil, subject = nil)
44
+ factory_for.new(
45
+ provenance_subject: provenance_subject,
46
+ subject: subject,
47
+ predicate: "http://example.org/test/name",
48
+ object: "European Union")
49
+ end
50
+
51
+ module Collection
52
+
53
+ def self.factory_for_instance
54
+ o = Object.new
55
+ o.extend(::Dbd::Fact::Collection)
56
+ o.send(:initialize)
57
+ o
58
+ end
59
+
60
+ def self.fact_2_3(provenance_subject)
61
+ factory_for_instance.tap do |fact_collection|
62
+ fact_collection << Fact.fact_2_with_subject(provenance_subject)
63
+ fact_collection << Fact.fact_3_with_subject(provenance_subject)
64
+ end
65
+ end
66
+
67
+ def self.provenance_facts(subject)
68
+ factory_for_instance.tap do |provenance_facts|
69
+ provenance_facts << ProvenanceFact.context(subject)
70
+ provenance_facts << ProvenanceFact.created_by(subject)
71
+ provenance_facts << ProvenanceFact.original_source(subject)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,34 @@
1
+ module Factories
2
+ module ProvenanceFact
3
+
4
+ def self.factory_for
5
+ ::Dbd::ProvenanceFact
6
+ end
7
+
8
+ def self.new_subject
9
+ factory_for.new_subject
10
+ end
11
+
12
+ def self.context(subject = nil)
13
+ factory_for.new(
14
+ subject: subject,
15
+ predicate: "https://data.vandenabeele.com/ontologies/provenance#context",
16
+ object: "public")
17
+ end
18
+
19
+ def self.created_by(subject = nil)
20
+ factory_for.new(
21
+ subject: subject,
22
+ predicate: "https://data.vandenabeele.com/ontologies/provenance#created_by",
23
+ object:"peter_v")
24
+ end
25
+
26
+ def self.original_source(subject = nil)
27
+ factory_for.new(
28
+ subject: subject,
29
+ predicate: "https://data.vandenabeele.com/ontologies/provenance#original_source",
30
+ object: "this has a comma , a newline \n and a double quote \"")
31
+ end
32
+
33
+ end
34
+ end