[More Detail] [Collapse All]
Feature: Snippet loader plugin

Sbuilder defines snippet loader extension point to import TLA+
snippets into sbuilder.

Notice:
It is also possible to use unamed objects, and not use 'objectName'
configuration in 'extend.loaders' configuration. In this case,
'snippets' configuration should uses 'className' property.
features/100-plugin-extension/200-snippet-loader-plugin.feature
- Background: Implement simple plugin
link

  Create Ruby class implementing Sbuilder Snippet loader
  interface. This class defines methods:
  
  - ::configure: set static configuration for the Snippet loader,
  
  - #initiazlize: create new Snippet loader instance
    
  - #doRegisterSnippets: invoke snippet load operation usin
    configuration given in sbuilder.yaml 'snippets' array element.
    Snippets are passed to sbuilder calling method #handOver for
    each snippet to load.

  Ruby class 'SnippetLoaderExample' defines two snippets:

  - service implementation for api inteface '/customer(post)' using
    metatype 'service_implementation'.
  
  - service code for api interfa '/customer(post)' using metatype 'service_code'
features/100-plugin-extension/200-snippet-loader-plugin.feature:12
Given a directory named "lib"
And a file named "lib/snippet_loader_example.rb" with:
module Tst 
class SnippetLoaderExample < Sbuilder::SnippetLoaderPlugin
   # This simple example uses a fixed meta data and TLA code.
   # Real application should parse source language, or extract configuration
   # repository for snippet code and meta data.
   SNIPPETS = [
         {
         'metatype' => 'service_implementation',
         'appName' => '/customer(post)',
         #
         # Snippet code uses 'SPEC_NAME' to resolve name of
         #  - 'service implementation' for '/customer(post)' 
         #  - 'service code' for '/customer(post)' 
         #
         'body' =>  <<-EOS
              macro {{#SPEC_NAME}}service_implementation./customer(post){{/SPEC_NAME}}( input ) {
                 \\* ''Name of 'service code' for '/customer(post)' 
                 call {{#SPEC_NAME}}service_code./customer(post){{/SPEC_NAME}}( input );
              }
          EOS
         },
         {
         'metatype' => 'service_code',
         'appName' => '/customer(post)',
         'body' =>  <<-EOS
              procedure {{#SPEC_NAME}}service_code./customer(post){{/SPEC_NAME}}( input ) {
                 \\* Snippet added in 'SnippetLoaderExample'
                 {{#SPEC_NAME}}service_code./customer(post){{/SPEC_NAME}}_start:
                           print << \"Service called\">>;
                 return;
              }
          EOS
         },
      ]
  #
  # Static configuration applicable for all plugin instances.
  # 
  # NOTICE: @logger is not available for static method ::configure
  def self.configure( configuration )
          @@configuration = configuration
  end
  # create snippet loader instance && must call super
  def initialize( options={} )
     super
  end

  # frawework calls set `snippetFacade` attribute immediately after constructor


  #
  # Override abstract method in parent class for the implementation. This
  # method loads snippets.
  #
  # @param [Hash:Array] snippetDefitions given in sbuilder.yaml
  def doRegisterSnippets( snippetDefitions )

      # SNIPPETS use metatype 'service_code' - must add it before handOver
      snippetFacade.registerMetatype( 'service_code', 'Service code' )

      # this simple example uses constant 'SNIPPETS', a real world implementation
      # would use generate snippets body and meta data based on 'snippetDefitions' 
      # 
      SNIPPETS.each do |snippet|
         # hand over body (with tla-code), and meta data
         snippetFacade.handOver( snippet['metatype'], snippet['appName'], snippet['body'] )
      end
  end
end # class
end # module
# sbuilder.yaml configuration
Given a file named "cnf/sbuilder.yaml" with:
# extend and snippets properties configured in scenarios!! 

preferences:
And YAML configuration file `cnf/sbuilder.yaml`
# Setup1:
#
# Setup1:
# - set cardinality for domain 'dommi'
# - lauch /customer(get)
#
interfaces:
    -  className: Sbuilder::ParamSetLoaderSwagger
       file: example_interface.yaml
And YAML configuration file `cnf/sbuilder.yaml`
#
# Define a simple 'setup1' 

setups:
  - setupDirectory: setup1
    extensions:
         - file: extend_example.yaml
Given a file named "cnf/extend_example.yaml" with:

# Setup makes just one call to interface /customer(post)
#
- step-extension:
       - interface: /customer(post)
And a file named "cnf/example_interface.yaml" with:
#
# Configure interface operation /customer(post) 
#
swagger: "2.0"
info:
  version: 1.0.0
  title: Sbuild demo customer
paths: 
  /customer:
    post: 
      operationId: postCustomer
      parameters:
        - name: customer
          in: body
          description: Customers to enter
          required: true
          schema:
              $ref: '#/definitions/Customer'
      responses:
         200:
            description: OK
definitions: 
     Customer:
         properties:
           id:
            type: string
           name:
            type: string
And YAML configuration file `cnf/sbuilder.yaml`
#
# 'resolvers' section loads resolvers from file
# 'cnf/resolver_customer.yaml'

resolvers:
     - file: resolver.yaml
And a file named "cnf/resolver.yaml" with:
# default resolver with one regexp rule
-    Name: default-resolver
     Matcher: !ruby/regexp /.*/
     Rules: 
      - Matcher: id
        Domain: id_dom
      - Matcher: name
        Domain: name_dom
- Scenario: Load snippets using named snipped loader plugin instance
link

Plugin extension configured in 'sbuilder.yaml'

- array element in 'extend.loader' defines 'gem' name and loader 'className'.
  This entry defines also 'objectName' to instantiate

- array element in 'snippets' refers to 'objectName' used in 'extend.loader' section

Running the scenario:

- setup 'setup1' defines a step calling interface operation '/customer(post)'

- SnippetLoaderExample defines macro for a metatype
  'service_implementation' and application name '/customer(post)'. This instructs
  sbuilder to add a call to the macro into process modeling interface operation
  '/customer(post)' into specification code.

- The macro calls a procedure, which is defined using metatype 'service_code', and application
  name '/customer(post)'. The metatype 'service_code' is registered before snippets are
  handed over to sbuilder.

- The procedure output message 'Service called', which can observed in stdout,
  during TLC verification run.
features/100-plugin-extension/200-snippet-loader-plugin.feature:206
Given YAML configuration file `cnf/sbuilder.yaml`
#
# Require the example plugin & configure it

extend:
    loaders:
        - gem: snippet_loader_example.rb
          className: Tst::SnippetLoaderExample
          # optional congfiuration to 'class.configure'
          configuration: 
          # Notice optionally create object with 'objectName',
          # and optional configuration to 'object#configure'
          objects:
             - objectName: snippetLdr
               configuration: 
And YAML configuration file `cnf/sbuilder.yaml`
#
# Map service implementation 

snippets:

  # className : when using unnamed objects
  # - className: Tst::SnippetLoaderExample
  # objectName: when refering objects named 'extend.loaders'
  - objectName: snippetLdr
    snippets:
Given I successfully run `bash -c "RUBYLIB=lib sbuilder.rb init"`
When I successfully run `bash -c "RUBYLIB=lib sbuilder.rb generate setup1 -l DEBUG"`
And I cd to "gen/setup1/tla"
When I successfully run command `java -cp $TLATOOLS pcal.trans model`
And I run command `java -cp $TLATOOLS tlc2.TLC setup`
And the stdout should contain "Model checking completed. No error has been found."
And the stdout should contain "Service called"
- Scenario: Load snippets using unnamed snipped loader plugin instance
link

Plugin extension configured in 'sbuilder.yaml'

- array element in 'extend.loader' defines 'gem' name and loader 'className'.
  Array 'objects' is empty

- array element in 'snippets' refers to 'className' to create an
  unamed snippet loader plugin instance
features/100-plugin-extension/200-snippet-loader-plugin.feature:274
Given YAML configuration file `cnf/sbuilder.yaml`
#
# Require the example plugin & configure it

extend:
    loaders:
        - gem: snippet_loader_example.rb
          className: Tst::SnippetLoaderExample
          # optional congfiuration to 'class.configure'
          configuration: 
          # No objects array!
          # and optional configuration to 'object#configure'
          # objects:
          #    - objectName: snippetLdr
          #      configuration: 
And YAML configuration file `cnf/sbuilder.yaml`
#
# Map service implementation 

snippets:
  # Using className to instantiate unnamed object
  - className: Tst::SnippetLoaderExample
    snippets:
  # # objectName: when refering objects named 'extend.loaders'
  # - objectName: snippetLdr
Given I successfully run `bash -c "RUBYLIB=lib sbuilder.rb init"`
When I successfully run `bash -c "RUBYLIB=lib sbuilder.rb generate setup1 -l DEBUG"`
And I cd to "gen/setup1/tla"
When I successfully run command `java -cp $TLATOOLS pcal.trans model`
And I run command `java -cp $TLATOOLS tlc2.TLC setup`
And the stdout should contain "Model checking completed. No error has been found."
And the stdout should contain "Service called"