talia_core 0.5.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/VERSION.yml +2 -2
  2. data/config/talia_core.yml.example +37 -35
  3. data/generators/talia_admin/templates/app/models/fake_source.rb +93 -0
  4. data/generators/talia_admin/templates/app/models/talia_collection.rb +13 -37
  5. data/generators/talia_base/talia_base_generator.rb +0 -1
  6. data/generators/talia_base/templates/app/controllers/custom_templates_controller.rb +2 -1
  7. data/generators/talia_base/templates/app/controllers/sources_controller.rb +1 -1
  8. data/generators/talia_base/templates/script/configure_talia +56 -73
  9. data/generators/talia_swicky/talia_swicky_generator.rb +18 -0
  10. data/generators/talia_swicky/templates/app/controllers/swicky_notebooks_controller.rb +111 -0
  11. data/generators/talia_swicky/templates/app/helpers/swicky_notebooks_helper.rb +29 -0
  12. data/generators/talia_swicky/templates/app/views/swicky_notebooks/index.builder +6 -0
  13. data/generators/talia_swicky/templates/app/views/swicky_notebooks/index.html.erb +10 -0
  14. data/generators/talia_swicky/templates/app/views/swicky_notebooks/show.html.erb +11 -0
  15. data/generators/talia_swicky/templates/test/fixtures/notebook.rdf +862 -0
  16. data/generators/talia_swicky/templates/test/functional/swicky_notebooks_controller_test.rb +44 -0
  17. data/lib/core_ext/boolean.rb +23 -0
  18. data/lib/core_ext/jdbc_rake_monkeypatch.rb +22 -0
  19. data/lib/core_ext/nil_class.rb +11 -0
  20. data/lib/core_ext/object.rb +34 -0
  21. data/lib/core_ext/string.rb +15 -0
  22. data/lib/custom_template.rb +3 -1
  23. data/lib/loader_helper.rb +16 -3
  24. data/lib/mysql.rb +7 -7
  25. data/lib/progressbar.rb +2 -2
  26. data/lib/swicky/exhibit_json/item.rb +129 -0
  27. data/lib/swicky/exhibit_json/item_collection.rb +129 -0
  28. data/lib/swicky/fragment.rb +0 -0
  29. data/lib/swicky/note.rb +7 -0
  30. data/lib/swicky/notebook.rb +78 -12
  31. data/lib/talia_core/active_source.rb +45 -13
  32. data/lib/talia_core/active_source_parts/class_methods.rb +154 -26
  33. data/lib/talia_core/active_source_parts/finders.rb +49 -26
  34. data/lib/talia_core/active_source_parts/predicate_handler.rb +71 -23
  35. data/lib/talia_core/active_source_parts/rdf/ntriples_reader.rb +13 -0
  36. data/lib/talia_core/active_source_parts/rdf/rdf_reader.rb +99 -0
  37. data/lib/talia_core/active_source_parts/rdf/rdfxml_reader.rb +12 -0
  38. data/lib/talia_core/active_source_parts/{rdf.rb → rdf_handler.rb} +52 -19
  39. data/lib/talia_core/active_source_parts/xml/generic_reader.rb +151 -260
  40. data/lib/talia_core/active_source_parts/xml/generic_reader_add_statements.rb +97 -0
  41. data/lib/talia_core/active_source_parts/xml/generic_reader_helpers.rb +88 -0
  42. data/lib/talia_core/active_source_parts/xml/generic_reader_import_statements.rb +239 -0
  43. data/lib/talia_core/active_source_parts/xml/rdf_builder.rb +14 -7
  44. data/lib/talia_core/active_source_parts/xml/source_builder.rb +7 -3
  45. data/lib/talia_core/active_source_parts/xml/source_reader.rb +17 -2
  46. data/lib/talia_core/collection.rb +192 -1
  47. data/lib/talia_core/data_types/data_loader.rb +88 -18
  48. data/lib/talia_core/data_types/data_record.rb +24 -2
  49. data/lib/talia_core/data_types/delayed_copier.rb +13 -3
  50. data/lib/talia_core/data_types/file_record.rb +24 -13
  51. data/lib/talia_core/data_types/file_store.rb +111 -94
  52. data/lib/talia_core/data_types/iip_data.rb +104 -23
  53. data/lib/talia_core/data_types/iip_loader.rb +102 -56
  54. data/lib/talia_core/data_types/image_data.rb +3 -1
  55. data/lib/talia_core/data_types/media_link.rb +4 -1
  56. data/lib/talia_core/data_types/mime_mapping.rb +65 -38
  57. data/lib/talia_core/data_types/path_helpers.rb +23 -17
  58. data/lib/talia_core/data_types/pdf_data.rb +9 -6
  59. data/lib/talia_core/data_types/simple_text.rb +5 -4
  60. data/lib/talia_core/data_types/xml_data.rb +53 -25
  61. data/lib/talia_core/dummy_handler.rb +3 -2
  62. data/lib/talia_core/errors.rb +13 -27
  63. data/lib/talia_core/initializer.rb +44 -4
  64. data/lib/talia_core/oai/active_source_model.rb +13 -6
  65. data/lib/talia_core/oai/active_source_oai_adapter.rb +13 -12
  66. data/lib/talia_core/rdf_import.rb +1 -1
  67. data/lib/talia_core/rdf_resource.rb +2 -1
  68. data/lib/talia_core/semantic_collection_wrapper.rb +143 -151
  69. data/lib/talia_core/semantic_property.rb +4 -0
  70. data/lib/talia_core/semantic_relation.rb +84 -33
  71. data/lib/talia_core/source.rb +45 -25
  72. data/lib/talia_core/source_fragment.rb +7 -0
  73. data/lib/talia_core/source_transfer_object.rb +3 -1
  74. data/lib/talia_core/source_types/agent.rb +16 -0
  75. data/lib/talia_core/source_types/dc_resource.rb +3 -3
  76. data/lib/talia_core/source_types/marcont_resource.rb +15 -0
  77. data/lib/talia_core/source_types/skos_concept.rb +17 -0
  78. data/lib/talia_dependencies.rb +1 -1
  79. data/lib/talia_util.rb +1 -1
  80. data/lib/talia_util/bar_progressor.rb +1 -1
  81. data/lib/talia_util/image_conversions.rb +8 -2
  82. data/lib/talia_util/import_job_helper.rb +40 -3
  83. data/lib/talia_util/io_helper.rb +15 -4
  84. data/lib/talia_util/progressable.rb +50 -1
  85. data/lib/talia_util/rake_tasks.rb +3 -21
  86. data/lib/talia_util/test_helpers.rb +6 -1
  87. data/lib/talia_util/util.rb +108 -27
  88. data/lib/talia_util/xml/base_builder.rb +28 -1
  89. data/lib/talia_util/xml/rdf_builder.rb +81 -5
  90. data/lib/tasks/talia_core_tasks.rake +2 -0
  91. data/test/core_ext/boolean_test.rb +26 -0
  92. data/test/core_ext/nil_class_test.rb +14 -0
  93. data/test/core_ext/object_test.rb +26 -0
  94. data/test/core_ext/string_test.rb +11 -0
  95. data/test/swicky/json_encoder_test.rb +51 -42
  96. data/test/swicky/notebook_test.rb +13 -6
  97. data/test/talia_core/active_source_finder_interface_test.rb +30 -0
  98. data/test/talia_core/active_source_test.rb +445 -34
  99. data/test/talia_core/collection_test.rb +332 -0
  100. data/test/talia_core/data_types/file_record_test.rb +2 -23
  101. data/test/talia_core/ntriples_reader_test.rb +49 -0
  102. data/test/talia_core/rdfxml_reader_test.rb +51 -0
  103. data/test/talia_core/source_test.rb +12 -0
  104. data/test/talia_util/import_job_helper_test.rb +19 -12
  105. metadata +190 -90
  106. data/config/database.yml +0 -19
  107. data/config/rdfstore.yml +0 -13
  108. data/config/talia_core.yml +0 -24
  109. data/generators/talia_base/templates/migrations/bj_migration.rb +0 -10
  110. data/lib/JXslt/jxslt.rb +0 -60
  111. data/lib/swicky/json_encoder.rb +0 -179
  112. data/lib/talia_core/agent.rb +0 -14
  113. data/lib/talia_core/background_jobs/job.rb +0 -82
  114. data/lib/talia_core/background_jobs/progress_job.rb +0 -68
  115. data/lib/talia_core/data_types/temp_file_handling.rb +0 -85
  116. data/lib/talia_core/ordered_source.rb +0 -228
  117. data/lib/talia_core/semantic_collection_item.rb +0 -94
  118. data/lib/talia_core/source_types/collection.rb +0 -15
  119. data/lib/talia_util/progressbar.rb +0 -236
  120. data/tasks/talia_core_tasks.rake +0 -2
  121. data/test/talia_core/ordered_source_test.rb +0 -394
  122. data/test/talia_core/semantic_collection_item_test.rb +0 -125
@@ -0,0 +1,44 @@
1
+ require 'test_helper'
2
+
3
+ class SwickyNotebooksControllerTest < ActionController::TestCase
4
+
5
+ def setup
6
+ @notebook = Swicky::Notebook.new('dan', 'booky')
7
+ @notebook_url = (N::LOCAL + 'users/dan/swicky_notebooks/bookyurl').to_s
8
+ @testpointer = "http://dbin.org/swickynotes/demo/HanselAndGretel.htm#xpointer(start-point(string-range(//BODY/DIV[1]/TABLE[1]/TBODY[1]/TR[1]/TD[2]/P[46]/SPAN[1]/SPAN[1]/text()[1],'',7))/range-to(string-range(//BODY/DIV[1]/TABLE[1]/TBODY[1]/TR[1]/TD[2]/P[47]/SPAN[1]/text()[1],'',189)))"
9
+ end
10
+
11
+ def teardown
12
+ @notebook.delete
13
+ end
14
+
15
+ def test_index_empty
16
+ get(:index, :user_name => 'admin')
17
+ assert_response(:success)
18
+ assert_tag :ul, :children => { :count => 0 }
19
+ end
20
+
21
+ def test_index
22
+ load_notebook
23
+ get(:index, :user_name => 'admin')
24
+ assert_response(:success)
25
+ assert_select('ul') { assert_select 'li', @notebook_url }
26
+ end
27
+
28
+ def test_index_xml
29
+ load_notebook
30
+ get(:index, { :user_name => 'admin' }, :headers => { :accept => 'application/xml' })
31
+ assert_select('notebooks') { assert_select 'notebook', @notebook_url }
32
+ end
33
+
34
+ def test_user_missing
35
+ assert_raises(ActiveRecord::RecordNotFound) { get(:index, :user_name => 'foo') }
36
+ end
37
+
38
+ private
39
+
40
+ def load_notebook
41
+ @notebook.load(File.join(ActiveSupport::TestCase.fixture_path, "notebook.rdf"))
42
+ end
43
+
44
+ end
@@ -0,0 +1,23 @@
1
+ class TrueClass
2
+
3
+ # See String#yes? from the core extensions. Always true here.
4
+ def yes? ; true ; end
5
+ alias_method :true?, :yes?
6
+
7
+ # See String#no? from the core extensions. Always false here.
8
+ def no? ; false ; end
9
+ alias_method :false?, :no?
10
+
11
+ end
12
+
13
+ class FalseClass
14
+
15
+ # See String#yes? from the core extensions. Always false here.
16
+ def yes? ; false ; end
17
+ alias_method :true?, :yes?
18
+
19
+ # See String#no? from the core extensions. Always true here.
20
+ def no? ; true ; end
21
+ alias_method :false?, :no?
22
+
23
+ end
@@ -0,0 +1,22 @@
1
+ # Patches a problem that prevents the default rake unit test task working nicely with the
2
+ # JDBC adapter. Note that for this to work correctly, the database type must be set
3
+ # to "mysql" instead of "jdbcmysql"
4
+
5
+ if((RUBY_PLATFORM =~ /java/) && defined?(ActiveRecord::ConnectionAdapters::JdbcAdapter))
6
+
7
+ require 'jdbc_adapter/jdbc_mysql'
8
+
9
+ assit(defined?(JdbcSpec::MySQL))
10
+
11
+ module JdbcSpec # :nodoc:
12
+ module MySQL # :nodoc:
13
+
14
+ alias_method :real_recreate_database, :recreate_database
15
+ def recreate_database(name, dummy = nil) # :nodoc:
16
+ real_recreate_database(name)
17
+ end
18
+
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,11 @@
1
+ class NilClass
2
+
3
+ # See String#yes? from the core extensions. Always false here
4
+ def yes? ; false ; end
5
+ alias_method :true?, :yes?
6
+
7
+ # See String#no? from the core extensions. Always true here
8
+ def no? ; true ; end
9
+ alias_method :false?, :no?
10
+
11
+ end
@@ -0,0 +1,34 @@
1
+ class Object
2
+
3
+
4
+ class TryObject
5
+
6
+ def initialize(original_object)
7
+ @original_object = original_object
8
+
9
+ end
10
+
11
+ def method_missing(method, *args, &block)
12
+ @original_object.send(method, *args, &block) if(@original_object.respond_to? method)
13
+ end
14
+
15
+ # Hacky, Hacky, make all public private (we always want to call on @original_object)
16
+ # new and alloc etc will automatically be ignored as they raise an exception
17
+ public_instance_methods.each do |pub_method|
18
+ begin ; private pub_method ; rescue NameError ; end
19
+ end
20
+
21
+ end
22
+
23
+
24
+ # Tries to call the given method if it exists. If the method doesn't
25
+ # exist, it will just return nil
26
+ def try_call(*args, &block)
27
+ if args.empty?
28
+ TryObject.new(self)
29
+ else
30
+ self.send(*args, &block) if(self.respond_to?(args.first))
31
+ end
32
+ end
33
+
34
+ end
@@ -9,4 +9,19 @@ class String
9
9
  N::URI.new(self)
10
10
  end
11
11
 
12
+ # Returns true if the string is "yes" or "true", regardless
13
+ # of capitalization and leading/trailing spaces
14
+ def yes?
15
+ me = self.downcase.strip
16
+ me == 'yes' || me == 'true'
17
+ end
18
+ alias_method :true?, :yes?
19
+
20
+ # Like #yes?, just checking for "no" or "false"
21
+ def no?
22
+ me = self.downcase.strip
23
+ me == 'no' || me == 'false'
24
+ end
25
+ alias_method :false?, :no?
26
+
12
27
  end
@@ -1,4 +1,6 @@
1
- class CustomTemplate < ActiveRecord::Base
1
+ # Class for custom templates (e.g. css and xslt). Mainly created for customization in the
2
+ # Discovery project. May need a major rework prior of being generally useful.
3
+ class CustomTemplate < ActiveRecord::Base # :nodoc:
2
4
  hobo_model # Don't put anything above this
3
5
 
4
6
  validates_presence_of :name, :content
@@ -42,7 +42,13 @@ module TLoad
42
42
  require_module("actionpack", "action_controller", "/../../../rails/actionpack", RAILS_GEM_VERSION)
43
43
  # This sets the automatic loader path for Talia, allowing the ActiveSupport
44
44
  # classes to automatically load classes from this directory.
45
- load_paths = ActiveSupport::Dependencies.load_paths
45
+ # From 2.3.9(?) onward a new method autoload_path is "suggested" but 2.3.8 does not know it and gives error.
46
+ # This is to avoid the error:
47
+ begin
48
+ load_paths = ActiveSupport::Dependencies.autoload_paths
49
+ rescue NoMethodError
50
+ load_paths = ActiveSupport::Dependencies.load_paths
51
+ end
46
52
  load_paths << TLoad.start_dir unless(load_paths.include?(TLoad.start_dir))
47
53
  # Add the other plugins to the path, if we have a Rails root
48
54
  if(defined?(RAILS_ROOT))
@@ -58,7 +64,14 @@ module TLoad
58
64
  root_dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
59
65
  Kernel.const_set(:TALIA_CODE_ROOT, root_dir) unless(defined?(TALIA_CODE_ROOT))
60
66
  lib_dir = File.join(root_dir, 'lib')
61
- ActiveSupport::Dependencies.load_paths << lib_dir unless(ActiveSupport::Dependencies.load_paths.include?(lib_dir))
67
+ # From 2.3.9(?) onward a new method autoload_path is "suggested" but 2.3.8 does not know it and gives error.
68
+ # This is to avoid the error:
69
+ begin
70
+ load_paths = ActiveSupport::Dependencies.autoload_paths
71
+ rescue NoMethodError
72
+ load_paths = ActiveSupport::Dependencies.load_paths
73
+ end
74
+ load_paths << lib_dir unless(load_paths.include?(lib_dir))
62
75
  end
63
76
 
64
77
  private
@@ -75,4 +88,4 @@ module TLoad
75
88
 
76
89
  end
77
90
 
78
- TLoad.start_dir # Load the paths and start directory
91
+ TLoad.start_dir # Load the paths and start directory
@@ -4,7 +4,7 @@
4
4
  # tommy@tmtm.org
5
5
  #
6
6
 
7
- class Mysql
7
+ class Mysql # :nodoc:
8
8
 
9
9
  VERSION = "4.0-ruby-0.2.6-plus-changes"
10
10
 
@@ -576,7 +576,7 @@ class Mysql
576
576
  raise Error::new(@errno, @error)
577
577
  end
578
578
 
579
- class Result
579
+ class Result # :nodoc:
580
580
  def initialize(mysql, fields, field_count, data=nil)
581
581
  @handle = mysql
582
582
  @fields = fields
@@ -691,7 +691,7 @@ class Mysql
691
691
 
692
692
  end
693
693
 
694
- class Field
694
+ class Field # :nodoc:
695
695
  # Field type
696
696
  TYPE_DECIMAL = 0
697
697
  TYPE_TINY = 1
@@ -759,7 +759,7 @@ class Mysql
759
759
  end
760
760
  end
761
761
 
762
- class Error < StandardError
762
+ class Error < StandardError # :nodoc:
763
763
  # Server Error
764
764
  ER_HASHCHK = 1000
765
765
  ER_NISAMCHK = 1001
@@ -1076,7 +1076,7 @@ class Mysql
1076
1076
  end
1077
1077
  end
1078
1078
 
1079
- class Net
1079
+ class Net # :nodoc:
1080
1080
  def initialize(sock)
1081
1081
  @sock = sock
1082
1082
  @pkt_nr = 0
@@ -1145,7 +1145,7 @@ class Mysql
1145
1145
 
1146
1146
  end
1147
1147
 
1148
- class Random
1148
+ class Random # :nodoc:
1149
1149
  def initialize(seed1, seed2)
1150
1150
  @max_value = 0x3FFFFFFF
1151
1151
  @seed1 = seed1 % @max_value
@@ -1161,7 +1161,7 @@ class Mysql
1161
1161
 
1162
1162
  end
1163
1163
 
1164
- class << Mysql
1164
+ class << Mysql # :nodoc:
1165
1165
  def init()
1166
1166
  Mysql::new :INIT
1167
1167
  end
@@ -9,7 +9,7 @@
9
9
  # of Ruby's license.
10
10
  #
11
11
 
12
- class ProgressBar
12
+ class ProgressBar # :nodoc:
13
13
  VERSION = "0.9"
14
14
 
15
15
  def initialize (title, total, out = STDERR)
@@ -228,7 +228,7 @@ class ProgressBar
228
228
  end
229
229
  end
230
230
 
231
- class ReversedProgressBar < ProgressBar
231
+ class ReversedProgressBar < ProgressBar # :nodoc:
232
232
  def do_percentage
233
233
  100 - super
234
234
  end
@@ -0,0 +1,129 @@
1
+ require 'digest/md5'
2
+
3
+ module Swicky
4
+ module ExhibitJson
5
+
6
+ # An item in the Exhibit JSON. Each item belongs to a collection.
7
+ class Item
8
+
9
+ attr_reader :element, :collection
10
+
11
+
12
+ def initialize(element, collection)
13
+ raise(ArgumentError, "Element must have an URI, was a #{element.class.name}: #{element.inspect}") unless(element.respond_to?(:uri))
14
+ raise(ArgumentError, "Element must always belong to a collection") unless(collection)
15
+ @original_properties = {}
16
+ @element = element
17
+ @properties = {}
18
+ @collection = collection
19
+ create_label!
20
+ @properties['uri'] = self.uri
21
+ @properties['id'] = @collection.make_id(self)
22
+ @properties['hash'] = ('h_' << Digest::MD5.hexdigest(self.uri))
23
+ end
24
+
25
+ def uri
26
+ @element.uri.to_s
27
+ end
28
+
29
+ def id
30
+ @properties['id']
31
+ end
32
+
33
+ def label
34
+ @properties['label']
35
+ end
36
+
37
+ def [](key)
38
+ @properties[key]
39
+ end
40
+
41
+ def []=(key, value)
42
+ @properties[key] = value
43
+ end
44
+
45
+ def add_properties(properties)
46
+ @original_properties = properties
47
+ create_label!
48
+ create_types!
49
+ create_properties!
50
+ end
51
+
52
+ def to_json(*a)
53
+ @properties.to_json(*a)
54
+ end
55
+
56
+ private
57
+
58
+ def create_label!
59
+ label = delete_with_key(N::RDFS.label) # Get the "real" label from the RDFS
60
+ if(@properties['label'].blank?)
61
+ # In this case we have no existing label on the item,
62
+ # so if we don't have the RDFS label we create one from the URI
63
+ label = label.blank? ? label_for(self.uri) : label.first
64
+ @properties['label'] = label
65
+ else
66
+ # We already have a label on the item, which we only
67
+ # overwrite in case there is a "real" label
68
+ @properties['label'] = label.first unless(label.blank?)
69
+ end
70
+ assit_kind_of(String, @properties['label'])
71
+ end
72
+
73
+ def create_types!
74
+ types = delete_with_key(N::RDF.type)
75
+ types = [ N::RDF.Resource ] if(types.blank?)
76
+ types.collect! { |t| collection.add_type(t) }
77
+ types.uniq!
78
+ @properties['type'] = types
79
+ end
80
+
81
+ def create_properties!
82
+ @original_properties.each do |key, values|
83
+ value_type = values.first.respond_to?(:uri) ? 'item' : 'text'
84
+ prop = collection.add_property(key, value_type)
85
+ prop_values = values.collect do |object|
86
+ object.respond_to?(:uri) ? collection.add_item(object) : object
87
+ end
88
+ prop_values.uniq!
89
+ if(@properties[prop].blank?)
90
+ @properties[prop] = (prop_values.size == 1) ? prop_values.first : prop_values
91
+ else
92
+ if(@properties[prop].is_a?(Array))
93
+ @properties[prop] += prop_values
94
+ else
95
+ @properties[prop] = (prop_values << @properties[prop])
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ # Little kludge for getting badly-specified keys
102
+ def delete_with_key(key)
103
+ real_key = @original_properties.keys.find { |k| k.to_s == key.to_s }
104
+ @original_properties.delete(real_key)
105
+ end
106
+
107
+ # Get the part of the the uri that can be used as a label
108
+ def label_for(uri)
109
+ if(uri =~ /xpointer/)
110
+ 'An Xpointer'
111
+ else
112
+ uri_fragment(uri)
113
+ end
114
+ end
115
+
116
+ def uri_fragment(uri)
117
+ raise(ArgumentError, "Must have a real uri") unless(uri)
118
+ fragment_match = /[\/#]?([^\/#]+)\Z/.match(uri.to_s)
119
+ fragment_match ||= /([^\.\/]+)\.[^\.]*\Z/.match(uri.to_s)
120
+ if(fragment_match && fragment_match[1] && (fragment_match[1].size > 2))
121
+ URI.escape(fragment_match[1])
122
+ else
123
+ Digest::MD5.hexdigest(uri.to_s)
124
+ end
125
+ end
126
+
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,129 @@
1
+ require 'digest/md5'
2
+
3
+ module Swicky
4
+ module ExhibitJson
5
+
6
+ # Takes a number of triples and encodes them in the SIMILE JSON format.
7
+ # See http://simile.mit.edu/wiki/Exhibit/Understanding_Exhibit_Database
8
+ class ItemCollection
9
+
10
+ # Intialize with the given triples. The original xpointer will (optionally)
11
+ # be passed into the resulting JSON for reference
12
+ def initialize(triples, original_xpointer = nil)
13
+ @triples = triples
14
+ @original_xpointer = original_xpointer
15
+ triple_hash.each { |object, values| add_item(object, values) }
16
+ end
17
+
18
+ # Returns the Exhibit-compatible JSON representation
19
+ def to_json(*a)
20
+ my_types = {}
21
+ types.values.each { |t| my_types[t.id] = t }
22
+ my_properties = {}
23
+ properties.values.each { |p| my_properties[p.id] = p }
24
+ result = {
25
+ 'items' => items.values,
26
+ 'types' => my_types,
27
+ 'properties' => my_properties
28
+ }
29
+ if(@original_xpointer)
30
+ result['annotation-for'] = {
31
+ 'uri' => @original_xpointer,
32
+ 'hash' => ('h_' << Digest::MD5.hexdigest(@original_xpointer))
33
+ }
34
+ end
35
+ result.to_json(*a)
36
+ end
37
+
38
+ # Add a new item to the collection. This passes in a resource-like
39
+ # object and returns the id of the resulting item
40
+ def add_item(item, values = nil)
41
+ items[item.to_s] = Item.new(item, self) if(!items[item.to_s])
42
+ items[item.to_s].add_properties(values) if(values)
43
+ items[item.to_s].id
44
+ end
45
+
46
+ # As #add_item, but for types
47
+ def add_type(type)
48
+ types[type.to_s] = Item.new(type, self) if(!types[type.to_s])
49
+ types[type.to_s].id
50
+ end
51
+
52
+ # As #add_item, but for properties. This must have a value type
53
+ # which will be added to the resulting property description
54
+ def add_property(prop, value_type)
55
+ properties[prop.to_s] = Item.new(prop, self) if(!properties[prop.to_s])
56
+ if(!properties[prop.to_s]['valueType'])
57
+ properties[prop.to_s]['valueType'] = value_type
58
+ end
59
+ properties[prop.to_s].id
60
+ end
61
+
62
+ # Make a unique id for the given item, based on the label of the item
63
+ def make_id(item)
64
+ if(id_hash_inv[item.uri]) # Already existing id
65
+ id_hash_inv[item.uri]
66
+ else
67
+ first_free(item.label, item.uri)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def triple_hash
74
+ @triple_hash ||= begin
75
+ triple_hash = {}
76
+ # Sort all the triples into a hash, mapping all predicates
77
+ # to a single subject entry and all objects into a single
78
+ # predicate entry for each subject or predicate
79
+ @triples.each do |triple|
80
+ subject = triple.shift.to_uri
81
+ triple_hash[subject] ||= {}
82
+ predicate = triple.shift.to_uri
83
+ triple_hash[subject][predicate] ||= []
84
+ triple_hash[subject][predicate] << triple.first
85
+ end
86
+ triple_hash
87
+ end
88
+ end
89
+
90
+ # Finds the first "free" element in the hash. This checks
91
+ # if hash[initial_value] is empty or equal to "value", if that is not the
92
+ # case if will try "initial_value2", "initial_value3", ... until the
93
+ # condition is fulfilled
94
+ def first_free(initial_key, uri)
95
+ candidate = initial_key
96
+ id_hash[candidate] ||= uri
97
+ count = 1
98
+ while(id_hash[candidate] != uri)
99
+ count += 1
100
+ candidate = "#{initial_key}#{count}"
101
+ id_hash[candidate] ||= uri
102
+ end
103
+ id_hash_inv[uri] ||= candidate
104
+ candidate
105
+ end
106
+
107
+ def types
108
+ @types ||= {}
109
+ end
110
+
111
+ def properties
112
+ @properties ||= {}
113
+ end
114
+
115
+ def id_hash
116
+ @id_hash ||= {}
117
+ end
118
+
119
+ def items
120
+ @items ||= {}
121
+ end
122
+
123
+ def id_hash_inv
124
+ @id_hash_inv ||= {}
125
+ end
126
+
127
+ end
128
+ end
129
+ end