dbd 0.0.1

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.
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