spread2rdf 0.0.1pre.1 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +8 -8
  2. data/Gemfile.ocra +6 -0
  3. data/VERSION +1 -1
  4. data/bin/spread2rdf +1 -1
  5. data/lib/spread2rdf/attributes.rb +1 -0
  6. data/lib/spread2rdf/cli.rb +128 -34
  7. data/lib/spread2rdf/coord.rb +49 -0
  8. data/lib/spread2rdf/mapping/cell.rb +105 -0
  9. data/lib/spread2rdf/mapping/column.rb +63 -0
  10. data/lib/spread2rdf/mapping/column_block.rb +23 -0
  11. data/lib/spread2rdf/mapping/default_cell_mappings.rb +26 -0
  12. data/lib/spread2rdf/mapping/element.rb +64 -0
  13. data/lib/spread2rdf/mapping/resource.rb +95 -0
  14. data/lib/spread2rdf/mapping/sheet.rb +80 -0
  15. data/lib/spread2rdf/mapping/spreadsheet.rb +56 -0
  16. data/lib/spread2rdf/mapping/statement.rb +22 -0
  17. data/lib/spread2rdf/mapping/worksheet.rb +12 -0
  18. data/lib/spread2rdf/namespace.rb +6 -2
  19. data/lib/spread2rdf/roo_helper.rb +45 -0
  20. data/lib/spread2rdf/schema/column.rb +37 -0
  21. data/lib/spread2rdf/{spreadsheet/sub_sheet.rb → schema/column_block.rb} +3 -2
  22. data/lib/spread2rdf/{spreadsheet → schema}/element.rb +12 -16
  23. data/lib/spread2rdf/schema/schema.rb +19 -0
  24. data/lib/spread2rdf/schema/sheet.rb +87 -0
  25. data/lib/spread2rdf/schema/sheet_dsl.rb +54 -0
  26. data/lib/spread2rdf/schema/spreadsheet.rb +49 -0
  27. data/lib/spread2rdf/schema/spreadsheet_dsl.rb +42 -0
  28. data/lib/spread2rdf/schema/statement_mapping_schema.rb +26 -0
  29. data/lib/spread2rdf/schema/worksheet.rb +47 -0
  30. data/lib/spread2rdf.rb +34 -2
  31. data/ontologies/unit-v1.1.ttl +8330 -0
  32. data/spread2rdf.gemspec +4 -2
  33. metadata +50 -26
  34. data/lib/spread2rdf/helper.rb +0 -14
  35. data/lib/spread2rdf/spreadsheet/column.rb +0 -48
  36. data/lib/spread2rdf/spreadsheet/column_mapping_context.rb +0 -156
  37. data/lib/spread2rdf/spreadsheet/coord.rb +0 -51
  38. data/lib/spread2rdf/spreadsheet/mapping_context.rb +0 -67
  39. data/lib/spread2rdf/spreadsheet/mapping_dsl.rb +0 -23
  40. data/lib/spread2rdf/spreadsheet/sheet.rb +0 -128
  41. data/lib/spread2rdf/spreadsheet/sheet_dsl.rb +0 -34
  42. data/lib/spread2rdf/spreadsheet/sheet_mapping_context.rb +0 -90
  43. data/lib/spread2rdf/spreadsheet/sub_sheet_mapping_context.rb +0 -55
  44. data/lib/spread2rdf/spreadsheet/worksheet.rb +0 -49
  45. data/lib/spread2rdf/spreadsheet.rb +0 -92
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MzBlOGJjZDMyMjBjMjNmNDA5MTk3MmY1NjMzMmE2OWNiZGE3MTljYw==
4
+ NTJjZDkxNmJhNmM2ZjFmYjM4ZGJlZjE2MGQyZmZkNWMzMWI1NGQ4Yw==
5
5
  data.tar.gz: !binary |-
6
- Y2IxMjY2MDBjZDk1OGYxNjE2YjlmM2UyZGZkYTQ2NmZjNDk5MWYyYQ==
6
+ YjFmZjA5ZmZmOTk5ZWJjODAwOTgwZDNkZjkyYWY3NTgwMjllYzBkOA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ODlkODhkZDQ1NzZmNGVhY2E0MGIwNDgyOWVmZDFkODkyYjJlYmNiZjYyMzlh
10
- ZmEzNjk4YjJiNjIyZjM1OTM2YTQ1ZGZlZDJjZTFhOTA1Mzc0ZWJlZTY4NjY1
11
- YmEyNGZmNGM1ZjBkYTMxMTNmZWQ2YTZkNzVkOTViMDA2Y2I0ZTk=
9
+ OTEzM2JjNjkyNTkyMGJiNDUwZTE1OTE0NjE4ZjcyZmNkZTk5MWFjMDYyYzU5
10
+ Nzk0NWU5MDVhMmIwYmJkNjFiMjA5ZmI5NjU1Yjg4NzMxNWM5Yjk3MzFjMTgy
11
+ MGRjMWJiN2VlMzU5OWNkYmNhMDkxZjNlN2ExOWUwN2ZhYmQzOGI=
12
12
  data.tar.gz: !binary |-
13
- MDExZWZiZjY4YjBlNDdkYzhkN2ExZjNmNTVhNWVkNGEwMDBmYzg0YTY0ZGM2
14
- Njk3ZmQ4NmU3MmY3MjAxYzkwZjkzZThjM2VhZWMzYjYyZmJjYjBjMTc0MTdi
15
- YzUwNzlhNTRhMjY0NDJiNzIxZTdiYTkzNTZlM2U0ZTBjZTNmODY=
13
+ N2MxZGQ4YjMxYmI2Y2IyN2NiMjFiN2U4NmRkZjA4NGJlMWNkNzgxYjg5MWU2
14
+ YTkyZmY1ZGM5YWIyZTkwMjFmODI5ZDUwZTc4ZWQ2ZmFlMzVmMzc3NGE1Nzcz
15
+ ZGIzZDBmMTA2MWRhNjJmZDJmMGY3NzM3NDEyOGNiODFlOTQwNGU=
data/Gemfile.ocra ADDED
@@ -0,0 +1,6 @@
1
+ # we need this dedicated Gemfile currently, due to this issue:
2
+ # https://github.com/larsch/ocra/issues/59
3
+
4
+ source 'https://rubygems.org'
5
+
6
+ gem 'spread2rdf', '~> 0.0.1'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1pre.1
1
+ 0.0.1
data/bin/spread2rdf CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'spread2rdf'
3
- Spread2RDF::Cli.new.run
3
+ Spread2RDF::CLI.run
@@ -6,6 +6,7 @@ module Spread2RDF
6
6
  def attributes
7
7
  if superclass.respond_to?(:attributes) and
8
8
  (super_attributes = superclass.attributes).is_a? Hash
9
+ @attributes ||= {}
9
10
  @attributes.reverse_merge(super_attributes)
10
11
  else
11
12
  @attributes
@@ -1,63 +1,119 @@
1
1
  # coding: utf-8
2
+ require 'singleton'
2
3
 
3
4
  module Spread2RDF
4
5
  class Cli
5
- def initialize
6
+ include Singleton
7
+
8
+ attr_accessor :mapping_schema
9
+
10
+ def run(options = {})
11
+ @running = true
12
+ init(options)
6
13
  parse_command_line!
14
+ case
15
+ when compile? then compile(@mapping_schema)
16
+ else convert
17
+ end
18
+ self
19
+ rescue => e
20
+ if Spread2RDF.debug_mode
21
+ raise e
22
+ else
23
+ abort e.to_s
24
+ end
7
25
  end
8
26
 
9
- def run(schema_spec_file = nil)
10
- schema_spec_file ||= @options[:schema_spec_file]
11
- abort "No schema specification file given" if schema_spec_file.nil?
12
- abort "Couldn't find schema specification file #{schema_spec_file}" unless
13
- File.exist?(schema_spec_file)
14
- load schema_spec_file
15
- abort "No schema specification found" if Spreadsheet.definitions.empty?
16
- puts "Reading #{@input_file} ..."
17
- @table = Spreadsheet.definitions.first
18
- @table.read(@input_file)
19
- write_output
20
- self
27
+ def init(options)
28
+ @options = options
29
+ @input_file = @options.delete(:input_file) if @options[:input_file]
30
+ self.mapping_schema = @options.delete(:schema) if @options[:schema]
31
+ end
32
+ private :init
33
+
34
+ def running?
35
+ @running
36
+ end
37
+
38
+ def compile?
39
+ !!@options[:compile]
40
+ end
41
+
42
+ def mapping_schema=(schema)
43
+ @mapping_schema =
44
+ case schema
45
+ when nil then nil
46
+ when Schema::Spreadsheet then schema
47
+ when String
48
+ abort "no schema specification file given" if schema.nil?
49
+ abort "couldn't find schema specification file #{schema}" unless
50
+ File.exist?(schema)
51
+ load schema
52
+ abort "no schema specification found" if Schema.definitions.empty?
53
+ Schema.definitions.first
54
+ else raise ArgumentError
55
+ end
21
56
  end
22
57
 
23
58
  private
24
59
 
25
- # Parse command line options
26
- def parse_command_line!(options={})
27
- @options = options
60
+ def parse_command_line!
28
61
  optparse = OptionParser.new do |opts|
29
- opts.banner = 'Usage: spread2rdf [options] -s SPEC_FILE SPREAD_SHEET_FILE'
62
+ if mapping_schema
63
+ opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] SPREAD_SHEET_FILE"
64
+ else
65
+ opts.banner = 'Usage: spread2rdf [options] -s SPEC_FILE SPREAD_SHEET_FILE'
66
+ end
30
67
 
31
68
  opts.on( '-h', '--help', 'Display this information' ) do
32
69
  puts opts
33
70
  exit
34
71
  end
35
72
 
36
- @options[:output_dir] = '.'
37
- opts.on( '-o', '--output DIR', 'Output directory (default: current directory)' ) do |dir|
38
- abort "Output directory #{dir} doesn't exist" unless Dir.exist?(dir)
73
+ opts.on( '-v', '--version', 'Print version information' ) do
74
+ puts "Spread2RDF #{VERSION}"
75
+ exit
76
+ end
77
+
78
+ if not mapping_schema
79
+ opts.on( '-c', '--compile', 'Compile the schema specification to an executable' ) do
80
+ @options[:compile] = true
81
+ end
82
+
83
+ opts.on( '-s', '--schema SPEC_FILE', 'Schema specification file (required)' ) do |file|
84
+ if @options[:compile]
85
+ @mapping_schema = file
86
+ else
87
+ self.mapping_schema = file
88
+ end
89
+ end
90
+ end
91
+
92
+ @options[:output_dir] ||= '.'
93
+ opts.on( '-o', '--output DIR', "Output directory (default: #{@options[:output_dir]})" ) do |dir|
94
+ abort "Output directory #{dir} doesn't exist" unless compile? or Dir.exist?(dir)
39
95
  @options[:output_dir] = dir
40
96
  end
41
97
 
42
- @options[:output_format] = 'ttl'
98
+ @options[:output_format] ||= 'ttl'
43
99
  opts.on( '-f', '--output-format FORMAT', 'Serialization format for the RDF data',
44
- "FORMAT being one of: nt, n3, ttl, rdf, xml, html, json (default: ttl)") do |format|
45
- #format = 'turtle' if format == 'ttl'
100
+ "FORMAT being one of: nt, n3, ttl, rdf, xml, html, json (default: #{@options[:output_format]})") do |format|
46
101
  @options[:output_format] = format.strip.downcase
47
102
  end
48
103
 
49
- @options[:schema_spec_file] = nil
50
- opts.on( '-s', '--schema SPEC_FILE', 'Schema specification file (required)' ) do |file|
51
- @options[:schema_spec_file] = file
104
+ opts.on( '-d', '--debug', 'Run in debug mode' ) do
105
+ Spread2RDF.debug_mode = true
52
106
  end
53
107
 
54
108
  end
55
-
56
109
  optparse.parse!
57
- raise OptionParser::ParseError, 'required file arguments missing' if ARGV.empty?
58
- raise OptionParser::ParseError, 'required schema specification file missing' if @options[:schema_spec_file].nil?
59
-
60
- @input_file = ARGV.first
110
+ if compile?
111
+ @mapping_schema ||= ARGV.first or raise OptionParser::ParseError, 'required schema specification file missing'
112
+ else
113
+ raise OptionParser::ParseError, 'required schema specification file missing' if @mapping_schema.nil?
114
+ @input_file = ARGV.first or @input_file or
115
+ raise OptionParser::ParseError, 'required file arguments missing'
116
+ end
61
117
  rescue OptionParser::ParseError => e
62
118
  puts e.message
63
119
  puts optparse.help
@@ -72,8 +128,8 @@ module Spread2RDF
72
128
 
73
129
  def write_output
74
130
  filename = output_filename
75
- abort 'No RDF data to write!' if @table.try(:to_rdf).blank?
76
- graph = @table.to_rdf
131
+ abort 'No RDF data to write!' if @mapping.try(:graph).blank?
132
+ graph = @mapping.graph
77
133
  puts "Writing #{graph.count} RDF statements to #{filename} ... "
78
134
  # TODO: base_uri: ... for writer constructor
79
135
  RDF::Writer.open(filename) do |writer|
@@ -85,7 +141,45 @@ module Spread2RDF
85
141
  end
86
142
  graph.each_statement { |statement| writer << statement }
87
143
  end
144
+
145
+ end
146
+
147
+ def convert
148
+ puts "Reading #{@input_file} ..."
149
+ @mapping = mapping_schema.map(@input_file)
150
+ write_output
151
+ end
152
+
153
+ def compile(mapping_file)
154
+ output = if File.directory?(@options[:output_dir])
155
+ output_dir = @options[:output_dir]
156
+ output_file = File.basename(mapping_file, File.extname(mapping_file)) + '.exe'
157
+ File.join(output_dir, output_file)
158
+ else
159
+ @options[:output_dir] += '.exe' unless File.extname(@options[:output_dir]) == '.exe'
160
+ @options[:output_dir]
161
+ end
162
+ ocra_options = [
163
+ '--gem-full',
164
+ '--add-all-core',
165
+ '--no-autoload',
166
+ '--no-dep-run',
167
+ '--no-enc',
168
+ '--console'
169
+ ]
170
+ #ocra_options << '--quiet' unless Spread2RDF.debug_mode
171
+ ocra_options << '--debug' if Spread2RDF.debug_mode
172
+ ocra_gemfile = File.join(Spread2RDF::ROOT, 'Gemfile.ocra')
173
+ ocra_options << "--gemfile #{ocra_gemfile}"
174
+ ocra_options << "--output #{output}"
175
+ ocra_cmd = "ocra #{ocra_options.join(' ')} #{mapping_file}"
176
+ puts "compiling #{mapping_file} to #{output}"
177
+ #puts ocra_cmd
178
+ Kernel.system(ocra_cmd)
88
179
  end
180
+
89
181
  self
90
182
  end
91
- end
183
+
184
+ CLI = Cli.instance
185
+ end
@@ -0,0 +1,49 @@
1
+ module Spread2RDF
2
+ class Coord < Struct.new(:column, :row)
3
+ def initialize(*args)
4
+ case args.length
5
+ when 2 then super
6
+ when 1
7
+ case args = args.first
8
+ when Hash
9
+ super(args[:column], args[:row])
10
+ when Symbol, String
11
+ coord = args.to_s
12
+ raise "Invalid cell coordinates #{coord}" unless coord =~ /(\w+)(\d+)/
13
+ super(Regexp.last_match[1], Regexp.last_match[2].to_i)
14
+ else raise ArgumentError, "can't handle argument #{args}"
15
+ end
16
+ else raise ArgumentError, "too many arguments: #{args}"
17
+ end
18
+ end
19
+
20
+ def column_as_number
21
+ Roo::Base.letter_to_number(column)
22
+ end
23
+
24
+ def column_as_index
25
+ column_as_number - 1
26
+ end
27
+
28
+ def increment_column(count = 1)
29
+ self.class.increment_column(self.column, count)
30
+ end
31
+
32
+ def to_s
33
+ "#{column}#{row}"
34
+ end
35
+
36
+ def to_sym
37
+ to_s.to_sym
38
+ end
39
+
40
+ class << self
41
+ alias [] new
42
+
43
+ def increment_column(column, count=1)
44
+ Roo::Base.number_to_letter(Roo::Base.letter_to_number(column) + count)
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,105 @@
1
+ module Spread2RDF
2
+ module Mapping
3
+ class Cell < Element
4
+
5
+ attr_reader :row
6
+
7
+ def_delegators :parent, :subject, :worksheet, :resource
8
+ def_delegators :schema, :predicate
9
+
10
+ def initialize(sheet, parent, row)
11
+ super(sheet, parent)
12
+ @row = row
13
+ map
14
+ end
15
+
16
+ def map
17
+ exec(&schema.block) unless empty?
18
+ end
19
+
20
+ def coord
21
+ Coord[column: schema.coord, row: row]
22
+ end
23
+
24
+ def value
25
+ @value ||= worksheet.cell_value(coord)
26
+ end
27
+
28
+ def object
29
+ @object ||= value && map_to_object(value)
30
+ end
31
+
32
+ def empty?
33
+ value.blank?
34
+ end
35
+
36
+ def map_to_object(value)
37
+ case schema.object_mapping_mode
38
+ when :to_string then value
39
+ when :resource_ref then resolve_resource_ref
40
+ when :new_resource then create_resource_object
41
+ when :custom then exec(&schema.cell_mapping)
42
+ else raise 'internal error: unknown column mapping type'
43
+ end
44
+ end
45
+ private :map_to_object
46
+
47
+ def resolve_resource_ref
48
+ source = schema.object[:from]
49
+ source = { worksheet: source } if source.is_a? Symbol
50
+ raise ArgumentError, "expecting a Hash as source, but got #{source}" unless source.is_a? Hash
51
+ source_worksheet = source[:worksheet]
52
+ source_worksheet = spreadsheet.worksheet(source_worksheet)
53
+ raise "#{self}: couldn't find source worksheet #{source[:worksheet]}" if source_worksheet.nil?
54
+ source_predicate = source[:predicate] || RDF::RDFS.label
55
+ result = source_worksheet.graph.query([nil, source_predicate, value])
56
+ raise "#{self}: couldn't find a resource for #{value} in #{source_worksheet}" if result.empty?
57
+ raise "#{self}: found multiple resources for #{value} in #{source_worksheet}: #{result.map(&:subject)}" if result.count > 1
58
+ result.first.subject
59
+ end
60
+ private :resolve_resource_ref
61
+
62
+ def create_resource_object
63
+ case
64
+ when (schema.object.try(:fetch, :uri, nil) || object) == :bnode
65
+ RDF::Node.new
66
+ else
67
+ raise NotImplementedError
68
+ end
69
+ end
70
+ private :create_resource_object
71
+
72
+ ##########################################################################
73
+ # for the DSL for column statement blocks
74
+
75
+ def value_of_column(name)
76
+ sheet_schema = parent.parent.schema
77
+ other_column = sheet_schema.column(name)
78
+ raise "couldn't find column #{name} when mapping #{self}" if
79
+ other_column.nil?
80
+ worksheet.cell_value(column: other_column.coord, row: row)
81
+ end
82
+
83
+ def object_of_column(name)
84
+ other_column = resource.column!(name)
85
+ raise "couldn't find column #{name} when mapping #{self}" if
86
+ other_column.nil?
87
+ other_column.cell!(row).object
88
+ end
89
+
90
+ def exec(&block)
91
+ #puts "executing block of #{@___column___} in row #{row}"
92
+ self.instance_exec(value, &block) if block_given?
93
+ end
94
+ private :exec
95
+
96
+ ##########################################################################
97
+ # Element#_children_
98
+
99
+ def _children_
100
+ nil
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,63 @@
1
+ module Spread2RDF
2
+ module Mapping
3
+ class Column < Element
4
+ include Statement
5
+
6
+ def_delegators :parent, :subject, :worksheet, :row_range
7
+ def_delegators :schema, :predicate
8
+
9
+ alias resource parent
10
+
11
+ def initialize(sheet, parent)
12
+ super
13
+ @cells = {}
14
+ map(row_range)
15
+ end
16
+
17
+ def map(range)
18
+ #puts "mapping #{self} in #{range} ..."
19
+ case range
20
+ when Integer
21
+ cell = cell!(range)
22
+ statements_to_object(cell.object) unless cell.empty?
23
+ when Range
24
+ range.each { |row| self.map(row) }
25
+ else raise ArgumentError
26
+ end
27
+ end
28
+
29
+ def objects
30
+ cells.map(&:object)
31
+ end
32
+
33
+ def cell_coord(row)
34
+ case row
35
+ when Integer then Coord[column: schema.coord, row: row]
36
+ when Coord then Coord
37
+ when Hash then Coord[row]
38
+ else raise ArgumentError
39
+ end
40
+ end
41
+
42
+ ##########################################################################
43
+ # Mapping::Element structure
44
+
45
+ def cells
46
+ @cells.values
47
+ end
48
+ alias _children_ cells
49
+
50
+ def cell(coord)
51
+ coord = cell_coord(coord)
52
+ @cells[coord.to_sym] # TODO: search @sub_sheet_mappings also
53
+ end
54
+
55
+ def cell!(coord)
56
+ coord = cell_coord(coord)
57
+ @cells[coord.to_sym] ||= Cell.new(schema, self, coord.row)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+
@@ -0,0 +1,23 @@
1
+ module Spread2RDF
2
+ module Mapping
3
+ class ColumnBlock < Sheet
4
+ include Statement
5
+
6
+ def_delegators :parent, :subject, :row_range
7
+ def_delegators :schema, :predicate
8
+
9
+ def map
10
+ super
11
+ @resources.each do |resource|
12
+ statements_to_object(resource.subject) unless resource.empty?
13
+ end
14
+ self
15
+ end
16
+
17
+ def objects
18
+ @resources.map(&:subject)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ module Spread2RDF
2
+ module Mapping
3
+ class Cell
4
+ module Default
5
+ def self.unit_mapping(value)
6
+ @qudt ||= RDF::Graph.load File.join(ONTOLOGY_DIR, 'unit-v1.1.ttl')
7
+ query = RDF::Query.new(
8
+ unit: { RDF::URI.new('http://qudt.org/schema/qudt#symbol') =>
9
+ RDF::Literal.new(value, datatype: RDF::XSD.string) })
10
+ solutions = query.execute(@qudt)
11
+ raise "unit #{value} is not unique; possible candidates:
12
+ #{solutions.map(&:unit).ai}" if solutions.count > 1
13
+ raise "couldn't find a QUDT unit for unit '#{value}''" if solutions.empty?
14
+ solutions.first.unit
15
+ end
16
+
17
+ def self.uri_normalization(string)
18
+ string
19
+ .gsub(', ', '-')
20
+ .gsub(' ', '-')
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,64 @@
1
+ require 'forwardable'
2
+
3
+ module Spread2RDF
4
+ module Mapping
5
+ class Element
6
+ extend Forwardable
7
+
8
+ attr_reader :schema
9
+ attr_reader :parent
10
+
11
+ def_delegators :parent, :spreadsheet
12
+
13
+ def initialize(schema, parent)
14
+ @graph = RDF::Repository.new
15
+ @schema = schema
16
+ @parent = parent
17
+ end
18
+
19
+ def to_s
20
+ "#{self.class.name.split('::').last}-mapping of #{schema}"
21
+ end
22
+
23
+ ##########################################################################
24
+ # children
25
+
26
+ def _children_
27
+ raise NotImplementedError, 'subclasses must implement this method'
28
+ end
29
+
30
+ def empty?
31
+ _children_.empty? or _children_.all?(&:empty?)
32
+ end
33
+
34
+ ##########################################################################
35
+ # RDF graph
36
+
37
+ def graph
38
+ if _children_
39
+ _children_.inject(@graph.clone) { |graph, child| graph << child.graph }
40
+ else
41
+ @graph
42
+ end
43
+ end
44
+ alias to_rdf graph
45
+
46
+ private
47
+
48
+ def add_statement(*args)
49
+ args = args.first if args.count == 1 and args.first.is_a? Array
50
+ #puts "adding statement: #{args.inspect}"
51
+ raise "internal error: trying to add a bad triple with nil value: #{args}" if args.count != 3 or args.one? { |arg| arg.nil? }
52
+ @graph << RDF::Statement.new(*args)
53
+ end
54
+ alias statement add_statement
55
+
56
+ def add_statements(*args)
57
+ args = args.first if args.count == 1 and args.first.is_a? Array
58
+ args.each { |arg| statement(arg) }
59
+ end
60
+ alias statements add_statements
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,95 @@
1
+ module Spread2RDF
2
+ module Mapping
3
+ class Resource < Element
4
+
5
+ attr_reader :row_range
6
+
7
+ def_delegators :parent, :worksheet
8
+
9
+ def initialize(sheet, parent, row_range)
10
+ super(sheet, parent)
11
+ @columns = {}
12
+ @row_range = row_range
13
+ map
14
+ end
15
+
16
+ def map
17
+ #puts "processing #{self} in #{row_range}"
18
+ object_columns = parent.schema.columns - [parent.schema.subject_column]
19
+ object_columns.each { |column| column!(column) }
20
+ subject_description unless empty?
21
+ self
22
+ end
23
+
24
+ ##########################################################################
25
+ # subject mapping
26
+
27
+ def subject
28
+ @subject ||=
29
+ case schema.subject_mapping_mode
30
+ when :bnode then RDF::Node.new
31
+ when :from_column then subject_resource_from_column
32
+ else raise 'unknown subject mapping type'
33
+ end
34
+ end
35
+ alias subject_resource subject
36
+
37
+ def subject_name_suffix
38
+ cells = row_range.map do |row|
39
+ parent.cell_value(row: row, column: schema.subject_column.coord).presence
40
+ end.compact
41
+ raise "no subject found for Resource in #{row_range} of #{sheet}" if cells.empty?
42
+ raise "multiple subjects found for Resource in #{row_range} of #{sheet}: #{cells.inspect}" if cells.count > 1
43
+ cells.first
44
+ end
45
+ private :subject_name_suffix
46
+
47
+ def subject_resource_from_column
48
+ namespace = schema.subject_namespace
49
+ subject_suffix = Mapping::Cell::Default.uri_normalization(subject_name_suffix)
50
+ #puts "subject resource for #{sheet} in #{range}: " + RDF::URI.new("#{namespace}#{subject_suffix}" )
51
+ RDF::URI.new("#{namespace}#{subject_suffix}")
52
+ end
53
+ private :subject_resource_from_column
54
+
55
+ def subject_description
56
+ type = schema.subject_resource_type
57
+ statement(subject, RDF.type, type) unless type.nil?
58
+ if type == RDF::RDFS.Class &&
59
+ super_class = schema.subject.try(:fetch, :sub_class_of, nil)
60
+ statement(subject, RDF::RDFS.subClassOf, super_class)
61
+ end
62
+ end
63
+ private :subject_description
64
+
65
+ ##########################################################################
66
+ # Mapping::Element structure
67
+
68
+ def columns
69
+ @columns.values
70
+ end
71
+ alias _children_ columns
72
+
73
+ def column(name)
74
+ @columns[column_schema(name).name]
75
+ end
76
+
77
+ def column!(name)
78
+ column_schema = column_schema(name)
79
+ @columns[column_schema.name] ||= case column_schema
80
+ when Schema::Column then Column.new(column_schema, self)
81
+ when Schema::ColumnBlock then ColumnBlock.new(column_schema, self).map
82
+ end
83
+ end
84
+
85
+ def column_schema(name)
86
+ case name
87
+ when Schema::Column, Schema::ColumnBlock then name
88
+ when String, Symbol then schema.column(name)
89
+ else raise ArgumentError
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end