sdl-ng 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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +2 -0
  4. data/.yard_redcarpet_ext +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +13 -0
  7. data/README.md +454 -0
  8. data/Rakefile +16 -0
  9. data/bin/process_service_descriptions +102 -0
  10. data/examples/services/google_drive_for_business.service.rb +50 -0
  11. data/examples/services/salesforce_sales_cloud.service.rb +51 -0
  12. data/examples/translations/en.yml +184 -0
  13. data/examples/vocabulary/base/base.sdl.rb +8 -0
  14. data/examples/vocabulary/base/location.sdl.rb +5 -0
  15. data/examples/vocabulary/crf/characteristics.sdl.rb +25 -0
  16. data/examples/vocabulary/crf/charging.sdl.rb +8 -0
  17. data/examples/vocabulary/crf/compliance.sdl.rb +35 -0
  18. data/examples/vocabulary/crf/delivery.sdl.rb +22 -0
  19. data/examples/vocabulary/crf/dynamics.sdl.rb +6 -0
  20. data/examples/vocabulary/crf/interop.sdl.rb +54 -0
  21. data/examples/vocabulary/crf/optimizing.sdl.rb +15 -0
  22. data/examples/vocabulary/crf/portability.sdl.rb +25 -0
  23. data/examples/vocabulary/crf/protection.sdl.rb +8 -0
  24. data/examples/vocabulary/crf/reliability.sdl.rb +1 -0
  25. data/examples/vocabulary/crf/reputation.sdl.rb +3 -0
  26. data/lib/sdl.rb +17 -0
  27. data/lib/sdl/base.rb +20 -0
  28. data/lib/sdl/base/fact.rb +11 -0
  29. data/lib/sdl/base/property.rb +34 -0
  30. data/lib/sdl/base/service.rb +13 -0
  31. data/lib/sdl/base/service_compendium.rb +130 -0
  32. data/lib/sdl/base/type.rb +66 -0
  33. data/lib/sdl/exporters.rb +9 -0
  34. data/lib/sdl/exporters/exporter.rb +19 -0
  35. data/lib/sdl/exporters/markdown_service_exporter.rb +5 -0
  36. data/lib/sdl/exporters/rdf_exporter.rb +34 -0
  37. data/lib/sdl/exporters/rdf_mapping.rb +48 -0
  38. data/lib/sdl/exporters/schema_exporter.rb +9 -0
  39. data/lib/sdl/exporters/service_exporter.rb +9 -0
  40. data/lib/sdl/exporters/xml_mapping.rb +51 -0
  41. data/lib/sdl/exporters/xml_service_exporter.rb +37 -0
  42. data/lib/sdl/exporters/xsd_schema_exporter.rb +94 -0
  43. data/lib/sdl/receivers.rb +22 -0
  44. data/lib/sdl/receivers/fact_receiver.rb +15 -0
  45. data/lib/sdl/receivers/service_receiver.rb +63 -0
  46. data/lib/sdl/receivers/type_instance_receiver.rb +86 -0
  47. data/lib/sdl/receivers/type_receiver.rb +87 -0
  48. data/lib/sdl/translations/en.yml +9 -0
  49. data/lib/sdl/types.rb +19 -0
  50. data/lib/sdl/types/sdl_datetime.rb +10 -0
  51. data/lib/sdl/types/sdl_default_type.rb +13 -0
  52. data/lib/sdl/types/sdl_description.rb +10 -0
  53. data/lib/sdl/types/sdl_duration.rb +12 -0
  54. data/lib/sdl/types/sdl_number.rb +10 -0
  55. data/lib/sdl/types/sdl_string.rb +10 -0
  56. data/lib/sdl/types/sdl_type.rb +31 -0
  57. data/lib/sdl/types/sdl_url.rb +12 -0
  58. data/lib/sdl/util.rb +4 -0
  59. data/lib/sdl/util/documentation.rb +80 -0
  60. data/lib/sdl/util/nokogiri.rb +28 -0
  61. data/lib/sdl/util/verbs.rb +3 -0
  62. data/lib/sdl/version.rb +3 -0
  63. data/sdl-ng.gemspec +34 -0
  64. data/spec/documentation_spec.rb +64 -0
  65. data/spec/fact_type_instance_definition_spec.rb +188 -0
  66. data/spec/property_definitions_spec.rb +44 -0
  67. data/spec/service_compendium_spec.rb +90 -0
  68. data/spec/service_definition_spec.rb +81 -0
  69. data/spec/shared_test_compendium.rb +65 -0
  70. data/spec/spec_helper.rb +10 -0
  71. metadata +291 -0
@@ -0,0 +1,9 @@
1
+ require_relative 'exporters/xml_mapping'
2
+ require_relative 'exporters/exporter'
3
+ require_relative 'exporters/schema_exporter'
4
+ require_relative 'exporters/service_exporter'
5
+ require_relative 'exporters/xsd_schema_exporter'
6
+ require_relative 'exporters/xml_service_exporter'
7
+ require_relative 'exporters/rdf_mapping'
8
+ require_relative 'exporters/rdf_exporter'
9
+ require_relative 'exporters/markdown_service_exporter'
@@ -0,0 +1,19 @@
1
+ module SDL
2
+ module Exporters
3
+ class Exporter
4
+ attr :compendium
5
+ attr :options
6
+
7
+ def initialize(compendium, options = {})
8
+ @compendium = compendium
9
+ @options = options
10
+ end
11
+
12
+ def export_to_file(path, content)
13
+ File.open(path, 'w') do |f|
14
+ f.write(content)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ class SDL::Exporters::MarkdownServiceExporter < SDL::Exporters::ServiceExporter
2
+ def export_service(service)
3
+ "Not implemented yet"
4
+ end
5
+ end
@@ -0,0 +1,34 @@
1
+ require 'rdf'
2
+ require 'rdf/rdfxml'
3
+
4
+ module SDL
5
+ module Exporters
6
+ class RDFExporter < ServiceExporter
7
+ @@s = RDF::Vocabulary.new('http://www.open-service-compendium.org/')
8
+
9
+ def export_service(service)
10
+ graph = RDF::Graph.new
11
+
12
+ service.facts.each do |fact|
13
+ graph << [RDF::URI.new(service.uri), @@s["has_#{fact.class.local_name.underscore}"], RDF::URI.new(fact.uri)]
14
+
15
+ expand_properties(fact, graph)
16
+ end
17
+
18
+ graph.dump(:rdf)
19
+ end
20
+
21
+ def expand_properties(type_instance, graph)
22
+ type_instance.property_values.each do |property, value|
23
+ [value].flatten.each do |v|
24
+ graph << [RDF::URI.new(type_instance.uri), @@s["#{property.name.underscore}"], v.rdf_object] unless v.nil?
25
+ end
26
+
27
+ if property.type < SDL::Base::Type
28
+ [value].flatten.each do |v| expand_properties(v, graph) end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,48 @@
1
+ require 'rdf'
2
+ require 'active_support/inflector'
3
+
4
+ [String, Fixnum, Nokogiri::XML::Element].each do |klass|
5
+ klass.class_eval do
6
+ def rdf_object
7
+ RDF::Literal.new(self)
8
+ end
9
+ end
10
+ end
11
+
12
+ module SDL
13
+ module Base
14
+ class Fact
15
+ class << self
16
+ def uri
17
+ "http://www.open-service-compendium.org/types/#{@local_name}"
18
+ end
19
+ end
20
+
21
+ def uri
22
+ "#{service.uri}/#{self.class.local_name.underscore}-#{hash}"
23
+ end
24
+ end
25
+
26
+ class Type
27
+ class << self
28
+ def uri
29
+ "http://www.open-service-compendium.org/types/#{@local_name}"
30
+ end
31
+ end
32
+
33
+ def uri
34
+ self.class.uri + '/' + hash.to_s
35
+ end
36
+
37
+ def rdf_object
38
+ RDF::URI.new(uri)
39
+ end
40
+ end
41
+
42
+ class Service
43
+ def uri
44
+ "http://www.open-service-compendium.org/services/#{@symbolic_name}"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,9 @@
1
+ module SDL
2
+ module Exporters
3
+ class SchemaExporter < Exporter
4
+ def export_schema_to_file(path)
5
+ export_to_file path, export_schema
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module SDL
2
+ module Exporters
3
+ class ServiceExporter < Exporter
4
+ def export_service_to_file(service, path)
5
+ export_to_file path, export_service(service)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,51 @@
1
+ module SDL
2
+ module Base
3
+ class Property
4
+ def xsd_element_name
5
+ if multi?
6
+ @name.singularize
7
+ else
8
+ @name
9
+ end
10
+ end
11
+ end
12
+
13
+ class Type
14
+ class << self
15
+ def xsd_element_name
16
+ local_name.camelize(:lower)
17
+ end
18
+
19
+ def xsd_type_name
20
+ local_name
21
+ end
22
+
23
+ def xsd_type_qname
24
+ 'sdl:' + xsd_type_name
25
+ end
26
+ end
27
+ end
28
+
29
+ class Fact
30
+ class << self
31
+ def xsd_type_name
32
+ "#{local_name}Fact"
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module Types
39
+ module SDLType
40
+ module ClassMethods
41
+ def xml_type
42
+ if self < SDL::Base::Type
43
+ wrapped_type.xsd_type_name
44
+ else
45
+ 'ns:string'
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ module SDL
2
+ module Exporters
3
+ class XMLServiceExporter < ServiceExporter
4
+ def export_service(service)
5
+ builder = Nokogiri::XML::Builder.new do |xml|
6
+ xml.service('xmlns' => 'http://www.open-service-compendium.org') do
7
+ service.facts.each do |fact|
8
+ xml.send(fact.class.xsd_element_name + '_') do
9
+ serialize_type_instance fact, xml
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ builder.to_xml
16
+ end
17
+
18
+ def serialize_type_instance(type_instance, xml)
19
+ type_instance.property_values.each do |property, value|
20
+ [value].flatten.each do |v|
21
+ if v.class < SDL::Base::Type
22
+ xml.send(property.xsd_element_name + '_', (!value.is_a?(Array) && value.identifier) ? {'identifier' => value.identifier.to_s} : {}) do
23
+ v.annotations.each do |annotation|
24
+ xml.annotation annotation
25
+ end
26
+ xml.documentation v.documentation if (!value.is_a?(Array) && value.identifier)
27
+ serialize_type_instance(v, xml)
28
+ end
29
+ else
30
+ xml.send(property.xsd_element_name + '_', v)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,94 @@
1
+ require 'active_support/inflector'
2
+ require 'nokogiri'
3
+
4
+ module SDL
5
+ module Exporters
6
+ ##
7
+ # The XSD schema exporter creates an XML Schema Definition for consuming service descriptions using the XML provided
8
+ # by the XMLServiceExporter.
9
+ #
10
+ # The schema consists of the following main components:
11
+ # - The definition of the root node, i.e., a service. The service contains an arbitrary number of elements of
12
+ # service fact types.
13
+ # - The definition of service fact classes and SDL types
14
+ # - The definition of a type base, containing annotations and documentation
15
+ class XSDSchemaExporter < SchemaExporter
16
+ def export_schema
17
+ export_schema_xml.to_xml
18
+ end
19
+
20
+ def export_schema_xml
21
+ Nokogiri::XML::Builder.new do |xml|
22
+ xml['ns'].schema('xmlns' => 'http://www.open-service-compendium.org', 'targetNamespace' => 'http://www.open-service-compendium.org', 'xmlns:ns' => 'http://www.w3.org/2001/XMLSchema', 'elementFormDefault' => 'qualified') do
23
+ xml['ns'].element :name => 'service' do
24
+ document(xml, I18n.t('sdl.xml.service_root'))
25
+ xml['ns'].complexType do
26
+ xml['ns'].choice :maxOccurs => 'unbounded' do
27
+ @compendium.fact_classes.each do |fact_class|
28
+ xml['ns'].element :name => fact_class.xsd_element_name, :type => fact_class.xsd_type_name do
29
+ document(xml, fact_class.documentation)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ xml['ns'].complexType :name => 'SDLTypeBase' do
37
+ document(xml, I18n.t('sdl.xml.typebase'))
38
+ xml['ns'].choice do
39
+ xml['ns'].element :name => 'documentation', :minOccurs => 0, :maxOccurs => 'unbounded', :type => 'ns:string' do
40
+ document(xml, I18n.t('sdl.xml.documentation'))
41
+ end
42
+ xml['ns'].element :name => 'annotation', :minOccurs => 0, :maxOccurs => 'unbounded', :type => 'ns:string' do
43
+ document(xml, I18n.t('sdl.xml.annotation'))
44
+ end
45
+ end
46
+ xml['ns'].attribute :name => 'identifier' do
47
+ document(xml, I18n.t('sdl.xml.identifier'))
48
+ end
49
+ end
50
+
51
+ (@compendium.fact_classes + @compendium.types).each do |fact_class|
52
+ xml['ns'].complexType :name => fact_class.xsd_type_name do
53
+ document(xml, fact_class.documentation)
54
+ xml['ns'].complexContent do
55
+ xml['ns'].extension :base => fact_class.is_sub? ? fact_class.superclass.xsd_type_name : 'SDLTypeBase' do
56
+ unless fact_class.properties.empty?
57
+ xml['ns'].sequence do
58
+ fact_class.properties.each do |property|
59
+ extend_property(property, xml) do
60
+ document(xml, property.documentation)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ # Creates an xml element corresponding to the SDL property
74
+ def extend_property(property, xml)
75
+ if property.multi?
76
+ xml['ns'].element :name => property.name.singularize, :type => property.type.xml_type, :minOccurs => 0, :maxOccurs => 'unbounded' do
77
+ yield
78
+ end
79
+ else
80
+ xml['ns'].element :name => property.name, :type => property.type.xml_type, :minOccurs => 0 do
81
+ yield
82
+ end
83
+ end
84
+ end
85
+
86
+ # Shortcut for adding an XSD documentation annotation
87
+ def document(xml, documentation)
88
+ xml['ns'].annotation do
89
+ xml['ns'].documentation documentation
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'receivers/type_instance_receiver'
2
+ require_relative 'receivers/type_receiver'
3
+ require_relative 'receivers/fact_receiver'
4
+ require_relative 'receivers/service_receiver'
5
+
6
+
7
+ module SDL
8
+ module Receivers
9
+ #
10
+ def self.set_value(type_class, type_instance, *property_values, compendium)
11
+ property_values.zip(type_class.properties(true)).each do |value, property|
12
+ if(value.is_a?(Hash))
13
+ TypeInstanceReceiver.new(type_instance, compendium).send(value.keys.first.to_s, value.values.first)
14
+ else
15
+ raise "Specified value '#{value}' for non-existing property." unless property
16
+
17
+ TypeInstanceReceiver.new(type_instance, compendium).send(property.name, value)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module SDL
2
+ module Receivers
3
+ class FactReceiver < TypeReceiver
4
+ def base_class
5
+ SDL::Base::Fact
6
+ end
7
+
8
+ def register_sdltype(type)
9
+ false
10
+ end
11
+
12
+ alias :subfact :subtype
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,63 @@
1
+ require 'verbs'
2
+
3
+ require_relative '../base/service'
4
+
5
+ module SDL
6
+ module Receivers
7
+ class ServiceReceiver
8
+ include ActiveSupport::Inflector
9
+
10
+ attr :service
11
+ attr :compendium
12
+
13
+ def initialize(sym, compendium)
14
+ @service = SDL::Base::Service.new(sym.to_s)
15
+ @compendium = compendium
16
+
17
+ compendium.fact_classes.each do |fact_class|
18
+ define_singleton_method("is_#{fact_class.local_name.underscore.verb.conjugate(:tense => :past, :person => :third, :plurality => :singular, :aspect => :perfective)}") do |*args, &block|
19
+ add_fact fact_class, *args, &block
20
+ end
21
+
22
+ define_singleton_method("has_#{fact_class.local_name.underscore}") do |*args, &block|
23
+ add_fact fact_class, *args, &block
24
+ end
25
+
26
+ define_singleton_method("#{fact_class.local_name.underscore}") do |*args, &block|
27
+ add_fact fact_class, *args, &block
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+ def add_fact(fact_class, *property_values, &block)
34
+ fact_instance = fact_class.new
35
+ fact_instance.service = @service
36
+
37
+ SDL::Receivers.set_value(fact_class, fact_instance, *property_values, @compendium)
38
+
39
+ if block_given?
40
+ SDL::Receivers::TypeInstanceReceiver.new(fact_instance, @compendium).instance_eval &block
41
+ end
42
+
43
+ @service.facts << fact_instance
44
+ end
45
+
46
+ ##
47
+ # Catches calls to methods named similarily to possible predefined type instances
48
+ def method_missing(name, *args)
49
+ possible_type_instances = @compendium.type_instances.map{|k, v| v[name]}.select{|v| v != nil}
50
+
51
+ unless possible_type_instances.nil? || possible_type_instances.empty?
52
+ if possible_type_instances.length > 1
53
+ raise Exception.new("Multiple possibilities for #{name} in #{caller[0]}")
54
+ else
55
+ possible_type_instances[0]
56
+ end
57
+ else
58
+ raise Exception.new("I do not know what to do with '#{name}' in #{caller[0]}")
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,86 @@
1
+ require 'active_support/inflector'
2
+
3
+ module SDL
4
+ module Receivers
5
+ ##
6
+ # Receiver for setting the properties of Type instances
7
+ class TypeInstanceReceiver
8
+ attr_accessor :instance
9
+
10
+ attr_accessor :compendium
11
+
12
+ ##
13
+ # When initialized for a fact or type instance, the receiver creates singleton methods on itself for all
14
+ # properties.
15
+ def initialize(instance, compendium)
16
+ @instance = instance
17
+ @compendium = compendium
18
+
19
+ instance.class.properties(true).each do |property|
20
+ if property.single?
21
+ # Single valued properties are set by their name
22
+ define_singleton_method property.name do |value = nil, &block|
23
+ if value.is_a? Symbol
24
+ value = compendium.type_instances[property.type][value] || raise("Could not find instance :#{value.to_s} in predefined #{property.type.name} types")
25
+ end
26
+
27
+ instance.send "#{property.name}=", value
28
+ end
29
+ else
30
+ # Multi-valued properties are added to by their singular name
31
+ define_singleton_method property.name.singularize do |*property_values, &block|
32
+ existing_list = instance.send "#{property.name}"
33
+
34
+ unless property_values.empty?
35
+ # If there is just one parameter for a multi-valued property setter
36
+ if property_values.length == 1
37
+ # It could be a symbol, which would resolve to a predefined type instance of the same name
38
+ if property_values[0].is_a?(Symbol)
39
+ predefined_value = compendium.type_instances[property.type][property_values[0]]
40
+
41
+ raise "Could not find instance :#{property_values[0]} in predefined #{property.type.name} types" unless predefined_value
42
+
43
+ existing_list << compendium.type_instances[property.type][property_values[0]]
44
+ # Or better: it could already be an instance of the type - e.g. when using the implemented #method_mssing
45
+ elsif property_values[0].is_a? property.type
46
+ existing_list << property_values[0]
47
+ end
48
+ else
49
+ new_list_item = property.type.new
50
+
51
+ SDL::Receivers.set_value(property.type, new_list_item, *property_values, @compendium)
52
+
53
+ self.class.new(new_list_item, @compendium).instance_exec(&block) unless block.nil?
54
+
55
+ existing_list << new_list_item
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def annotation(value)
64
+ @instance.annotations << value
65
+ end
66
+
67
+ ##
68
+ # Catches calls to methods named similarily to possible predefined type instances
69
+ def method_missing(name, *args)
70
+ # Possible type instances are all types of properties of properties of this instance ...
71
+ possible_type_classes = @instance.class.properties.map(&:type).map(&:properties).flatten.map(&:type).select{|type| type.wrapped_type < SDL::Base::Type}
72
+
73
+ # ... and the types of multi-value instances
74
+ possible_type_classes.concat(@instance.class.properties.find_all{|p| p.multi?}.map(&:type))
75
+
76
+ possible_type_instances = @compendium.type_instances.select{|k, v| possible_type_classes.include?(k)}.map{|k, v| v[name]}.select{|v| v != nil}
77
+
78
+ unless possible_type_instances.nil? || possible_type_instances.empty?
79
+ possible_type_instances[0]
80
+ else
81
+ raise Exception.new("I do not know what to do with '#{name}' in #{caller[0]}")
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end