grumlin 0.20.2 → 0.22.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 +4 -4
 - data/Gemfile.lock +6 -6
 - data/README.md +66 -8
 - data/gremlin_server/Dockerfile +1 -1
 - data/grumlin.gemspec +1 -1
 - data/lib/grumlin/action.rb +13 -8
 - data/lib/grumlin/client.rb +16 -12
 - data/lib/grumlin/config.rb +26 -0
 - data/lib/grumlin/dummy_transaction.rb +25 -0
 - data/lib/grumlin/features/feature_list.rb +19 -0
 - data/lib/grumlin/features/neptune_features.rb +13 -0
 - data/lib/grumlin/features/tinkergraph_features.rb +13 -0
 - data/lib/grumlin/features.rb +16 -0
 - data/lib/grumlin/repository/instance_methods.rb +19 -18
 - data/lib/grumlin/repository.rb +7 -0
 - data/lib/grumlin/request_dispatcher.rb +4 -43
 - data/lib/grumlin/request_error_factory.rb +59 -0
 - data/lib/grumlin/shortcuts/properties.rb +5 -4
 - data/lib/grumlin/shortcuts/upserts.rb +2 -2
 - data/lib/grumlin/steppable.rb +8 -3
 - data/lib/grumlin/steps.rb +3 -1
 - data/lib/grumlin/steps_serializers/bytecode.rb +8 -6
 - data/lib/grumlin/transaction.rb +39 -0
 - data/lib/grumlin/traversal_start.rb +27 -0
 - data/lib/grumlin/version.rb +1 -1
 - data/lib/grumlin.rb +14 -11
 - metadata +11 -4
 - data/CHANGELOG.md +0 -71
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 519d93b97b2cd8f07a1e11e75e45b0b7ca780947e82b3624d5e6c7c6d719ec27
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 5eed9eab9e99fe02246995adbc8c01988bd21d150df234946a575d01cda518f2
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 76cd4990acd94d119b2a716e25b10d35bdc970e794a77de4d25dcc7e3f67f5129236ff1c037a4f0f0c3208643afca9695b6dc602bb84ae01d61a84c1b89364fe
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: afda51dc7010f1fc508e96b203727ebee971597b6425aa2ec29932d7957e1b2b12d8959526a5fc10d92e46926e94dab1ca3d62eb583bbb36498f99bbd3e11689
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                grumlin (0. 
     | 
| 
      
 4 
     | 
    
         
            +
                grumlin (0.22.0)
         
     | 
| 
       5 
5 
     | 
    
         
             
                  async-pool (~> 0.3)
         
     | 
| 
       6 
6 
     | 
    
         
             
                  async-websocket (~> 0.19)
         
     | 
| 
       7 
7 
     | 
    
         
             
                  oj (~> 3.13)
         
     | 
| 
         @@ -21,11 +21,11 @@ GEM 
     | 
|
| 
       21 
21 
     | 
    
         
             
                  console (~> 1.10)
         
     | 
| 
       22 
22 
     | 
    
         
             
                  nio4r (~> 2.3)
         
     | 
| 
       23 
23 
     | 
    
         
             
                  timers (~> 4.1)
         
     | 
| 
       24 
     | 
    
         
            -
                async-http (0. 
     | 
| 
      
 24 
     | 
    
         
            +
                async-http (0.57.0)
         
     | 
| 
       25 
25 
     | 
    
         
             
                  async (>= 1.25)
         
     | 
| 
       26 
26 
     | 
    
         
             
                  async-io (>= 1.28)
         
     | 
| 
       27 
27 
     | 
    
         
             
                  async-pool (>= 0.2)
         
     | 
| 
       28 
     | 
    
         
            -
                  protocol-http (~> 0. 
     | 
| 
      
 28 
     | 
    
         
            +
                  protocol-http (~> 0.23.1)
         
     | 
| 
       29 
29 
     | 
    
         
             
                  protocol-http1 (~> 0.14.0)
         
     | 
| 
       30 
30 
     | 
    
         
             
                  protocol-http2 (~> 0.14.0)
         
     | 
| 
       31 
31 
     | 
    
         
             
                  traces (~> 0.4.0)
         
     | 
| 
         @@ -37,7 +37,7 @@ GEM 
     | 
|
| 
       37 
37 
     | 
    
         
             
                  rspec (~> 3.0)
         
     | 
| 
       38 
38 
     | 
    
         
             
                  rspec-files (~> 1.0)
         
     | 
| 
       39 
39 
     | 
    
         
             
                  rspec-memory (~> 1.0)
         
     | 
| 
       40 
     | 
    
         
            -
                async-websocket (0.19. 
     | 
| 
      
 40 
     | 
    
         
            +
                async-websocket (0.19.2)
         
     | 
| 
       41 
41 
     | 
    
         
             
                  async-http (~> 0.54)
         
     | 
| 
       42 
42 
     | 
    
         
             
                  async-io (~> 1.23)
         
     | 
| 
       43 
43 
     | 
    
         
             
                  protocol-websocket (~> 0.7.0)
         
     | 
| 
         @@ -72,7 +72,7 @@ GEM 
     | 
|
| 
       72 
72 
     | 
    
         
             
                  racc (~> 1.4)
         
     | 
| 
       73 
73 
     | 
    
         
             
                nokogiri (1.13.6-x86_64-linux)
         
     | 
| 
       74 
74 
     | 
    
         
             
                  racc (~> 1.4)
         
     | 
| 
       75 
     | 
    
         
            -
                oj (3.13. 
     | 
| 
      
 75 
     | 
    
         
            +
                oj (3.13.20)
         
     | 
| 
       76 
76 
     | 
    
         
             
                overcommit (0.59.1)
         
     | 
| 
       77 
77 
     | 
    
         
             
                  childprocess (>= 0.6.3, < 5)
         
     | 
| 
       78 
78 
     | 
    
         
             
                  iniparse (~> 1.4)
         
     | 
| 
         @@ -81,7 +81,7 @@ GEM 
     | 
|
| 
       81 
81 
     | 
    
         
             
                parser (3.1.2.0)
         
     | 
| 
       82 
82 
     | 
    
         
             
                  ast (~> 2.4.1)
         
     | 
| 
       83 
83 
     | 
    
         
             
                protocol-hpack (1.4.2)
         
     | 
| 
       84 
     | 
    
         
            -
                protocol-http (0. 
     | 
| 
      
 84 
     | 
    
         
            +
                protocol-http (0.23.1)
         
     | 
| 
       85 
85 
     | 
    
         
             
                protocol-http1 (0.14.4)
         
     | 
| 
       86 
86 
     | 
    
         
             
                  protocol-http (~> 0.22)
         
     | 
| 
       87 
87 
     | 
    
         
             
                protocol-http2 (0.14.2)
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -46,9 +46,38 @@ Or install it yourself as: 
     | 
|
| 
       46 
46 
     | 
    
         
             
            ```ruby
         
     | 
| 
       47 
47 
     | 
    
         
             
            Grumlin.configure do |config|
         
     | 
| 
       48 
48 
     | 
    
         
             
              config.url = "ws://localhost:8182/gremlin"
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              # make sure you select right provider for better compatibility
         
     | 
| 
      
 51 
     | 
    
         
            +
              config.provider = :tinkergraph
         
     | 
| 
       49 
52 
     | 
    
         
             
            end
         
     | 
| 
       50 
53 
     | 
    
         
             
            ```
         
     | 
| 
       51 
54 
     | 
    
         | 
| 
      
 55 
     | 
    
         
            +
            #### Providers
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            Currently `Grumlin` supports 2 providers:
         
     | 
| 
      
 58 
     | 
    
         
            +
            - tinkergraph (default)
         
     | 
| 
      
 59 
     | 
    
         
            +
            - neptune
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            As different providers may have or may have not support for specific features it's recommended to
         
     | 
| 
      
 62 
     | 
    
         
            +
            explicitly specify the provider you use.
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            #### Provider features
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
            Every provider is described by a set of features. In the future `Grumlin` may decide to disable or enable 
         
     | 
| 
      
 67 
     | 
    
         
            +
            some parts of it's functionality to comply provider's supported features.
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            To check current providers supported features use
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 72 
     | 
    
         
            +
            Grumlin.features
         
     | 
| 
      
 73 
     | 
    
         
            +
            ```
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            Current differences between providers:
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
            | Feature      | TinkerGraph                                                                                                                                               |AWS Neptune|
         
     | 
| 
      
 78 
     | 
    
         
            +
            |--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
         
     | 
| 
      
 79 
     | 
    
         
            +
            | Transactions | Transaction semantic is ignoroed, data is always writen, `tx.rollback` does nothing, an info is printed every time transactions are used with TinkerGraph |Full support
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
       52 
81 
     | 
    
         
             
            ### Traversing graphs
         
     | 
| 
       53 
82 
     | 
    
         | 
| 
       54 
83 
     | 
    
         
             
            **Warning**: Not all steps and expressions defined in the reference documentation are supported.
         
     | 
| 
         @@ -216,21 +245,32 @@ Each `return_mode` is mapped to a particular termination step: 
     | 
|
| 
       216 
245 
     | 
    
         
             
            - `:traversal` - do not execute the query and return the traversal as is
         
     | 
| 
       217 
246 
     | 
    
         | 
| 
       218 
247 
     | 
    
         
             
            `Grumlin::Repository` also provides a set of generic CRUD operations:
         
     | 
| 
       219 
     | 
    
         
            -
            - `add_vertex(label, id = nil, **properties)`
         
     | 
| 
       220 
     | 
    
         
            -
            - `add_edge(label, id = nil, from:, to:, **properties)`
         
     | 
| 
       221 
     | 
    
         
            -
            - `drop_vertex(id)`
         
     | 
| 
       222 
     | 
    
         
            -
            - `drop_edge(id = nil, from: nil, to: nil, label: nil)`
         
     | 
| 
      
 248 
     | 
    
         
            +
            - `add_vertex(label, id = nil, start: g, **properties)`
         
     | 
| 
      
 249 
     | 
    
         
            +
            - `add_edge(label, id = nil, from:, to:, start: g, **properties)`
         
     | 
| 
      
 250 
     | 
    
         
            +
            - `drop_vertex(id, start: g)`
         
     | 
| 
      
 251 
     | 
    
         
            +
            - `drop_edge(id = nil, from: nil, to: nil, label: nil, start: g)`
         
     | 
| 
       223 
252 
     | 
    
         | 
| 
       224 
253 
     | 
    
         
             
            and a few methods that emulate upserts:
         
     | 
| 
       225 
     | 
    
         
            -
            - `upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, **params)` 
         
     | 
| 
       226 
     | 
    
         
            -
            - `upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, on_failure: :retry, **params)`
         
     | 
| 
       227 
     | 
    
         
            -
            - `upsert_edges(edges, batch_size: 100, on_failure: :retry, **params)`
         
     | 
| 
       228 
     | 
    
         
            -
            - `upsert_vertices(edges, batch_size: 100, on_failure: :retry, **params)`
         
     | 
| 
      
 254 
     | 
    
         
            +
            - `upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params)` 
         
     | 
| 
      
 255 
     | 
    
         
            +
            - `upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params)`
         
     | 
| 
      
 256 
     | 
    
         
            +
            - `upsert_edges(edges, batch_size: 100, on_failure: :retry, start: g, **params)`
         
     | 
| 
      
 257 
     | 
    
         
            +
            - `upsert_vertices(edges, batch_size: 100, on_failure: :retry, start: g, **params)`
         
     | 
| 
       229 
258 
     | 
    
         | 
| 
       230 
259 
     | 
    
         
             
            All of them support 3 different modes for error handling: `:retry`, `:ignore` and `:raise`. Retry mode is implemented
         
     | 
| 
       231 
260 
     | 
    
         
             
            with [retryable](https://github.com/nfedyashev/retryable). **params will be merged to the default config for upserts 
         
     | 
| 
       232 
261 
     | 
    
         
             
            and passed to `Retryable.retryable`. In case if you want to modify retryable behaviour you are to do so.
         
     | 
| 
       233 
262 
     | 
    
         | 
| 
      
 263 
     | 
    
         
            +
            If you want to use these methods inside a transaction simply pass your `gtx` as `start` parameter:
         
     | 
| 
      
 264 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 265 
     | 
    
         
            +
            g.tx do |gtx|
         
     | 
| 
      
 266 
     | 
    
         
            +
              add_vertex(:vertex, start: gtx)
         
     | 
| 
      
 267 
     | 
    
         
            +
            end
         
     | 
| 
      
 268 
     | 
    
         
            +
            ```
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
            If you don't want to define you own repository, simply use
         
     | 
| 
      
 271 
     | 
    
         
            +
             
     | 
| 
      
 272 
     | 
    
         
            +
            `Grumlin::Repository.new` returns an instance of an anonymous class extending `Grumlin::Repository`. 
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
       234 
274 
     | 
    
         
             
            **Usage**
         
     | 
| 
       235 
275 
     | 
    
         | 
| 
       236 
276 
     | 
    
         
             
            To execute the query defined in a query block one simply needs to call a method with the same name:
         
     | 
| 
         @@ -254,6 +294,24 @@ it may be useful for debugging. Note that one needs to call a termination step m 
     | 
|
| 
       254 
294 
     | 
    
         | 
| 
       255 
295 
     | 
    
         
             
            method will return profiling data of the results.
         
     | 
| 
       256 
296 
     | 
    
         | 
| 
      
 297 
     | 
    
         
            +
            #### Transactions
         
     | 
| 
      
 298 
     | 
    
         
            +
             
     | 
| 
      
 299 
     | 
    
         
            +
            Since 0.22.0 `Grumlin` supports transactions when working with providers that supports them:
         
     | 
| 
      
 300 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 301 
     | 
    
         
            +
            # Using Transaction directly
         
     | 
| 
      
 302 
     | 
    
         
            +
            tx = g.tx
         
     | 
| 
      
 303 
     | 
    
         
            +
            gtx = tx.begin
         
     | 
| 
      
 304 
     | 
    
         
            +
            gtx.addV(:vertex).iterate
         
     | 
| 
      
 305 
     | 
    
         
            +
            tx.commit # or tx.rollback
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
            # Using with a block
         
     | 
| 
      
 308 
     | 
    
         
            +
            g.tx do |gtx|
         
     | 
| 
      
 309 
     | 
    
         
            +
              gtx.addV(:vertex).iterate
         
     | 
| 
      
 310 
     | 
    
         
            +
              # raise Grumlin::Rollback to manually rollback
         
     | 
| 
      
 311 
     | 
    
         
            +
              # any other exception will also rollback the transaction and will be reraised 
         
     | 
| 
      
 312 
     | 
    
         
            +
            end # commits automatically
         
     | 
| 
      
 313 
     | 
    
         
            +
            ```
         
     | 
| 
      
 314 
     | 
    
         
            +
             
     | 
| 
       257 
315 
     | 
    
         
             
            #### IRB
         
     | 
| 
       258 
316 
     | 
    
         | 
| 
       259 
317 
     | 
    
         
             
            An example of how to start an IRB session with support for executing gremlin queries:
         
     | 
    
        data/gremlin_server/Dockerfile
    CHANGED
    
    
    
        data/grumlin.gemspec
    CHANGED
    
    | 
         @@ -20,7 +20,7 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
              spec.metadata["homepage_uri"] = spec.homepage
         
     | 
| 
       22 
22 
     | 
    
         
             
              spec.metadata["source_code_uri"] = "https://github.com/zhulik/grumlin"
         
     | 
| 
       23 
     | 
    
         
            -
              spec.metadata["changelog_uri"] = "https://github.com/ 
     | 
| 
      
 23 
     | 
    
         
            +
              spec.metadata["changelog_uri"] = "https://github.com/babbel/grumlin/releases"
         
     | 
| 
       24 
24 
     | 
    
         
             
              spec.metadata["rubygems_mfa_required"] = "true"
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
              spec.files = Dir.chdir(File.expand_path(__dir__)) do
         
     | 
    
        data/lib/grumlin/action.rb
    CHANGED
    
    | 
         @@ -4,18 +4,19 @@ module Grumlin 
     | 
|
| 
       4 
4 
     | 
    
         
             
              class Action < Steppable
         
     | 
| 
       5 
5 
     | 
    
         
             
                attr_reader :name, :args, :params, :next_step, :configuration_steps, :previous_step, :shortcut
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
                 
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
                # client is only used when a traversal is a part of transaction
         
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(name, args: [], params: {}, previous_step: nil, pool: Grumlin.default_pool, session_id: nil)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  super(pool: pool, session_id: session_id)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       9 
11 
     | 
    
         
             
                  @name = name.to_sym
         
     | 
| 
       10 
12 
     | 
    
         
             
                  @args = args # TODO: add recursive validation: only json types or Action
         
     | 
| 
       11 
13 
     | 
    
         
             
                  @params = params # TODO: add recursive validation: only json types
         
     | 
| 
       12 
14 
     | 
    
         
             
                  @previous_step = previous_step
         
     | 
| 
       13 
15 
     | 
    
         
             
                  @shortcut = shortcuts[@name]
         
     | 
| 
       14 
     | 
    
         
            -
                  @pool = pool || Grumlin.default_pool
         
     | 
| 
       15 
16 
     | 
    
         
             
                end
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         
             
                def configuration_step?
         
     | 
| 
       18 
     | 
    
         
            -
                  CONFIGURATION_STEPS.include?(@name)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  CONFIGURATION_STEPS.include?(@name) || name.to_sym == :tx
         
     | 
| 
       19 
20 
     | 
    
         
             
                end
         
     | 
| 
       20 
21 
     | 
    
         | 
| 
       21 
22 
     | 
    
         
             
                def start_step?
         
     | 
| 
         @@ -73,14 +74,18 @@ module Grumlin 
     | 
|
| 
       73 
74 
     | 
    
         
             
                end
         
     | 
| 
       74 
75 
     | 
    
         | 
| 
       75 
76 
     | 
    
         
             
                def toList
         
     | 
| 
       76 
     | 
    
         
            -
                   
     | 
| 
       77 
     | 
    
         
            -
                    client.write(bytecode)
         
     | 
| 
       78 
     | 
    
         
            -
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  client_write(bytecode)
         
     | 
| 
       79 
78 
     | 
    
         
             
                end
         
     | 
| 
       80 
79 
     | 
    
         | 
| 
       81 
80 
     | 
    
         
             
                def iterate
         
     | 
| 
      
 81 
     | 
    
         
            +
                  client_write(bytecode(no_return: true))
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                private
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                def client_write(payload)
         
     | 
| 
       82 
87 
     | 
    
         
             
                  @pool.acquire do |client|
         
     | 
| 
       83 
     | 
    
         
            -
                    client.write( 
     | 
| 
      
 88 
     | 
    
         
            +
                    client.write(payload, session_id: @session_id)
         
     | 
| 
       84 
89 
     | 
    
         
             
                  end
         
     | 
| 
       85 
90 
     | 
    
         
             
                end
         
     | 
| 
       86 
91 
     | 
    
         
             
              end
         
     | 
    
        data/lib/grumlin/client.rb
    CHANGED
    
    | 
         @@ -24,8 +24,8 @@ module Grumlin 
     | 
|
| 
       24 
24 
     | 
    
         
             
                    @client.close
         
     | 
| 
       25 
25 
     | 
    
         
             
                  end
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                  def write(bytecode)
         
     | 
| 
       28 
     | 
    
         
            -
                    @client.write(bytecode)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def write(bytecode, session_id: nil)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @client.write(bytecode, session_id: session_id)
         
     | 
| 
       29 
29 
     | 
    
         
             
                  ensure
         
     | 
| 
       30 
30 
     | 
    
         
             
                    @count += 1
         
     | 
| 
       31 
31 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -94,14 +94,14 @@ module Grumlin 
     | 
|
| 
       94 
94 
     | 
    
         
             
                end
         
     | 
| 
       95 
95 
     | 
    
         | 
| 
       96 
96 
     | 
    
         
             
                # TODO: support yielding
         
     | 
| 
       97 
     | 
    
         
            -
                def write(bytecode)
         
     | 
| 
      
 97 
     | 
    
         
            +
                def write(bytecode, session_id: nil)
         
     | 
| 
       98 
98 
     | 
    
         
             
                  raise NotConnectedError unless connected?
         
     | 
| 
       99 
99 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
                  request = to_query(bytecode)
         
     | 
| 
       101 
     | 
    
         
            -
                  channel = @request_dispatcher.add_request(request)
         
     | 
| 
       102 
     | 
    
         
            -
                  @transport.write(request)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  request = to_query(bytecode, session_id: session_id)
         
     | 
| 
       103 
101 
     | 
    
         | 
| 
      
 102 
     | 
    
         
            +
                  channel = @request_dispatcher.add_request(request)
         
     | 
| 
       104 
103 
     | 
    
         
             
                  begin
         
     | 
| 
      
 104 
     | 
    
         
            +
                    @transport.write(request)
         
     | 
| 
       105 
105 
     | 
    
         
             
                    channel.dequeue.flat_map { |item| Typing.cast(item) }
         
     | 
| 
       106 
106 
     | 
    
         
             
                  rescue Async::Stop, Async::TimeoutError
         
     | 
| 
       107 
107 
     | 
    
         
             
                    close(check_requests: false)
         
     | 
| 
         @@ -124,15 +124,19 @@ module Grumlin 
     | 
|
| 
       124 
124 
     | 
    
         
             
                  Transport.new(@url, parent: @parent, **@client_options)
         
     | 
| 
       125 
125 
     | 
    
         
             
                end
         
     | 
| 
       126 
126 
     | 
    
         | 
| 
       127 
     | 
    
         
            -
                def to_query(bytecode)
         
     | 
| 
      
 127 
     | 
    
         
            +
                def to_query(bytecode, session_id:)
         
     | 
| 
       128 
128 
     | 
    
         
             
                  {
         
     | 
| 
       129 
129 
     | 
    
         
             
                    requestId: SecureRandom.uuid,
         
     | 
| 
       130 
     | 
    
         
            -
                    op:  
     | 
| 
       131 
     | 
    
         
            -
                    processor:  
     | 
| 
      
 130 
     | 
    
         
            +
                    op: :bytecode,
         
     | 
| 
      
 131 
     | 
    
         
            +
                    processor: session_id ? :session : :traversal,
         
     | 
| 
       132 
132 
     | 
    
         
             
                    args: {
         
     | 
| 
       133 
     | 
    
         
            -
                      gremlin: { 
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
      
 133 
     | 
    
         
            +
                      gremlin: {
         
     | 
| 
      
 134 
     | 
    
         
            +
                        :@type => "g:Bytecode",
         
     | 
| 
      
 135 
     | 
    
         
            +
                        :@value => bytecode.serialize
         
     | 
| 
      
 136 
     | 
    
         
            +
                      },
         
     | 
| 
      
 137 
     | 
    
         
            +
                      aliases: { g: :g },
         
     | 
| 
      
 138 
     | 
    
         
            +
                      session: session_id
         
     | 
| 
      
 139 
     | 
    
         
            +
                    }.compact
         
     | 
| 
       136 
140 
     | 
    
         
             
                  }
         
     | 
| 
       137 
141 
     | 
    
         
             
                end
         
     | 
| 
       138 
142 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Grumlin
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Config
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :url, :pool_size, :client_concurrency, :client_factory, :provider
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                SUPPORTED_PROVIDERS = %i[neptune tinkergraph].freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                class ConfigurationError < Grumlin::Error; end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                class UnknownProviderError < ConfigurationError; end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @pool_size = 10
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @client_concurrency = 5
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @provider = :tinkergraph
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def validate!
         
     | 
| 
      
 21 
     | 
    
         
            +
                  return if SUPPORTED_PROVIDERS.include?(provider.to_sym)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  raise UnknownProviderError, "provider '#{provider}' is unknown. Supported providers: #{SUPPORTED_PROVIDERS}"
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Grumlin
         
     | 
| 
      
 4 
     | 
    
         
            +
              class DummyTransaction < Transaction
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :uuid
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                include Console
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(traversal_start_class, pool: Grumlin.default_pool) # rubocop:disable Lint/MissingSuper, Lint/UnusedMethodArgument
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @traversal_start_class = traversal_start_class
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  logger.info(self) do
         
     | 
| 
      
 13 
     | 
    
         
            +
                    "#{Grumlin.config.provider} does not support transactions. commit and rollback are ignored, data will be saved"
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def commit
         
     | 
| 
      
 18 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def rollback
         
     | 
| 
      
 22 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Grumlin
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Features
         
     | 
| 
      
 5 
     | 
    
         
            +
                class FeatureList
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def user_supplied_ids?
         
     | 
| 
      
 7 
     | 
    
         
            +
                    raise(NotImplementedError) if @user_supplied_ids.nil?
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    @user_supplied_ids
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def supports_transactions?
         
     | 
| 
      
 13 
     | 
    
         
            +
                    raise(NotImplementedError) if @supports_transactions.nil?
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    @supports_transactions
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Grumlin
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Features
         
     | 
| 
      
 5 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 6 
     | 
    
         
            +
                  FEATURES = {
         
     | 
| 
      
 7 
     | 
    
         
            +
                    neptune: NeptuneFeatures.new,
         
     | 
| 
      
 8 
     | 
    
         
            +
                    tinkergraph: TinkergraphFeatures.new
         
     | 
| 
      
 9 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def for(provider)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    FEATURES[provider]
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -8,7 +8,7 @@ module Grumlin 
     | 
|
| 
       8 
8 
     | 
    
         
             
                  extend Forwardable
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
                  UPSERT_RETRY_PARAMS = {
         
     | 
| 
       11 
     | 
    
         
            -
                    on: [Grumlin::AlreadyExistsError, Grumlin:: 
     | 
| 
      
 11 
     | 
    
         
            +
                    on: [Grumlin::AlreadyExistsError, Grumlin::ConcurrentModificationError],
         
     | 
| 
       12 
12 
     | 
    
         
             
                    sleep_method: ->(n) { Async::Task.current.sleep(n) },
         
     | 
| 
       13 
13 
     | 
    
         
             
                    tries: 3,
         
     | 
| 
       14 
14 
     | 
    
         
             
                    sleep: ->(n) { (n**2) + 1 + rand }
         
     | 
| 
         @@ -22,51 +22,51 @@ module Grumlin 
     | 
|
| 
       22 
22 
     | 
    
         
             
                    self.class.shortcuts
         
     | 
| 
       23 
23 
     | 
    
         
             
                  end
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                  def drop_vertex(id)
         
     | 
| 
       26 
     | 
    
         
            -
                     
     | 
| 
      
 25 
     | 
    
         
            +
                  def drop_vertex(id, start: g)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    start.V(id).drop.iterate
         
     | 
| 
       27 
27 
     | 
    
         
             
                  end
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                  def drop_edge(id = nil, from: nil, to: nil, label: nil) # rubocop:disable Metrics/AbcSize
         
     | 
| 
      
 29 
     | 
    
         
            +
                  def drop_edge(id = nil, from: nil, to: nil, label: nil, start: g) # rubocop:disable Metrics/AbcSize
         
     | 
| 
       30 
30 
     | 
    
         
             
                    raise ArgumentError, "either id or from:, to: and label: must be passed" if [id, from, to, label].all?(&:nil?)
         
     | 
| 
       31 
     | 
    
         
            -
                    return  
     | 
| 
      
 31 
     | 
    
         
            +
                    return start.E(id).drop.iterate unless id.nil?
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
33 
     | 
    
         
             
                    raise ArgumentError, "from:, to: and label: must be passed" if [from, to, label].any?(&:nil?)
         
     | 
| 
       34 
34 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                     
     | 
| 
      
 35 
     | 
    
         
            +
                    start.V(from).outE(label).where(__.inV.hasId(to)).limit(1).drop.iterate
         
     | 
| 
       36 
36 
     | 
    
         
             
                  end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
                  def add_vertex(label, id = nil, **properties)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  def add_vertex(label, id = nil, start: g, **properties)
         
     | 
| 
       39 
39 
     | 
    
         
             
                    id ||= properties[T.id]
         
     | 
| 
       40 
40 
     | 
    
         
             
                    properties = except(properties, T.id)
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                    t =  
     | 
| 
      
 42 
     | 
    
         
            +
                    t = start.addV(label)
         
     | 
| 
       43 
43 
     | 
    
         
             
                    t = t.props(T.id => id) unless id.nil?
         
     | 
| 
       44 
44 
     | 
    
         
             
                    t.props(**properties).next
         
     | 
| 
       45 
45 
     | 
    
         
             
                  end
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                  def add_edge(label, id = nil, from:, to:, **properties)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  def add_edge(label, id = nil, from:, to:, start: g, **properties)
         
     | 
| 
       48 
48 
     | 
    
         
             
                    id ||= properties[T.id]
         
     | 
| 
       49 
49 
     | 
    
         
             
                    properties = except(properties, T.label)
         
     | 
| 
       50 
50 
     | 
    
         
             
                    properties[T.id] = id
         
     | 
| 
       51 
51 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
                     
     | 
| 
      
 52 
     | 
    
         
            +
                    start.addE(label).from(__.V(from)).to(__.V(to)).props(**properties).next
         
     | 
| 
       53 
53 
     | 
    
         
             
                  end
         
     | 
| 
       54 
54 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
                  def upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, **params)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params) # rubocop:disable Metrics/ParameterLists
         
     | 
| 
       56 
56 
     | 
    
         
             
                    with_upsert_error_handling(on_failure, params) do
         
     | 
| 
       57 
57 
     | 
    
         
             
                      create_properties, update_properties = cleanup_properties(create_properties, update_properties)
         
     | 
| 
       58 
58 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
                       
     | 
| 
      
 59 
     | 
    
         
            +
                      start.upsertV(label, id, create_properties, update_properties).id.next
         
     | 
| 
       60 
60 
     | 
    
         
             
                    end
         
     | 
| 
       61 
61 
     | 
    
         
             
                  end
         
     | 
| 
       62 
62 
     | 
    
         | 
| 
       63 
63 
     | 
    
         
             
                  # vertices:
         
     | 
| 
       64 
64 
     | 
    
         
             
                  # [["label", "id", {create: :properties}, {update: properties}]]
         
     | 
| 
       65 
65 
     | 
    
         
             
                  # params can override Retryable config from UPSERT_RETRY_PARAMS
         
     | 
| 
       66 
     | 
    
         
            -
                  def upsert_vertices(vertices, batch_size: 100, on_failure: :retry, **params)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def upsert_vertices(vertices, batch_size: 100, on_failure: :retry, start: g, **params)
         
     | 
| 
       67 
67 
     | 
    
         
             
                    vertices.each_slice(batch_size) do |slice|
         
     | 
| 
       68 
68 
     | 
    
         
             
                      with_upsert_error_handling(on_failure, params) do
         
     | 
| 
       69 
     | 
    
         
            -
                        slice.reduce( 
     | 
| 
      
 69 
     | 
    
         
            +
                        slice.reduce(start) do |t, (label, id, create_properties, update_properties)|
         
     | 
| 
       70 
70 
     | 
    
         
             
                          create_properties, update_properties = cleanup_properties(create_properties, update_properties)
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
       72 
72 
     | 
    
         
             
                          t.upsertV(label, id, create_properties, update_properties)
         
     | 
| 
         @@ -77,20 +77,21 @@ module Grumlin 
     | 
|
| 
       77 
77 
     | 
    
         | 
| 
       78 
78 
     | 
    
         
             
                  # Only from and to are used to find the existing edge, if one wants to assign an id to a created edge,
         
     | 
| 
       79 
79 
     | 
    
         
             
                  # it must be passed as T.id in create_properties.
         
     | 
| 
       80 
     | 
    
         
            -
                  def upsert_edge(label, from:, to:, create_properties: {}, update_properties: {},  
     | 
| 
      
 80 
     | 
    
         
            +
                  def upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, # rubocop:disable Metrics/ParameterLists
         
     | 
| 
      
 81 
     | 
    
         
            +
                                  on_failure: :retry, start: g, **params)
         
     | 
| 
       81 
82 
     | 
    
         
             
                    with_upsert_error_handling(on_failure, params) do
         
     | 
| 
       82 
83 
     | 
    
         
             
                      create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
         
     | 
| 
       83 
     | 
    
         
            -
                       
     | 
| 
      
 84 
     | 
    
         
            +
                      start.upsertE(label, from, to, create_properties, update_properties).id.next
         
     | 
| 
       84 
85 
     | 
    
         
             
                    end
         
     | 
| 
       85 
86 
     | 
    
         
             
                  end
         
     | 
| 
       86 
87 
     | 
    
         | 
| 
       87 
88 
     | 
    
         
             
                  # edges:
         
     | 
| 
       88 
89 
     | 
    
         
             
                  # [["label", "from", "to", {create: :properties}, {update: properties}]]
         
     | 
| 
       89 
90 
     | 
    
         
             
                  # params can override Retryable config from UPSERT_RETRY_PARAMS
         
     | 
| 
       90 
     | 
    
         
            -
                  def upsert_edges(edges, batch_size: 100, on_failure: :retry, **params)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  def upsert_edges(edges, batch_size: 100, on_failure: :retry, start: g, **params)
         
     | 
| 
       91 
92 
     | 
    
         
             
                    edges.each_slice(batch_size) do |slice|
         
     | 
| 
       92 
93 
     | 
    
         
             
                      with_upsert_error_handling(on_failure, params) do
         
     | 
| 
       93 
     | 
    
         
            -
                        slice.reduce( 
     | 
| 
      
 94 
     | 
    
         
            +
                        slice.reduce(start) do |t, (label, from, to, create_properties, update_properties)|
         
     | 
| 
       94 
95 
     | 
    
         
             
                          create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
         
     | 
| 
       95 
96 
     | 
    
         | 
| 
       96 
97 
     | 
    
         
             
                          t.upsertE(label, from, to, create_properties, update_properties)
         
     | 
    
        data/lib/grumlin/repository.rb
    CHANGED
    
    | 
         @@ -10,6 +10,7 @@ module Grumlin 
     | 
|
| 
       10 
10 
     | 
    
         
             
                }.freeze
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                def self.extended(base)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  super
         
     | 
| 
       13 
14 
     | 
    
         
             
                  base.extend(Grumlin::Shortcuts)
         
     | 
| 
       14 
15 
     | 
    
         
             
                  base.include(Repository::InstanceMethods)
         
     | 
| 
       15 
16 
     | 
    
         | 
| 
         @@ -17,6 +18,12 @@ module Grumlin 
     | 
|
| 
       17 
18 
     | 
    
         
             
                  base.shortcuts_from(Grumlin::Shortcuts::Upserts)
         
     | 
| 
       18 
19 
     | 
    
         
             
                end
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
      
 21 
     | 
    
         
            +
                def self.new
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @repository ||= Class.new do # rubocop:disable Naming/MemoizedInstanceVariableName
         
     | 
| 
      
 23 
     | 
    
         
            +
                    extend Grumlin::Repository
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end.new
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
       20 
27 
     | 
    
         
             
                def query(name, return_mode: :list, postprocess_with: nil, &query_block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         
     | 
| 
       21 
28 
     | 
    
         
             
                  return_mode = validate_return_mode!(return_mode)
         
     | 
| 
       22 
29 
     | 
    
         
             
                  postprocess_with = validate_postprocess_with!(postprocess_with)
         
     | 
| 
         @@ -10,23 +10,6 @@ module Grumlin 
     | 
|
| 
       10 
10 
     | 
    
         
             
                  206 => :partial_content
         
     | 
| 
       11 
11 
     | 
    
         
             
                }.freeze
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                ERRORS = {
         
     | 
| 
       14 
     | 
    
         
            -
                  499 => InvalidRequestArgumentsError,
         
     | 
| 
       15 
     | 
    
         
            -
                  500 => ServerError,
         
     | 
| 
       16 
     | 
    
         
            -
                  597 => ScriptEvaluationError,
         
     | 
| 
       17 
     | 
    
         
            -
                  599 => ServerSerializationError,
         
     | 
| 
       18 
     | 
    
         
            -
                  598 => ServerTimeoutError,
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
                  401 => ClientSideError,
         
     | 
| 
       21 
     | 
    
         
            -
                  407 => ClientSideError,
         
     | 
| 
       22 
     | 
    
         
            -
                  498 => ClientSideError
         
     | 
| 
       23 
     | 
    
         
            -
                }.freeze
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                VERTEX_ALREADY_EXISTS = "Vertex with id already exists:"
         
     | 
| 
       26 
     | 
    
         
            -
                EDGE_ALREADY_EXISTS = "Edge with id already exists:"
         
     | 
| 
       27 
     | 
    
         
            -
                CONCURRENT_VERTEX_INSERT_FAILED = "Failed to complete Insert operation for a Vertex due to conflicting concurrent"
         
     | 
| 
       28 
     | 
    
         
            -
                CONCURRENT_EDGE_INSERT_FAILED = "Failed to complete Insert operation for an Edge due to conflicting concurrent"
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
13 
     | 
    
         
             
                class DispatcherError < Grumlin::Error; end
         
     | 
| 
       31 
14 
     | 
    
         | 
| 
       32 
15 
     | 
    
         
             
                class RequestAlreadyAddedError < DispatcherError; end
         
     | 
| 
         @@ -47,14 +30,16 @@ module Grumlin 
     | 
|
| 
       47 
30 
     | 
    
         | 
| 
       48 
31 
     | 
    
         
             
                # builds a response object, when it's ready sends it to the client via a channel
         
     | 
| 
       49 
32 
     | 
    
         
             
                # TODO: sometimes response does not include requestID, no idea how to handle it so far.
         
     | 
| 
       50 
     | 
    
         
            -
                def add_response(response) # rubocop:disable Metrics/AbcSize
         
     | 
| 
      
 33 
     | 
    
         
            +
                def add_response(response) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
         
     | 
| 
       51 
34 
     | 
    
         
             
                  request_id = response[:requestId]
         
     | 
| 
       52 
35 
     | 
    
         
             
                  raise UnknownRequestError unless ongoing_request?(request_id)
         
     | 
| 
       53 
36 
     | 
    
         | 
| 
       54 
37 
     | 
    
         
             
                  begin
         
     | 
| 
       55 
38 
     | 
    
         
             
                    request = @requests[request_id]
         
     | 
| 
       56 
39 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
                     
     | 
| 
      
 40 
     | 
    
         
            +
                    RequestErrorFactory.build(request, response).tap do |err|
         
     | 
| 
      
 41 
     | 
    
         
            +
                      raise err unless err.nil?
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
       58 
43 
     | 
    
         | 
| 
       59 
44 
     | 
    
         
             
                    case SUCCESS[response.dig(:status, :code)]
         
     | 
| 
       60 
45 
     | 
    
         
             
                    when :success
         
     | 
| 
         @@ -91,29 +76,5 @@ module Grumlin 
     | 
|
| 
       91 
76 
     | 
    
         
             
                  request = @requests.delete(request_id)
         
     | 
| 
       92 
77 
     | 
    
         
             
                  request[:channel].close
         
     | 
| 
       93 
78 
     | 
    
         
             
                end
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                def check_errors!(status, query)
         
     | 
| 
       96 
     | 
    
         
            -
                  if (error = ERRORS[status[:code]])
         
     | 
| 
       97 
     | 
    
         
            -
                    raise (
         
     | 
| 
       98 
     | 
    
         
            -
                      already_exists_error(status) ||
         
     | 
| 
       99 
     | 
    
         
            -
                      concurrent_insert_error(status) ||
         
     | 
| 
       100 
     | 
    
         
            -
                      error
         
     | 
| 
       101 
     | 
    
         
            -
                    ).new(status, query)
         
     | 
| 
       102 
     | 
    
         
            -
                  end
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                  return unless SUCCESS[status[:code]].nil?
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
                  raise(UnknownResponseStatus, status)
         
     | 
| 
       107 
     | 
    
         
            -
                end
         
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
                def already_exists_error(status)
         
     | 
| 
       110 
     | 
    
         
            -
                  return VertexAlreadyExistsError if status[:message]&.include?(VERTEX_ALREADY_EXISTS)
         
     | 
| 
       111 
     | 
    
         
            -
                  return EdgeAlreadyExistsError if status[:message]&.include?(EDGE_ALREADY_EXISTS)
         
     | 
| 
       112 
     | 
    
         
            -
                end
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                def concurrent_insert_error(status)
         
     | 
| 
       115 
     | 
    
         
            -
                  return ConcurrentVertexInsertFailedError if status[:message]&.include?(CONCURRENT_VERTEX_INSERT_FAILED)
         
     | 
| 
       116 
     | 
    
         
            -
                  return ConcurrentEdgeInsertFailedError if status[:message]&.include?(CONCURRENT_EDGE_INSERT_FAILED)
         
     | 
| 
       117 
     | 
    
         
            -
                end
         
     | 
| 
       118 
79 
     | 
    
         
             
              end
         
     | 
| 
       119 
80 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,59 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Grumlin
         
     | 
| 
      
 4 
     | 
    
         
            +
              class RequestErrorFactory
         
     | 
| 
      
 5 
     | 
    
         
            +
                ERRORS = {
         
     | 
| 
      
 6 
     | 
    
         
            +
                  499 => InvalidRequestArgumentsError,
         
     | 
| 
      
 7 
     | 
    
         
            +
                  500 => ServerError,
         
     | 
| 
      
 8 
     | 
    
         
            +
                  597 => ScriptEvaluationError,
         
     | 
| 
      
 9 
     | 
    
         
            +
                  599 => ServerSerializationError,
         
     | 
| 
      
 10 
     | 
    
         
            +
                  598 => ServerTimeoutError,
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  401 => ClientSideError,
         
     | 
| 
      
 13 
     | 
    
         
            +
                  407 => ClientSideError,
         
     | 
| 
      
 14 
     | 
    
         
            +
                  498 => ClientSideError
         
     | 
| 
      
 15 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # Neptune presumably returns message as a JSON string of format
         
     | 
| 
      
 18 
     | 
    
         
            +
                # {"detailedMessage":"",
         
     | 
| 
      
 19 
     | 
    
         
            +
                #  "requestId":"UUID",
         
     | 
| 
      
 20 
     | 
    
         
            +
                #  "code":"ConcurrentModificationException"}
         
     | 
| 
      
 21 
     | 
    
         
            +
                # Currencly we simply search for substings to identify the exact error
         
     | 
| 
      
 22 
     | 
    
         
            +
                # TODO: parse json and use `code` instead
         
     | 
| 
      
 23 
     | 
    
         
            +
                VERTEX_ALREADY_EXISTS = "Vertex with id already exists:"
         
     | 
| 
      
 24 
     | 
    
         
            +
                EDGE_ALREADY_EXISTS = "Edge with id already exists:"
         
     | 
| 
      
 25 
     | 
    
         
            +
                CONCURRENT_VERTEX_INSERT_FAILED = "Failed to complete Insert operation for a Vertex due to conflicting concurrent"
         
     | 
| 
      
 26 
     | 
    
         
            +
                CONCURRENT_EDGE_INSERT_FAILED = "Failed to complete Insert operation for an Edge due to conflicting concurrent"
         
     | 
| 
      
 27 
     | 
    
         
            +
                CONCURRENCT_MODIFICATION_FAILED = "Failed to complete operation due to conflicting concurrent"
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def build(request, response)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    status = response[:status]
         
     | 
| 
      
 32 
     | 
    
         
            +
                    query = request[:request]
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    if (error = ERRORS[status[:code]])
         
     | 
| 
      
 35 
     | 
    
         
            +
                      return (
         
     | 
| 
      
 36 
     | 
    
         
            +
                        already_exists_error(status) ||
         
     | 
| 
      
 37 
     | 
    
         
            +
                        concurrent_modification_error(status) ||
         
     | 
| 
      
 38 
     | 
    
         
            +
                        error
         
     | 
| 
      
 39 
     | 
    
         
            +
                      ).new(status, query)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    return unless RequestDispatcher::SUCCESS[status[:code]].nil?
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    UnknownResponseStatus.new(status)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  def already_exists_error(status)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    return VertexAlreadyExistsError if status[:message]&.include?(VERTEX_ALREADY_EXISTS)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    return EdgeAlreadyExistsError if status[:message]&.include?(EDGE_ALREADY_EXISTS)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  def concurrent_modification_error(status)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    return ConcurrentVertexInsertFailedError if status[:message]&.include?(CONCURRENT_VERTEX_INSERT_FAILED)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    return ConcurrentEdgeInsertFailedError if status[:message]&.include?(CONCURRENT_EDGE_INSERT_FAILED)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    return ConcurrentModificationError if status[:message]&.include?(CONCURRENCT_MODIFICATION_FAILED)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -5,11 +5,12 @@ module Grumlin 
     | 
|
| 
       5 
5 
     | 
    
         
             
                module Properties
         
     | 
| 
       6 
6 
     | 
    
         
             
                  extend Grumlin::Shortcuts
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                  shortcut :props do  
     | 
| 
       9 
     | 
    
         
            -
                    props.reduce(self) do |tt, (prop, value)| 
     | 
| 
       10 
     | 
    
         
            -
                      next tt 
     | 
| 
      
 8 
     | 
    
         
            +
                  shortcut :props do |cardinality = nil, **props|
         
     | 
| 
      
 9 
     | 
    
         
            +
                    props.reduce(self) do |tt, (prop, value)|
         
     | 
| 
      
 10 
     | 
    
         
            +
                      next tt if value.nil? # nils are not supported
         
     | 
| 
      
 11 
     | 
    
         
            +
                      next tt.property(prop, value) if cardinality.nil?
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                      tt
         
     | 
| 
      
 13 
     | 
    
         
            +
                      tt.property(cardinality, prop, value)
         
     | 
| 
       13 
14 
     | 
    
         
             
                    end
         
     | 
| 
       14 
15 
     | 
    
         
             
                  end
         
     | 
| 
       15 
16 
     | 
    
         | 
| 
         @@ -10,8 +10,8 @@ module Grumlin 
     | 
|
| 
       10 
10 
     | 
    
         
             
                        .fold
         
     | 
| 
       11 
11 
     | 
    
         
             
                        .coalesce(
         
     | 
| 
       12 
12 
     | 
    
         
             
                          __.unfold,
         
     | 
| 
       13 
     | 
    
         
            -
                          __.addV(label).props(**create_properties.merge(T.id => id))
         
     | 
| 
       14 
     | 
    
         
            -
                        ).props(**update_properties)
         
     | 
| 
      
 13 
     | 
    
         
            +
                          __.addV(label).props(Cardinality.single, **create_properties.merge(T.id => id))
         
     | 
| 
      
 14 
     | 
    
         
            +
                        ).props(Cardinality.single, **update_properties)
         
     | 
| 
       15 
15 
     | 
    
         
             
                  end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                  shortcut :upsertE do |label, from, to, create_properties = {}, update_properties = {}|
         
     | 
    
        data/lib/grumlin/steppable.rb
    CHANGED
    
    | 
         @@ -4,13 +4,18 @@ module Grumlin 
     | 
|
| 
       4 
4 
     | 
    
         
             
              class Steppable
         
     | 
| 
       5 
5 
     | 
    
         
             
                extend Forwardable
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
                attr_reader :session_id
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       7 
9 
     | 
    
         
             
                START_STEPS = Grumlin.definitions.dig(:steps, :start).map(&:to_sym).freeze
         
     | 
| 
       8 
10 
     | 
    
         
             
                REGULAR_STEPS = Grumlin.definitions.dig(:steps, :regular).map(&:to_sym).freeze
         
     | 
| 
       9 
11 
     | 
    
         
             
                CONFIGURATION_STEPS = Grumlin.definitions.dig(:steps, :configuration).map(&:to_sym).freeze
         
     | 
| 
       10 
12 
     | 
    
         | 
| 
       11 
13 
     | 
    
         
             
                ALL_STEPS = START_STEPS + CONFIGURATION_STEPS + REGULAR_STEPS
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                def initialize
         
     | 
| 
      
 15 
     | 
    
         
            +
                def initialize(pool: Grumlin.default_pool, session_id: nil)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @pool = pool
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @session_id = session_id
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       14 
19 
     | 
    
         
             
                  return if respond_to?(:shortcuts)
         
     | 
| 
       15 
20 
     | 
    
         | 
| 
       16 
21 
     | 
    
         
             
                  raise "steppable must not be initialized directly, use Grumlin::Shortcuts::Storage#g or #__ instead"
         
     | 
| 
         @@ -18,12 +23,12 @@ module Grumlin 
     | 
|
| 
       18 
23 
     | 
    
         | 
| 
       19 
24 
     | 
    
         
             
                ALL_STEPS.each do |step|
         
     | 
| 
       20 
25 
     | 
    
         
             
                  define_method step do |*args, **params|
         
     | 
| 
       21 
     | 
    
         
            -
                    shortcuts.action_class.new(step, args: args, params: params, previous_step: self)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    shortcuts.action_class.new(step, args: args, params: params, previous_step: self, session_id: @session_id)
         
     | 
| 
       22 
27 
     | 
    
         
             
                  end
         
     | 
| 
       23 
28 
     | 
    
         
             
                end
         
     | 
| 
       24 
29 
     | 
    
         | 
| 
       25 
30 
     | 
    
         
             
                def step(name, *args, **params)
         
     | 
| 
       26 
     | 
    
         
            -
                  shortcuts.action_class.new(name, args: args, params: params, previous_step: self)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  shortcuts.action_class.new(name, args: args, params: params, previous_step: self, session_id: @session_id)
         
     | 
| 
       27 
32 
     | 
    
         
             
                end
         
     | 
| 
       28 
33 
     | 
    
         | 
| 
       29 
34 
     | 
    
         
             
                def_delegator :shortcuts, :__
         
     | 
    
        data/lib/grumlin/steps.rb
    CHANGED
    
    | 
         @@ -32,7 +32,9 @@ module Grumlin 
     | 
|
| 
       32 
32 
     | 
    
         
             
                end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                def add(name, args: [], params: {})
         
     | 
| 
       35 
     | 
    
         
            -
                   
     | 
| 
      
 35 
     | 
    
         
            +
                  if CONFIGURATION_STEPS.include?(name) || name.to_sym == :tx
         
     | 
| 
      
 36 
     | 
    
         
            +
                    return add_configuration_step(name, args: args, params: params)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
       36 
38 
     | 
    
         | 
| 
       37 
39 
     | 
    
         
             
                  StepData.new(name, args: cast_arguments(args), params: params).tap do |step|
         
     | 
| 
       38 
40 
     | 
    
         
             
                    @steps << step
         
     | 
| 
         @@ -10,17 +10,19 @@ module Grumlin 
     | 
|
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                  def serialize
         
     | 
| 
       12 
12 
     | 
    
         
             
                    steps = ShortcutsApplyer.call(@steps)
         
     | 
| 
       13 
     | 
    
         
            -
                    no_return = @params 
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                       
     | 
| 
       17 
     | 
    
         
            -
                    }.tap do |v|
         
     | 
| 
       18 
     | 
    
         
            -
                      v.merge!(source: steps.configuration_steps.map { |s| serialize_step(s) }) if steps.configuration_steps.any?
         
     | 
| 
      
 13 
     | 
    
         
            +
                    no_return = @params.fetch(:no_return, false)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    {}.tap do |result|
         
     | 
| 
      
 15 
     | 
    
         
            +
                      result[:step] = serialize_steps(steps.steps + (no_return ? [NONE_STEP] : [])) if steps.steps.any?
         
     | 
| 
      
 16 
     | 
    
         
            +
                      result[:source] = serialize_steps(steps.configuration_steps) if steps.configuration_steps.any?
         
     | 
| 
       19 
17 
     | 
    
         
             
                    end
         
     | 
| 
       20 
18 
     | 
    
         
             
                  end
         
     | 
| 
       21 
19 
     | 
    
         | 
| 
       22 
20 
     | 
    
         
             
                  private
         
     | 
| 
       23 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
                  def serialize_steps(steps)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    steps.map { |s| serialize_step(s) }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
       24 
26 
     | 
    
         
             
                  def serialize_step(step)
         
     | 
| 
       25 
27 
     | 
    
         
             
                    [step.name].tap do |result|
         
     | 
| 
       26 
28 
     | 
    
         
             
                      step.args.each do |arg|
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Grumlin
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Transaction
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :session_id
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                include Console
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                COMMIT = Grumlin::Repository.new.g.step(:tx, :commit).bytecode
         
     | 
| 
      
 10 
     | 
    
         
            +
                ROLLBACK = Grumlin::Repository.new.g.step(:tx, :rollback).bytecode
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(traversal_start_class, pool: Grumlin.default_pool)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @traversal_start_class = traversal_start_class
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @pool = pool
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  @session_id = SecureRandom.uuid
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def begin
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @traversal_start_class.new(session_id: @session_id)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def commit
         
     | 
| 
      
 24 
     | 
    
         
            +
                  finalize(COMMIT)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def rollback
         
     | 
| 
      
 28 
     | 
    
         
            +
                  finalize(ROLLBACK)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                private
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def finalize(action)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @pool.acquire do |client|
         
     | 
| 
      
 35 
     | 
    
         
            +
                    client.write(action, session_id: @session_id)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -4,6 +4,27 @@ module Grumlin 
     | 
|
| 
       4 
4 
     | 
    
         
             
              class TraversalStart < Steppable
         
     | 
| 
       5 
5 
     | 
    
         
             
                include WithExtension
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
                class TraversalError < Grumlin::Error; end
         
     | 
| 
      
 8 
     | 
    
         
            +
                class AlreadyBoundToTransactionError < TraversalError; end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def tx
         
     | 
| 
      
 11 
     | 
    
         
            +
                  raise AlreadyBoundToTransactionError if @session_id
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  transaction = tx_class.new(self.class, pool: @pool)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  return transaction unless block_given?
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 17 
     | 
    
         
            +
                    yield transaction.begin
         
     | 
| 
      
 18 
     | 
    
         
            +
                  rescue Grumlin::Rollback
         
     | 
| 
      
 19 
     | 
    
         
            +
                    transaction.rollback
         
     | 
| 
      
 20 
     | 
    
         
            +
                  rescue StandardError
         
     | 
| 
      
 21 
     | 
    
         
            +
                    transaction.rollback
         
     | 
| 
      
 22 
     | 
    
         
            +
                    raise
         
     | 
| 
      
 23 
     | 
    
         
            +
                  else
         
     | 
| 
      
 24 
     | 
    
         
            +
                    transaction.commit
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       7 
28 
     | 
    
         
             
                def to_s(*)
         
     | 
| 
       8 
29 
     | 
    
         
             
                  self.class.to_s
         
     | 
| 
       9 
30 
     | 
    
         
             
                end
         
     | 
| 
         @@ -11,5 +32,11 @@ module Grumlin 
     | 
|
| 
       11 
32 
     | 
    
         
             
                def inspect
         
     | 
| 
       12 
33 
     | 
    
         
             
                  self.class.inspect
         
     | 
| 
       13 
34 
     | 
    
         
             
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def tx_class
         
     | 
| 
      
 39 
     | 
    
         
            +
                  Grumlin.features.supports_transactions? ? Transaction : DummyTransaction
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
       14 
41 
     | 
    
         
             
              end
         
     | 
| 
       15 
42 
     | 
    
         
             
            end
         
     | 
    
        data/lib/grumlin/version.rb
    CHANGED
    
    
    
        data/lib/grumlin.rb
    CHANGED
    
    | 
         @@ -33,6 +33,9 @@ loader.do_not_eager_load(test_helpers) 
     | 
|
| 
       33 
33 
     | 
    
         
             
            module Grumlin
         
     | 
| 
       34 
34 
     | 
    
         
             
              class Error < StandardError; end
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
      
 36 
     | 
    
         
            +
              class TransactionError < Error; end
         
     | 
| 
      
 37 
     | 
    
         
            +
              class Rollback < TransactionError; end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       36 
39 
     | 
    
         
             
              class UnknownError < Error; end
         
     | 
| 
       37 
40 
     | 
    
         | 
| 
       38 
41 
     | 
    
         
             
              class ConnectionError < Error; end
         
     | 
| 
         @@ -98,7 +101,8 @@ module Grumlin 
     | 
|
| 
       98 
101 
     | 
    
         
             
              class VertexAlreadyExistsError < AlreadyExistsError; end
         
     | 
| 
       99 
102 
     | 
    
         
             
              class EdgeAlreadyExistsError < AlreadyExistsError; end
         
     | 
| 
       100 
103 
     | 
    
         | 
| 
       101 
     | 
    
         
            -
              class  
     | 
| 
      
 104 
     | 
    
         
            +
              class ConcurrentModificationError < ServerError; end
         
     | 
| 
      
 105 
     | 
    
         
            +
              class ConcurrentInsertFailedError < ConcurrentModificationError; end
         
     | 
| 
       102 
106 
     | 
    
         | 
| 
       103 
107 
     | 
    
         
             
              class ConcurrentVertexInsertFailedError < ConcurrentInsertFailedError; end
         
     | 
| 
       104 
108 
     | 
    
         
             
              class ConcurrentEdgeInsertFailedError < ConcurrentInsertFailedError; end
         
     | 
| 
         @@ -127,27 +131,26 @@ module Grumlin 
     | 
|
| 
       127 
131 
     | 
    
         | 
| 
       128 
132 
     | 
    
         
             
              class WrongQueryResult < RepositoryError; end
         
     | 
| 
       129 
133 
     | 
    
         | 
| 
       130 
     | 
    
         
            -
              class Config
         
     | 
| 
       131 
     | 
    
         
            -
                attr_accessor :url, :pool_size, :client_concurrency, :client_factory
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
                def initialize
         
     | 
| 
       134 
     | 
    
         
            -
                  @pool_size = 10
         
     | 
| 
       135 
     | 
    
         
            -
                  @client_concurrency = 5
         
     | 
| 
       136 
     | 
    
         
            -
                  @client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
         
     | 
| 
       137 
     | 
    
         
            -
                end
         
     | 
| 
       138 
     | 
    
         
            -
              end
         
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
134 
     | 
    
         
             
              @pool_mutex = Mutex.new
         
     | 
| 
       141 
135 
     | 
    
         | 
| 
       142 
136 
     | 
    
         
             
              class << self
         
     | 
| 
       143 
137 
     | 
    
         
             
                def configure
         
     | 
| 
       144 
138 
     | 
    
         
             
                  yield config
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                  config.validate!
         
     | 
| 
       145 
141 
     | 
    
         
             
                end
         
     | 
| 
       146 
142 
     | 
    
         | 
| 
       147 
143 
     | 
    
         
             
                def config
         
     | 
| 
       148 
144 
     | 
    
         
             
                  @config ||= Config.new
         
     | 
| 
       149 
145 
     | 
    
         
             
                end
         
     | 
| 
       150 
146 
     | 
    
         | 
| 
      
 147 
     | 
    
         
            +
                # returns a subset of features for currently configured backend.
         
     | 
| 
      
 148 
     | 
    
         
            +
                # The features lists are hardcoded as there is no way to get them
         
     | 
| 
      
 149 
     | 
    
         
            +
                # from the remote server.
         
     | 
| 
      
 150 
     | 
    
         
            +
                def features
         
     | 
| 
      
 151 
     | 
    
         
            +
                  Features.for(config.provider) # no memoization as provider may be changed
         
     | 
| 
      
 152 
     | 
    
         
            +
                end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
       151 
154 
     | 
    
         
             
                def default_pool
         
     | 
| 
       152 
155 
     | 
    
         
             
                  if Thread.current.thread_variable_get(:grumlin_default_pool)
         
     | 
| 
       153 
156 
     | 
    
         
             
                    return Thread.current.thread_variable_get(:grumlin_default_pool)
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: grumlin
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.22.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Gleb Sinyavskiy
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2022- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-08-12 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: async-pool
         
     | 
| 
         @@ -96,7 +96,6 @@ files: 
     | 
|
| 
       96 
96 
     | 
    
         
             
            - ".rspec"
         
     | 
| 
       97 
97 
     | 
    
         
             
            - ".rubocop.yml"
         
     | 
| 
       98 
98 
     | 
    
         
             
            - ".tool-versions"
         
     | 
| 
       99 
     | 
    
         
            -
            - CHANGELOG.md
         
     | 
| 
       100 
99 
     | 
    
         
             
            - CODE_OF_CONDUCT.md
         
     | 
| 
       101 
100 
     | 
    
         
             
            - Gemfile
         
     | 
| 
       102 
101 
     | 
    
         
             
            - Gemfile.lock
         
     | 
| 
         @@ -117,6 +116,8 @@ files: 
     | 
|
| 
       117 
116 
     | 
    
         
             
            - lib/grumlin/action.rb
         
     | 
| 
       118 
117 
     | 
    
         
             
            - lib/grumlin/benchmark/repository.rb
         
     | 
| 
       119 
118 
     | 
    
         
             
            - lib/grumlin/client.rb
         
     | 
| 
      
 119 
     | 
    
         
            +
            - lib/grumlin/config.rb
         
     | 
| 
      
 120 
     | 
    
         
            +
            - lib/grumlin/dummy_transaction.rb
         
     | 
| 
       120 
121 
     | 
    
         
             
            - lib/grumlin/edge.rb
         
     | 
| 
       121 
122 
     | 
    
         
             
            - lib/grumlin/expressions/cardinality.rb
         
     | 
| 
       122 
123 
     | 
    
         
             
            - lib/grumlin/expressions/column.rb
         
     | 
| 
         @@ -129,12 +130,17 @@ files: 
     | 
|
| 
       129 
130 
     | 
    
         
             
            - lib/grumlin/expressions/t.rb
         
     | 
| 
       130 
131 
     | 
    
         
             
            - lib/grumlin/expressions/text_p.rb
         
     | 
| 
       131 
132 
     | 
    
         
             
            - lib/grumlin/expressions/with_options.rb
         
     | 
| 
      
 133 
     | 
    
         
            +
            - lib/grumlin/features.rb
         
     | 
| 
      
 134 
     | 
    
         
            +
            - lib/grumlin/features/feature_list.rb
         
     | 
| 
      
 135 
     | 
    
         
            +
            - lib/grumlin/features/neptune_features.rb
         
     | 
| 
      
 136 
     | 
    
         
            +
            - lib/grumlin/features/tinkergraph_features.rb
         
     | 
| 
       132 
137 
     | 
    
         
             
            - lib/grumlin/path.rb
         
     | 
| 
       133 
138 
     | 
    
         
             
            - lib/grumlin/property.rb
         
     | 
| 
       134 
139 
     | 
    
         
             
            - lib/grumlin/repository.rb
         
     | 
| 
       135 
140 
     | 
    
         
             
            - lib/grumlin/repository/error_handling_strategy.rb
         
     | 
| 
       136 
141 
     | 
    
         
             
            - lib/grumlin/repository/instance_methods.rb
         
     | 
| 
       137 
142 
     | 
    
         
             
            - lib/grumlin/request_dispatcher.rb
         
     | 
| 
      
 143 
     | 
    
         
            +
            - lib/grumlin/request_error_factory.rb
         
     | 
| 
       138 
144 
     | 
    
         
             
            - lib/grumlin/shortcut.rb
         
     | 
| 
       139 
145 
     | 
    
         
             
            - lib/grumlin/shortcuts.rb
         
     | 
| 
       140 
146 
     | 
    
         
             
            - lib/grumlin/shortcuts/properties.rb
         
     | 
| 
         @@ -152,6 +158,7 @@ files: 
     | 
|
| 
       152 
158 
     | 
    
         
             
            - lib/grumlin/test/rspec.rb
         
     | 
| 
       153 
159 
     | 
    
         
             
            - lib/grumlin/test/rspec/db_cleaner_context.rb
         
     | 
| 
       154 
160 
     | 
    
         
             
            - lib/grumlin/test/rspec/gremlin_context.rb
         
     | 
| 
      
 161 
     | 
    
         
            +
            - lib/grumlin/transaction.rb
         
     | 
| 
       155 
162 
     | 
    
         
             
            - lib/grumlin/transport.rb
         
     | 
| 
       156 
163 
     | 
    
         
             
            - lib/grumlin/traversal_start.rb
         
     | 
| 
       157 
164 
     | 
    
         
             
            - lib/grumlin/traversal_strategies/options_strategy.rb
         
     | 
| 
         @@ -168,7 +175,7 @@ licenses: 
     | 
|
| 
       168 
175 
     | 
    
         
             
            metadata:
         
     | 
| 
       169 
176 
     | 
    
         
             
              homepage_uri: https://github.com/zhulik/grumlin
         
     | 
| 
       170 
177 
     | 
    
         
             
              source_code_uri: https://github.com/zhulik/grumlin
         
     | 
| 
       171 
     | 
    
         
            -
              changelog_uri: https://github.com/ 
     | 
| 
      
 178 
     | 
    
         
            +
              changelog_uri: https://github.com/babbel/grumlin/releases
         
     | 
| 
       172 
179 
     | 
    
         
             
              rubygems_mfa_required: 'true'
         
     | 
| 
       173 
180 
     | 
    
         
             
            post_install_message: 
         
     | 
| 
       174 
181 
     | 
    
         
             
            rdoc_options: []
         
     | 
    
        data/CHANGELOG.md
    DELETED
    
    | 
         @@ -1,71 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            ## [0.16.0] - 2022-03-11
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            - Query building is rewritten from scratch. No public APIs were changed. [Details](https://github.com/babbel/grumlin/pull/64)
         
     | 
| 
       4 
     | 
    
         
            -
            - Add support for [TextP](https://tinkerpop.apache.org/javadocs/current/core/org/apache/tinkerpop/gremlin/process/traversal/TextP.html)
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
            ## [0.15.4] - 2022-01-20
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
            - Move step and expression definitions to a yaml file for better diffs
         
     | 
| 
       9 
     | 
    
         
            -
            - Add `definitions:format` rake task
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
            ## [0.15.3] - 2022-01-18
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
            - Fix passing nils as step arguments. Even if they are not supported by the server, they should not be omitted.
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
            ## [0.15.2] - 2022-01-17
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
            - New steps: `map` and `identity`
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
            ## [0.15.1] - 2022-01-17
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
            - Fix passing arrays as step arguments
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
            ## [0.15.0] - 2022-01-11
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
            - Add `properties` step
         
     | 
| 
       26 
     | 
    
         
            -
            - Add proper support for bulked results
         
     | 
| 
       27 
     | 
    
         
            -
            - Add support for `Property` objects
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
            ## [0.14.5] - 2021-12-27
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
            - Fix params handling
         
     | 
| 
       32 
     | 
    
         
            -
            - Add `aggregate` step
         
     | 
| 
       33 
     | 
    
         
            -
            - Add `Order.shuffle`
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
            ## [0.14.4] - 2021-12-17
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
            - `Grumlin::Repository.shorcuts_from` do not raise `ArgumentError` when importing an already existing shortcut
         
     | 
| 
       38 
     | 
    
         
            -
              pointing to the same block. This fixes importing shortcuts from another repository.
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
            ## [0.14.2] - 2021-12-13
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
            - Fix `Module` bloating
         
     | 
| 
       43 
     | 
    
         
            -
            - Add `Operator` expressions
         
     | 
| 
       44 
     | 
    
         
            -
            - Add `__.coalesce` and `__.constant`
         
     | 
| 
       45 
     | 
    
         
            -
            - Add steps: `sum`, `sack`
         
     | 
| 
       46 
     | 
    
         
            -
            - Add configuration steps: `withSack`
         
     | 
| 
       47 
     | 
    
         
            -
            - Rename `Grumlin::Expressions::Tool` to `Grumlin::Expressions::Expression`
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
            ## [0.14.2] - 2021-12-12
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
            - Better exceptions
         
     | 
| 
       53 
     | 
    
         
            -
            - Add `choose` step
         
     | 
| 
       54 
     | 
    
         
            -
            - Add `__.hasNot`, `__.is`, `__.select`
         
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
            ## [0.14.0] - 2021-12-07
         
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
            - Add initial support for [configuration steps](https://tinkerpop.apache.org/docs/current/reference/#configuration-steps)
         
     | 
| 
       59 
     | 
    
         
            -
            - Add the `withSideEffect` configuration step
         
     | 
| 
       60 
     | 
    
         
            -
            - Fix passing keyword arguments to regular steps
         
     | 
| 
       61 
     | 
    
         
            -
            - *Drop support for ruby 2.6*
         
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
            ## [0.13.0] - 2021-12-03
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
            - Add `Shortcuts` and `Repository`
         
     | 
| 
       66 
     | 
    
         
            -
            - Allow executing any gremlin steps by name using `Grumlin::AnonymousStep#step`
         
     | 
| 
       67 
     | 
    
         
            -
            - Rename `Grumlin::Tools` to `Grumlin::Expressions`
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
            ## [0.1.0] - 2021-05-25
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
            - Initial release
         
     |