audumbla 0.1.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.
- checksums.yaml +7 -0
- data/README.md +22 -0
- data/lib/audumbla.rb +4 -0
- data/lib/audumbla.rb~ +4 -0
- data/lib/audumbla/enrichment.rb +148 -0
- data/lib/audumbla/enrichment.rb~ +148 -0
- data/lib/audumbla/enrichments.rb +4 -0
- data/lib/audumbla/enrichments.rb~ +7 -0
- data/lib/audumbla/enrichments/geocode.rb~ +11 -0
- data/lib/audumbla/enrichments/version.rb~ +5 -0
- data/lib/audumbla/field_enrichment.rb +60 -0
- data/lib/audumbla/field_enrichment.rb~ +60 -0
- data/lib/audumbla/spec/enrichment.rb +242 -0
- data/lib/audumbla/version.rb +3 -0
- data/lib/audumbla/version.rb~ +5 -0
- data/spec/lib/audumbla/enrichment_spec.rb +15 -0
- data/spec/lib/audumbla/enrichment_spec.rb~ +15 -0
- data/spec/lib/audumbla/enrichments/geocode_spec.rb~ +10 -0
- data/spec/lib/audumbla/field_enrichment_spec.rb +19 -0
- data/spec/lib/audumbla/field_enrichment_spec.rb~ +19 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/spec_helper.rb~ +23 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 85d96776f960b63fce1a6e0533f8e53d9afe9ae8
|
4
|
+
data.tar.gz: c9fa438ae3c3ff6e6ae06395f188b18872b44a6b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f409960eca0091c5405aecbdafb6dab5d1eaf1e3383ac23c3ac32a40a38b24210ec57f8456be9d4f949498537d4e2bc4b678551c836f121f8c255ddc9b627571
|
7
|
+
data.tar.gz: d0d5b93cad7e668c0faae99ad5dca370baa9d3e15d7d22becb917aa62acfb438428bd66b9f9ab29c79aececd376950b52b244ce26c0b5c623a6f018685626d66
|
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Auðumbla
|
2
|
+
========
|
3
|
+
|
4
|
+
[](https://travis-ci.org/dpla/audumbla)
|
5
|
+
|
6
|
+
Auðumbla provides the basic functionality for defining and running
|
7
|
+
metadata enrichments in [KriKri](https://github.com/dpla/KriKri).
|
8
|
+
|
9
|
+
Contribution Guidelines
|
10
|
+
-----------------------
|
11
|
+
Please observe the following guidelines:
|
12
|
+
|
13
|
+
- Write tests for your contributions.
|
14
|
+
- Document methods you add using YARD annotations.
|
15
|
+
- Follow the included style guidelines (i.e. run `rubocop` before committing).
|
16
|
+
- Use well formed commit messages.
|
17
|
+
|
18
|
+
Copyright & License
|
19
|
+
--------------------
|
20
|
+
|
21
|
+
- Copyright Digital Public Library of America, 2014-2015
|
22
|
+
- License: MIT
|
data/lib/audumbla.rb
ADDED
data/lib/audumbla.rb~
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
module Audumbla
|
2
|
+
##
|
3
|
+
# Mixin module for enriching a set of input_fields and setting the resulting
|
4
|
+
# values to a set of output fields.
|
5
|
+
module Enrichment
|
6
|
+
##
|
7
|
+
# The main enrichment method; passes specified input fields to
|
8
|
+
# #enrich_values, which must return an array of values with length equal to
|
9
|
+
# the number of output fields. The values of the output fields are set to
|
10
|
+
# the corresponding result from the enrichment.
|
11
|
+
#
|
12
|
+
# Pass fields to `input_fields` and `output_fields`. Fields are formatted
|
13
|
+
# as symbols in nested hashes, targeting a particular field in an
|
14
|
+
# ActiveTriples Resource property hierarchy:
|
15
|
+
#
|
16
|
+
# :sourceResource
|
17
|
+
# {:sourceResource => :spatial}
|
18
|
+
# {:sourceResource => {:creator => :name}}
|
19
|
+
#
|
20
|
+
# The record passed in is not altered, but cloned before the enrichment is
|
21
|
+
# applied. A common pattern may be:
|
22
|
+
#
|
23
|
+
# record = my_enrichment.enrich(record, input, output)
|
24
|
+
# record.persist!
|
25
|
+
#
|
26
|
+
# Input fields create an array selecting the values of all matching fields.
|
27
|
+
# For example:
|
28
|
+
#
|
29
|
+
# an array of values from record.sourceResource:
|
30
|
+
# :sourceResource
|
31
|
+
#
|
32
|
+
# an array of values combining spatial fields from the values of
|
33
|
+
# record.sourceResource:
|
34
|
+
# {:sourceResource => :spatial}
|
35
|
+
#
|
36
|
+
# an array of values combining name fields from the creators in
|
37
|
+
# record.sourceResource:
|
38
|
+
# {:sourceResource => {:creator => :name}}
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# Output fields should be specified at a high enough level that the
|
42
|
+
# enrichment can build a complete value set from the input values provided.
|
43
|
+
# An enrichment for mapping names to LCSH URIs, that alters all creator
|
44
|
+
# fields might be formatted:
|
45
|
+
#
|
46
|
+
# my_enrichment.enrich(record,
|
47
|
+
# [{:sourceResource => {:creator => :providedLabel}}],
|
48
|
+
# [{:sourceResource => :creator}])
|
49
|
+
#
|
50
|
+
# This would pass the values like the following, sourced from the
|
51
|
+
# providedLabel, to #enrich_value:
|
52
|
+
#
|
53
|
+
# [['Moomintroll', 'Moomin Papa', 'Moomin Mama']]
|
54
|
+
#
|
55
|
+
# And it would expect to receive an array of values set directly to creator,
|
56
|
+
# overwriting all existing creator values:
|
57
|
+
#
|
58
|
+
# [DPLA::MAP::Agent:0x3ff(default),
|
59
|
+
# DPLA::MAP::Agent:0x9f5(default),
|
60
|
+
# DPLA::MAP::Agent:0x3a8(default)]
|
61
|
+
#
|
62
|
+
# @param record [ActiveTriples::Resource] the record to enrich
|
63
|
+
# @param input_fields [Array] the fields whose values to pass to the
|
64
|
+
# enrichment method
|
65
|
+
# @param output_fields [Array] the fields on which to apply the enrichment
|
66
|
+
# @return [ActiveTriples::Resource] the enriched record
|
67
|
+
def enrich(record, input_fields, output_fields)
|
68
|
+
enrich!(record.clone, input_fields, output_fields)
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Runs the enrichment directly on the given record.
|
73
|
+
#
|
74
|
+
# @see Audumbla::Enrichment#enrich
|
75
|
+
def enrich!(record, input_fields, output_fields)
|
76
|
+
output_fields.map! { |f| field_to_chain(f) }
|
77
|
+
|
78
|
+
values = values_from_fields(record, input_fields)
|
79
|
+
values = enrich_value(values).dup
|
80
|
+
|
81
|
+
raise 'field/value mismatch.' \
|
82
|
+
"#{values.count} values for #{output_fields.count} fields." unless
|
83
|
+
values.count == output_fields.count
|
84
|
+
|
85
|
+
output_fields.each { |field| set_field(record, field, values.shift) }
|
86
|
+
record
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# @abstract Runs the enrichment against a field
|
91
|
+
#
|
92
|
+
# Accept an array of values from an ActiveTriples::Resource property, and
|
93
|
+
# return an array of values to set to output fields.
|
94
|
+
#
|
95
|
+
# @param [ActiveTriples::Resource, RDF::Literal] the value(s) to process
|
96
|
+
# @return [ActiveTriples::Resource] the enriched record
|
97
|
+
def enrich_value(_)
|
98
|
+
raise NotImplementedError
|
99
|
+
end
|
100
|
+
|
101
|
+
def list_fields(record)
|
102
|
+
fields = []
|
103
|
+
record.class.properties.each do |prop, _|
|
104
|
+
fields << prop.to_sym
|
105
|
+
|
106
|
+
objs = resources(record.send(fields.last)).map { |r| list_fields(r) }
|
107
|
+
next if objs.empty?
|
108
|
+
|
109
|
+
objs.flatten.each { |obj| fields << { prop => obj } }
|
110
|
+
end
|
111
|
+
fields
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def values_for_field(record, field_chain)
|
117
|
+
values = record.send(field_chain.first)
|
118
|
+
return values if field_chain.length == 1
|
119
|
+
resources(values).map { |v| values_for_field(v, field_chain[1..-1]) }
|
120
|
+
.flatten.compact
|
121
|
+
end
|
122
|
+
|
123
|
+
def set_field(record, field_chain, values)
|
124
|
+
field = field_chain.pop
|
125
|
+
return record.send("#{field}=".to_sym, values) if field_chain.length == 0
|
126
|
+
values_for_field(record, field_chain).each do |obj|
|
127
|
+
obj.send("#{field}=".to_sym, values)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def resources(values)
|
132
|
+
values.select { |v| v.is_a? ActiveTriples::Resource }
|
133
|
+
end
|
134
|
+
|
135
|
+
def literals(values)
|
136
|
+
values.select { |v| !v.is_a?(ActiveTriples::Resource) }
|
137
|
+
end
|
138
|
+
|
139
|
+
def field_to_chain(field)
|
140
|
+
return Array(field) if field.is_a? Symbol
|
141
|
+
[field.keys.first, field_to_chain(field.values.first)].flatten
|
142
|
+
end
|
143
|
+
|
144
|
+
def values_from_fields(record, input_fields)
|
145
|
+
input_fields.map { |f| values_for_field(record, field_to_chain(f)) }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Audumbla
|
2
|
+
##
|
3
|
+
# Mixin module for enriching a set of input_fields and setting the resulting
|
4
|
+
# values to a set of output fields.
|
5
|
+
module Enrichment
|
6
|
+
##
|
7
|
+
# The main enrichment method; passes specified input fields to
|
8
|
+
# #enrich_values, which must return an array of values with length equal to
|
9
|
+
# the number of output fields. The values of the output fields are set to
|
10
|
+
# the corresponding result from the enrichment.
|
11
|
+
#
|
12
|
+
# Pass fields to `input_fields` and `output_fields`. Fields are formatted
|
13
|
+
# as symbols in nested hashes, targeting a particular field in an
|
14
|
+
# ActiveTriples Resource property hierarchy:
|
15
|
+
#
|
16
|
+
# :sourceResource
|
17
|
+
# {:sourceResource => :spatial}
|
18
|
+
# {:sourceResource => {:creator => :name}}
|
19
|
+
#
|
20
|
+
# The record passed in is not altered, but cloned before the enrichment is
|
21
|
+
# applied. A common pattern may be:
|
22
|
+
#
|
23
|
+
# record = my_enrichment.enrich(record, input, output)
|
24
|
+
# record.persist!
|
25
|
+
#
|
26
|
+
# Input fields create an array selecting the values of all matching fields.
|
27
|
+
# For example:
|
28
|
+
#
|
29
|
+
# an array of values from record.sourceResource:
|
30
|
+
# :sourceResource
|
31
|
+
#
|
32
|
+
# an array of values combining spatial fields from the values of
|
33
|
+
# record.sourceResource:
|
34
|
+
# {:sourceResource => :spatial}
|
35
|
+
#
|
36
|
+
# an array of values combining name fields from the creators in
|
37
|
+
# record.sourceResource:
|
38
|
+
# {:sourceResource => {:creator => :name}}
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# Output fields should be specified at a high enough level that the
|
42
|
+
# enrichment can build a complete value set from the input values provided.
|
43
|
+
# An enrichment for mapping names to LCSH URIs, that alters all creator
|
44
|
+
# fields might be formatted:
|
45
|
+
#
|
46
|
+
# my_enrichment.enrich(record,
|
47
|
+
# [{:sourceResource => {:creator => :providedLabel}}],
|
48
|
+
# [{:sourceResource => :creator}])
|
49
|
+
#
|
50
|
+
# This would pass the values like the following, sourced from the
|
51
|
+
# providedLabel, to #enrich_value:
|
52
|
+
#
|
53
|
+
# [['Moomintroll', 'Moomin Papa', 'Moomin Mama']]
|
54
|
+
#
|
55
|
+
# And it would expect to receive an array of values set directly to creator,
|
56
|
+
# overwriting all existing creator values:
|
57
|
+
#
|
58
|
+
# [DPLA::MAP::Agent:0x3ff(default),
|
59
|
+
# DPLA::MAP::Agent:0x9f5(default),
|
60
|
+
# DPLA::MAP::Agent:0x3a8(default)]
|
61
|
+
#
|
62
|
+
# @param record [ActiveTriples::Resource] the record to enrich
|
63
|
+
# @param input_fields [Array] the fields whose values to pass to the
|
64
|
+
# enrichment method
|
65
|
+
# @param output_fields [Array] the fields on which to apply the enrichment
|
66
|
+
# @return [ActiveTriples::Resource] the enriched record
|
67
|
+
def enrich(record, input_fields, output_fields)
|
68
|
+
enrich!(record.clone, input_fields, output_fields)
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Runs the enrichment directly on the given record.
|
73
|
+
#
|
74
|
+
# @see Krikri::Enrichment#enrich
|
75
|
+
def enrich!(record, input_fields, output_fields)
|
76
|
+
output_fields.map! { |f| field_to_chain(f) }
|
77
|
+
|
78
|
+
values = values_from_fields(record, input_fields)
|
79
|
+
values = enrich_value(values).dup
|
80
|
+
|
81
|
+
raise 'field/value mismatch.' \
|
82
|
+
"#{values.count} values for #{output_fields.count} fields." unless
|
83
|
+
values.count == output_fields.count
|
84
|
+
|
85
|
+
output_fields.each { |field| set_field(record, field, values.shift) }
|
86
|
+
record
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# @abstract Runs the enrichment against a field
|
91
|
+
#
|
92
|
+
# Accept an array of values from an ActiveTriples::Resource property, and
|
93
|
+
# return an array of values to set to output fields.
|
94
|
+
#
|
95
|
+
# @param [ActiveTriples::Resource, RDF::Literal] the value(s) to process
|
96
|
+
# @return [ActiveTriples::Resource] the enriched record
|
97
|
+
def enrich_value(_)
|
98
|
+
raise NotImplementedError
|
99
|
+
end
|
100
|
+
|
101
|
+
def list_fields(record)
|
102
|
+
fields = []
|
103
|
+
record.class.properties.each do |prop, _|
|
104
|
+
fields << prop.to_sym
|
105
|
+
|
106
|
+
objs = resources(record.send(fields.last)).map { |r| list_fields(r) }
|
107
|
+
next if objs.empty?
|
108
|
+
|
109
|
+
objs.flatten.each { |obj| fields << { prop => obj } }
|
110
|
+
end
|
111
|
+
fields
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def values_for_field(record, field_chain)
|
117
|
+
values = record.send(field_chain.first)
|
118
|
+
return values if field_chain.length == 1
|
119
|
+
resources(values).map { |v| values_for_field(v, field_chain[1..-1]) }
|
120
|
+
.flatten.compact
|
121
|
+
end
|
122
|
+
|
123
|
+
def set_field(record, field_chain, values)
|
124
|
+
field = field_chain.pop
|
125
|
+
return record.send("#{field}=".to_sym, values) if field_chain.length == 0
|
126
|
+
values_for_field(record, field_chain).each do |obj|
|
127
|
+
obj.send("#{field}=".to_sym, values)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def resources(values)
|
132
|
+
values.select { |v| v.is_a? ActiveTriples::Resource }
|
133
|
+
end
|
134
|
+
|
135
|
+
def literals(values)
|
136
|
+
values.select { |v| !v.is_a?(ActiveTriples::Resource) }
|
137
|
+
end
|
138
|
+
|
139
|
+
def field_to_chain(field)
|
140
|
+
return Array(field) if field.is_a? Symbol
|
141
|
+
[field.keys.first, field_to_chain(field.values.first)].flatten
|
142
|
+
end
|
143
|
+
|
144
|
+
def values_from_fields(record, input_fields)
|
145
|
+
input_fields.map { |f| values_for_field(record, field_to_chain(f)) }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Audumbla
|
2
|
+
##
|
3
|
+
# Enrich a specific field or list of fields, setting the property to the
|
4
|
+
# supplied value
|
5
|
+
module FieldEnrichment
|
6
|
+
include Enrichment
|
7
|
+
|
8
|
+
##
|
9
|
+
# The main enrichment method; runs the enrichment against a stated
|
10
|
+
# set of fields for a record.
|
11
|
+
#
|
12
|
+
# This is a narrower case of `Audumbla::Enrichment` which runs the
|
13
|
+
# enrichment against each of the specified fields in turn, setting
|
14
|
+
# the field's value to the result.
|
15
|
+
#
|
16
|
+
# For example:
|
17
|
+
#
|
18
|
+
# delete_empty_string_literals.enrich(record,
|
19
|
+
# {:sourceResource => {:creator => :name}})
|
20
|
+
#
|
21
|
+
# To apply the enrichment across all fields, leave the fields parameter
|
22
|
+
# empty, or use `:all`:
|
23
|
+
#
|
24
|
+
# delete_empty_string_literals.enrich(record)
|
25
|
+
# delete_empty_string_literals.enrich(record, :all)
|
26
|
+
#
|
27
|
+
# @see Audumbla::Enrichment#enrich for documentation about field
|
28
|
+
# formatting
|
29
|
+
#
|
30
|
+
# @param record [ActiveTriples::Resource] the record to enrich
|
31
|
+
# @param fields [Array] the fields on which to apply the enrichment
|
32
|
+
# @return [ActiveTriples::Resource] the enriched record
|
33
|
+
def enrich(record, *fields)
|
34
|
+
record = record.clone
|
35
|
+
return enrich_all(record) if fields.empty? || fields == [:all]
|
36
|
+
fields.each { |f| enrich_field(record, field_to_chain(f)) }
|
37
|
+
record
|
38
|
+
end
|
39
|
+
|
40
|
+
def enrich_field(record, field_chain)
|
41
|
+
field = field_chain.first
|
42
|
+
return record unless record.respond_to? field
|
43
|
+
values = record.send(field)
|
44
|
+
if field_chain.length == 1
|
45
|
+
new_values = values.map { |v| enrich_value(v) }.flatten.compact
|
46
|
+
record.send("#{field}=".to_sym, new_values)
|
47
|
+
else
|
48
|
+
resources(values).each { |v| enrich_field(v, field_chain[1..-1]) }
|
49
|
+
end
|
50
|
+
record
|
51
|
+
end
|
52
|
+
|
53
|
+
def enrich_all(record)
|
54
|
+
list_fields(record).each do |field|
|
55
|
+
enrich_field(record, field_to_chain(field))
|
56
|
+
end
|
57
|
+
record
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Krikri
|
2
|
+
##
|
3
|
+
# Enrich a specific field or list of fields, setting the property to the
|
4
|
+
# supplied value
|
5
|
+
module FieldEnrichment
|
6
|
+
include Enrichment
|
7
|
+
|
8
|
+
##
|
9
|
+
# The main enrichment method; runs the enrichment against a stated
|
10
|
+
# set of fields for a record.
|
11
|
+
#
|
12
|
+
# This is a narrower case of `Krikri::Enrichment` which runs the
|
13
|
+
# enrichment against each of the specified fields in turn, setting
|
14
|
+
# the field's value to the result.
|
15
|
+
#
|
16
|
+
# For example:
|
17
|
+
#
|
18
|
+
# delete_empty_string_literals.enrich(record,
|
19
|
+
# {:sourceResource => {:creator => :name}})
|
20
|
+
#
|
21
|
+
# To apply the enrichment across all fields, leave the fields parameter
|
22
|
+
# empty, or use `:all`:
|
23
|
+
#
|
24
|
+
# delete_empty_string_literals.enrich(record)
|
25
|
+
# delete_empty_string_literals.enrich(record, :all)
|
26
|
+
#
|
27
|
+
# @see Krikri::Enrichment#enrich for documentation about field
|
28
|
+
# formatting
|
29
|
+
#
|
30
|
+
# @param record [ActiveTriples::Resource] the record to enrich
|
31
|
+
# @param fields [Array] the fields on which to apply the enrichment
|
32
|
+
# @return [ActiveTriples::Resource] the enriched record
|
33
|
+
def enrich(record, *fields)
|
34
|
+
record = record.clone
|
35
|
+
return enrich_all(record) if fields.empty? || fields == [:all]
|
36
|
+
fields.each { |f| enrich_field(record, field_to_chain(f)) }
|
37
|
+
record
|
38
|
+
end
|
39
|
+
|
40
|
+
def enrich_field(record, field_chain)
|
41
|
+
field = field_chain.first
|
42
|
+
return record unless record.respond_to? field
|
43
|
+
values = record.send(field)
|
44
|
+
if field_chain.length == 1
|
45
|
+
new_values = values.map { |v| enrich_value(v) }.flatten.compact
|
46
|
+
record.send("#{field}=".to_sym, new_values)
|
47
|
+
else
|
48
|
+
resources(values).each { |v| enrich_field(v, field_chain[1..-1]) }
|
49
|
+
end
|
50
|
+
record
|
51
|
+
end
|
52
|
+
|
53
|
+
def enrich_all(record)
|
54
|
+
list_fields(record).each do |field|
|
55
|
+
enrich_field(record, field_to_chain(field))
|
56
|
+
end
|
57
|
+
record
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
shared_context 'with record' do
|
2
|
+
let(:record) { build(:aggregation) }
|
3
|
+
end
|
4
|
+
|
5
|
+
shared_examples 'an enrichment' do
|
6
|
+
include_context 'with record'
|
7
|
+
|
8
|
+
it { is_expected.to be_a Audumbla::Enrichment }
|
9
|
+
it { is_expected.to respond_to :enrich_value }
|
10
|
+
|
11
|
+
describe '#list_fields' do
|
12
|
+
let(:list) { subject.list_fields(record) }
|
13
|
+
|
14
|
+
it 'generates a list of fields' do
|
15
|
+
expect(list).to include(an_instance_of(Symbol), an_instance_of(Hash))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#enrich' do
|
20
|
+
before { allow(subject).to receive(:enrich_value).and_return(new_value) }
|
21
|
+
|
22
|
+
let(:new_value) { ['Christmas in Moominvalley'] }
|
23
|
+
let(:args) do
|
24
|
+
subject.class.instance_method(:enrich).arity > 1 ? enrich_args : [record]
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns a record eql to the input record' do
|
28
|
+
expect(subject.enrich(*args)).to eq record
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'copies the input record' do
|
32
|
+
expect(subject.enrich(*args)).not_to eql record
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'does not change the record object passed in' do
|
36
|
+
expect { subject.enrich(*args) }.not_to change { record }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
shared_examples 'a generic enrichment' do
|
42
|
+
it_behaves_like 'an enrichment'
|
43
|
+
include_context 'with record'
|
44
|
+
|
45
|
+
let(:enrich_args) do
|
46
|
+
[record, [{ :sourceResource => :title }], [{ :sourceResource => :creator }]]
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#enrich' do
|
50
|
+
before do
|
51
|
+
allow(subject).to receive(:enrich_value).and_return(new_value)
|
52
|
+
end
|
53
|
+
|
54
|
+
shared_examples 'multiple input fields' do
|
55
|
+
before do
|
56
|
+
record.sourceResource.first.spatial.first.name = 'NY'
|
57
|
+
enrich_args[1] << { :sourceResource => { :spatial => :name } }
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:input_values) do
|
61
|
+
[record.sourceResource.map(&:title).flatten,
|
62
|
+
record.sourceResource.map { |sr| sr.spatial.map(&:name) }.flatten]
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'enriches with values for input fields' do
|
66
|
+
expect(subject).to receive(:enrich_value).with(input_values)
|
67
|
+
subject.enrich(*enrich_args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with single output field' do
|
72
|
+
include_examples 'multiple input fields'
|
73
|
+
|
74
|
+
let(:new_value) { ['snufkin'] }
|
75
|
+
|
76
|
+
it 'enriches targeted field' do
|
77
|
+
subject.enrich(*enrich_args).sourceResource.map do |cho|
|
78
|
+
expect(cho.creator).to eq new_value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with multiple output fields' do
|
84
|
+
include_examples 'multiple input fields'
|
85
|
+
|
86
|
+
before do
|
87
|
+
enrich_args[2] << { :sourceResource => :spatial }
|
88
|
+
end
|
89
|
+
|
90
|
+
let(:new_value) { [['snufkin'], ['moominvalley']] }
|
91
|
+
|
92
|
+
it 'enriches targeted fields' do
|
93
|
+
subject.enrich(*enrich_args).sourceResource.map do |cho|
|
94
|
+
expect(cho.creator).to eq new_value.first
|
95
|
+
expect(cho.spatial).to eq new_value[1]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
shared_examples 'a field enrichment' do
|
103
|
+
it_behaves_like 'an enrichment'
|
104
|
+
include_context 'with record'
|
105
|
+
|
106
|
+
let(:updated_value) { 'Christmas in Moominvalley' }
|
107
|
+
|
108
|
+
describe '#enrich_field' do
|
109
|
+
let(:field_chain) { [:sourceResource, :creator, :providedLabel] }
|
110
|
+
let(:klass) { record.sourceResource.first.creator.first.class }
|
111
|
+
let(:enriched) { subject.enrich_field(record, field_chain) }
|
112
|
+
|
113
|
+
before do
|
114
|
+
allow(subject).to receive(:enrich_value).and_return(updated_value)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'updates value with enriched version' do
|
118
|
+
expect(enriched.sourceResource.first.creator.first.providedLabel)
|
119
|
+
.to eq [updated_value]
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when targeted value is empty' do
|
123
|
+
before do
|
124
|
+
enriched.sourceResource.first.creator.first.providedLabel = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'passes over value' do
|
128
|
+
expect(enriched.sourceResource.first.creator.first.providedLabel)
|
129
|
+
.to eq []
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'with multiple values' do
|
134
|
+
before do
|
135
|
+
new_creator = klass.new
|
136
|
+
new_creator.providedLabel = 'old value'
|
137
|
+
record.sourceResource.first.creator << new_creator
|
138
|
+
record.sourceResource.first.creator << 'literal value'
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'retains literal values' do
|
142
|
+
expect(enriched.sourceResource.first.creator)
|
143
|
+
.to contain_exactly('literal value',
|
144
|
+
an_instance_of(klass),
|
145
|
+
an_instance_of(klass))
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'updates values with enriched versions' do
|
149
|
+
creators = enriched.sourceResource.first.creator.select do |o|
|
150
|
+
o.is_a?(klass)
|
151
|
+
end
|
152
|
+
creators.each { |val| expect(val.providedLabel).to eq [updated_value] }
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when node is missing property' do
|
156
|
+
before do
|
157
|
+
record.sourceResource.first.creator << node
|
158
|
+
end
|
159
|
+
|
160
|
+
let(:node) do
|
161
|
+
creator = ActiveTriples::Resource.new
|
162
|
+
creator.set_value(RDF::DC.title, 'moomin')
|
163
|
+
creator
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'leaves node unaltered' do
|
167
|
+
expect(record.sourceResource.first.creator).to include node
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe '#enrich_all' do
|
174
|
+
it 'runs enrichment over all fields' do
|
175
|
+
expect(subject).to receive(:enrich_field)
|
176
|
+
.exactly(subject.list_fields(record).count).times
|
177
|
+
subject.enrich_all(record)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'sets fields to new value' do
|
181
|
+
allow(subject).to receive(:enrich_value).and_return(updated_value)
|
182
|
+
subject.enrich_all(record)
|
183
|
+
record.class.properties.each do |prop, _|
|
184
|
+
expect(record.send(prop)).to eq [updated_value]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'returns the record' do
|
189
|
+
expect(subject.enrich_all(record)).to eq record
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe 'enrich' do
|
194
|
+
it 'defaults to all fields' do
|
195
|
+
expect(subject).to receive(:enrich_all)
|
196
|
+
subject.enrich(record)
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'accepts :all for fields' do
|
200
|
+
expect(subject).to receive(:enrich_all)
|
201
|
+
subject.enrich(record, :all)
|
202
|
+
end
|
203
|
+
|
204
|
+
context 'with field arguments' do
|
205
|
+
let(:simple_field) { :preview }
|
206
|
+
let(:deep_field) do
|
207
|
+
{ :sourceResource => { :creator => :providedLabel } }
|
208
|
+
end
|
209
|
+
let(:deep_field_2) do
|
210
|
+
{ :sourceResource => { :spatial => :parentFeature } }
|
211
|
+
end
|
212
|
+
|
213
|
+
let(:deep_field_chain) { [:sourceResource, :creator, :providedLabel] }
|
214
|
+
let(:deep_field_2_chain) { [:sourceResource, :spatial, :parentFeature] }
|
215
|
+
|
216
|
+
it 'runs against simple fields' do
|
217
|
+
expect(subject).to receive(:enrich_field).with(record, [simple_field])
|
218
|
+
subject.enrich(record, simple_field)
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'runs against deep fields' do
|
222
|
+
expect(subject).to receive(:enrich_field).with(record, deep_field_chain)
|
223
|
+
subject.enrich(record, deep_field)
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'runs against multiple fields' do
|
227
|
+
expect(subject).to receive(:enrich_field).with(record, [simple_field])
|
228
|
+
expect(subject).to receive(:enrich_field).with(record, deep_field_chain)
|
229
|
+
expect(subject).to receive(:enrich_field)
|
230
|
+
.with(record, deep_field_2_chain)
|
231
|
+
subject.enrich(record, simple_field, deep_field, deep_field_2)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'is a copy of the input record' do
|
235
|
+
expect(subject.enrich(record, simple_field, deep_field, deep_field_2))
|
236
|
+
.to eq record
|
237
|
+
expect(subject.enrich(record, simple_field, deep_field, deep_field_2))
|
238
|
+
.not_to eql record
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Audumbla::Enrichment do
|
4
|
+
subject { klass.new }
|
5
|
+
|
6
|
+
let(:klass) { Class.new { include Audumbla::Enrichment } }
|
7
|
+
|
8
|
+
it_behaves_like 'a generic enrichment'
|
9
|
+
|
10
|
+
describe '#enrich_value' do
|
11
|
+
it 'is abstract' do
|
12
|
+
expect { subject.enrich_value(nil) }.to raise_error NotImplementedError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Krikri::Enrichment do
|
4
|
+
subject { klass.new }
|
5
|
+
|
6
|
+
let(:klass) { Class.new { include Krikri::Enrichment } }
|
7
|
+
|
8
|
+
it_behaves_like 'a generic enrichment'
|
9
|
+
|
10
|
+
describe '#enrich_value' do
|
11
|
+
it 'is abstract' do
|
12
|
+
expect { subject.enrich_value(nil) }.to raise_error NotImplementedError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Audumbla::FieldEnrichment do
|
4
|
+
subject { enrichment_class.new }
|
5
|
+
|
6
|
+
let(:enrichment_class) { Class.new { include Audumbla::FieldEnrichment } }
|
7
|
+
|
8
|
+
context 'with implementation' do
|
9
|
+
before { allow(subject).to receive(:enrich_value).and_return('moomin') }
|
10
|
+
|
11
|
+
it_behaves_like 'a field enrichment'
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#enrich_value' do
|
15
|
+
it 'is abstract' do
|
16
|
+
expect { subject.enrich_value(nil) }.to raise_error NotImplementedError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Krikri::FieldEnrichment do
|
4
|
+
subject { enrichment_class.new }
|
5
|
+
|
6
|
+
let(:enrichment_class) { Class.new { include Krikri::FieldEnrichment } }
|
7
|
+
|
8
|
+
context 'with implementation' do
|
9
|
+
before { allow(subject).to receive(:enrich_value).and_return('moomin') }
|
10
|
+
|
11
|
+
it_behaves_like 'a field enrichment'
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#enrich_value' do
|
15
|
+
it 'is abstract' do
|
16
|
+
expect { subject.enrich_value(nil) }.to raise_error NotImplementedError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'audumbla'
|
2
|
+
require 'audumbla/spec/enrichment'
|
3
|
+
|
4
|
+
if ENV['COVERAGE'] == 'yes'
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'codeclimate-test-reporter'
|
10
|
+
CodeClimate::TestReporter.start
|
11
|
+
|
12
|
+
require 'rspec'
|
13
|
+
require 'factory_girl'
|
14
|
+
require 'dpla/map/factories'
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.color = true
|
18
|
+
config.formatter = :progress
|
19
|
+
config.mock_with :rspec
|
20
|
+
|
21
|
+
config.include FactoryGirl::Syntax::Methods
|
22
|
+
|
23
|
+
config.order = 'random'
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
if ENV['COVERAGE'] == 'yes'
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'codeclimate-test-reporter'
|
7
|
+
CodeClimate::TestReporter.start
|
8
|
+
|
9
|
+
require 'rspec'
|
10
|
+
require 'factory_girl'
|
11
|
+
|
12
|
+
# Load support files
|
13
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.color = true
|
17
|
+
config.formatter = :progress
|
18
|
+
config.mock_with :rspec
|
19
|
+
|
20
|
+
config.include FactoryGirl::Syntax::Methods
|
21
|
+
|
22
|
+
config.order = 'random'
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: audumbla
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Audrey Altman
|
8
|
+
- Mark Breedlove
|
9
|
+
- Tom Johnson
|
10
|
+
- Mark Matienzo
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2015-05-29 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: dpla-map
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - "~>"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 4.0.0.0.pre.10
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 4.0.0.0.pre.10
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - "~>"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '3.0'
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '3.0'
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: pry
|
46
|
+
requirement: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 0.10.0
|
51
|
+
type: :development
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 0.10.0
|
58
|
+
description: Metadata enrichment for cultural heritage institutions.
|
59
|
+
email:
|
60
|
+
- tech@dp.la
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- README.md
|
66
|
+
- lib/audumbla.rb
|
67
|
+
- lib/audumbla.rb~
|
68
|
+
- lib/audumbla/enrichment.rb
|
69
|
+
- lib/audumbla/enrichment.rb~
|
70
|
+
- lib/audumbla/enrichments.rb
|
71
|
+
- lib/audumbla/enrichments.rb~
|
72
|
+
- lib/audumbla/enrichments/geocode.rb~
|
73
|
+
- lib/audumbla/enrichments/version.rb~
|
74
|
+
- lib/audumbla/field_enrichment.rb
|
75
|
+
- lib/audumbla/field_enrichment.rb~
|
76
|
+
- lib/audumbla/spec/enrichment.rb
|
77
|
+
- lib/audumbla/version.rb
|
78
|
+
- lib/audumbla/version.rb~
|
79
|
+
- spec/lib/audumbla/enrichment_spec.rb
|
80
|
+
- spec/lib/audumbla/enrichment_spec.rb~
|
81
|
+
- spec/lib/audumbla/enrichments/geocode_spec.rb~
|
82
|
+
- spec/lib/audumbla/field_enrichment_spec.rb
|
83
|
+
- spec/lib/audumbla/field_enrichment_spec.rb~
|
84
|
+
- spec/spec_helper.rb
|
85
|
+
- spec/spec_helper.rb~
|
86
|
+
homepage: http://github.com/dpla/audumbla
|
87
|
+
licenses:
|
88
|
+
- MIT
|
89
|
+
metadata: {}
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
requirements: []
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 2.2.1
|
107
|
+
signing_key:
|
108
|
+
specification_version: 4
|
109
|
+
summary: A toolkit for enhancement of RDF Metadata
|
110
|
+
test_files:
|
111
|
+
- spec/lib/audumbla/enrichment_spec.rb
|
112
|
+
- spec/lib/audumbla/field_enrichment_spec.rb
|
113
|
+
- spec/lib/audumbla/field_enrichment_spec.rb~
|
114
|
+
- spec/lib/audumbla/enrichment_spec.rb~
|
115
|
+
- spec/lib/audumbla/enrichments/geocode_spec.rb~
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
- spec/spec_helper.rb~
|
118
|
+
has_rdoc:
|