snapbot 0.2.1 → 0.4.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/.rubocop.yml +7 -0
 - data/Gemfile +2 -0
 - data/README.md +36 -5
 - data/Steepfile +1 -1
 - data/docs/img/models-with-lets.svg +96 -0
 - data/docs/img/models.svg +94 -0
 - data/lib/snapbot/diagram/dot_generator.rb +13 -9
 - data/lib/snapbot/diagram/renderer.rb +7 -7
 - data/lib/snapbot/diagram.rb +16 -10
 - data/lib/snapbot/version.rb +1 -1
 - data/sig/lib/snapbot/diagram/dot_generator.rbs +1 -1
 - data/sig/lib/snapbot/diagram/renderer.rbs +4 -3
 - data/sig/lib/snapbot/diagram.rbs +2 -1
 - data/snapbot.gemspec +1 -0
 - metadata +18 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: fd8932c76924b2ce01335b13174915694258dcd419d5c7da3fe5daa45a7e66c7
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 9aa4282ef7e47c1789b742f28a977c8ea387d526ed0a8b4d524b6e205120d4f1
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 5c3e26e2eedb55db8876619da0c63fb79754b8c584d3ed1cf6599fbc37d8f8174af90f706bf6c5ee34e88e60a4482ab57b438613c04e426992a0f8ab80b13499
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 3153f489921edecb2ca66bb7781911d0ff13e3ce563749773e649cb5a8c1c6229cc67f89cfbdcb96e17e9270cfcdc65ea2a4af7aa10e2cec8f29f6977e397357
         
     | 
    
        data/.rubocop.yml
    CHANGED
    
    | 
         @@ -1,9 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            AllCops:
         
     | 
| 
       2 
2 
     | 
    
         
             
              TargetRubyVersion: 2.6
         
     | 
| 
       3 
3 
     | 
    
         
             
              SuggestExtensions: false
         
     | 
| 
      
 4 
     | 
    
         
            +
              NewCops: enable
         
     | 
| 
       4 
5 
     | 
    
         
             
              Exclude:
         
     | 
| 
       5 
6 
     | 
    
         
             
                - Steepfile
         
     | 
| 
      
 7 
     | 
    
         
            +
                - vendor/bundle/**/*
         
     | 
| 
       6 
8 
     | 
    
         | 
| 
      
 9 
     | 
    
         
            +
            Gemspec/RequireMFA:
         
     | 
| 
      
 10 
     | 
    
         
            +
              Enabled: false
         
     | 
| 
       7 
11 
     | 
    
         | 
| 
       8 
12 
     | 
    
         
             
            Metrics/AbcSize:
         
     | 
| 
       9 
13 
     | 
    
         
             
              Exclude:
         
     | 
| 
         @@ -18,6 +22,9 @@ Metrics/MethodLength: 
     | 
|
| 
       18 
22 
     | 
    
         
             
              Exclude:
         
     | 
| 
       19 
23 
     | 
    
         
             
                - spec/support/fixture_database.rb
         
     | 
| 
       20 
24 
     | 
    
         | 
| 
      
 25 
     | 
    
         
            +
            Style/DoubleNegation:
         
     | 
| 
      
 26 
     | 
    
         
            +
              Enabled: false
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       21 
28 
     | 
    
         
             
            Style/StderrPuts:
         
     | 
| 
       22 
29 
     | 
    
         
             
              Exclude:
         
     | 
| 
       23 
30 
     | 
    
         
             
                - lib/snapbot/diagram/renderer.rb
         
     | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -6,10 +6,13 @@ Snapbot generates little diagrams via `save_and_open_diagram` for you to visuali 
     | 
|
| 
       6 
6 
     | 
    
         
             
            ActiveRecord objects that you find in feature and integration tests. These are most often made by
         
     | 
| 
       7 
7 
     | 
    
         
             
             [FactoryBot](https://github.com/thoughtbot/factory_bot) or some other fixture-handling method, but this gem has no 
         
     | 
| 
       8 
8 
     | 
    
         
             
             opinions on those (beyond namechecking).
         
     | 
| 
      
 9 
     | 
    
         
            +
             
         
     | 
| 
      
 10 
     | 
    
         
            +
            
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
       10 
12 
     | 
    
         
             
            ## Installation
         
     | 
| 
       11 
13 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
            Snapbot requires [Graphviz](https://graphviz.org/download/#executable-packages), and cannot function without it.
         
     | 
| 
      
 15 
     | 
    
         
            +
            Install this first, then add the gem to your project's `:test` group in the gemfile:
         
     | 
| 
       13 
16 
     | 
    
         | 
| 
       14 
17 
     | 
    
         
             
            ```ruby 
         
     | 
| 
       15 
18 
     | 
    
         
             
              group :test do 
         
     | 
| 
         @@ -17,13 +20,19 @@ Either `gem install snapbot` or add the gem to your project's `:test` group in t 
     | 
|
| 
       17 
20 
     | 
    
         
             
              end 
         
     | 
| 
       18 
21 
     | 
    
         
             
            ```
         
     | 
| 
       19 
22 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
            `include Snapbot::Diagram` in your tests.
         
     | 
| 
       21 
24 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
      
 25 
     | 
    
         
            +
            For RSpec, you may prefer to put something like
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 28 
     | 
    
         
            +
              RSpec.config do |config|
         
     | 
| 
      
 29 
     | 
    
         
            +
                config.include Snapbot::Diagram, type: :feature
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
       24 
31 
     | 
    
         
             
            ```
         
     | 
| 
       25 
32 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 33 
     | 
    
         
            +
            in your `spec/rails_helper` to have it mixed in automatically (to features, in this case).
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            ## Usage example
         
     | 
| 
       27 
36 
     | 
    
         | 
| 
       28 
37 
     | 
    
         
             
            ```
         
     | 
| 
       29 
38 
     | 
    
         
             
              blog = Blog.create(title: 'My blog')
         
     | 
| 
         @@ -32,6 +41,28 @@ Use: 
     | 
|
| 
       32 
41 
     | 
    
         
             
              save_and_open_diagram
         
     | 
| 
       33 
42 
     | 
    
         
             
            ```
         
     | 
| 
       34 
43 
     | 
    
         | 
| 
      
 44 
     | 
    
         
            +
            ## RSpec integration
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            Sometimes it's useful to see the models you created as preconditions vs the ones your code has created in response to
         
     | 
| 
      
 47 
     | 
    
         
            +
            those. If you use RSpec's [`let` construct](https://relishapp.com/rspec/rspec-core/v/3-11/docs/helper-methods/let-and-let)
         
     | 
| 
      
 48 
     | 
    
         
            +
            then Snapbot will match these automatically for you and annotate the diagram. For example:
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 51 
     | 
    
         
            +
              include Snapbot::Diagram
         
     | 
| 
      
 52 
     | 
    
         
            +
              describe "the categorised post creator" do
         
     | 
| 
      
 53 
     | 
    
         
            +
                let(:blog)   { create :blog }
         
     | 
| 
      
 54 
     | 
    
         
            +
                let(:author) { create :author }
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                it "creates posts automatically, categorised" do
         
     | 
| 
      
 57 
     | 
    
         
            +
                  CategorisedPostCreator.new(blog, author).run
         
     | 
| 
      
 58 
     | 
    
         
            +
                  save_and_open_diagram
         
     | 
| 
      
 59 
     | 
    
         
            +
                  expect(Post.count).to eql(2)
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
            ```
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
       35 
66 
     | 
    
         
             
            ## Why?
         
     | 
| 
       36 
67 
     | 
    
         | 
| 
       37 
68 
     | 
    
         
             
            Sometimes, you need to create a few ActiveRecord objects for your test suite. Sometimes, there will be a little cluster
         
     | 
    
        data/Steepfile
    CHANGED
    
    | 
         @@ -26,7 +26,7 @@ target :lib do 
     | 
|
| 
       26 
26 
     | 
    
         
             
                # lib/snapbot/diagram/renderer.rb:21:58: [error] The method cannot be called with a block
         
     | 
| 
       27 
27 
     | 
    
         
             
                # │ Diagnostic ID: Ruby::UnexpectedBlockGiven
         
     | 
| 
       28 
28 
     | 
    
         
             
                # │
         
     | 
| 
       29 
     | 
    
         
            -
                # └         IO.popen("dot -Tsvg -o #{ 
     | 
| 
      
 29 
     | 
    
         
            +
                # └         IO.popen("dot -Tsvg -o #{DEFAULT_OUTPUT_FILENAME}", "w+") do |pipe|
         
     | 
| 
       30 
30 
     | 
    
         
             
                #   ~~~~~~~~~
         
     | 
| 
       31 
31 
     | 
    
         
             
                #
         
     | 
| 
       32 
32 
     | 
    
         
             
                # This disables that ^^ but probably a bit too much. Can we restrict to renderer.rb?
         
     | 
| 
         @@ -0,0 +1,96 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <?xml version="1.0" encoding="UTF-8" standalone="no"?>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
         
     | 
| 
      
 3 
     | 
    
         
            +
             "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
         
     | 
| 
      
 4 
     | 
    
         
            +
            <!-- Generated by graphviz version 3.0.0 (20220226.1711)
         
     | 
| 
      
 5 
     | 
    
         
            +
             -->
         
     | 
| 
      
 6 
     | 
    
         
            +
            <!-- Title: g Pages: 1 -->
         
     | 
| 
      
 7 
     | 
    
         
            +
            <svg width="322pt" height="247pt"
         
     | 
| 
      
 8 
     | 
    
         
            +
             viewBox="0.00 0.00 322.10 246.60" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
         
     | 
| 
      
 9 
     | 
    
         
            +
            <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(28.8 217.8)">
         
     | 
| 
      
 10 
     | 
    
         
            +
            <title>g</title>
         
     | 
| 
      
 11 
     | 
    
         
            +
            <polygon fill="white" stroke="transparent" points="-28.8,28.8 -28.8,-217.8 293.3,-217.8 293.3,28.8 -28.8,28.8"/>
         
     | 
| 
      
 12 
     | 
    
         
            +
            <!-- Category#1 -->
         
     | 
| 
      
 13 
     | 
    
         
            +
            <g id="node1" class="node">
         
     | 
| 
      
 14 
     | 
    
         
            +
            <title>Category#1</title>
         
     | 
| 
      
 15 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M12,-149.5C12,-149.5 57,-149.5 57,-149.5 63,-149.5 69,-155.5 69,-161.5 69,-161.5 69,-173.5 69,-173.5 69,-179.5 63,-185.5 57,-185.5 57,-185.5 12,-185.5 12,-185.5 6,-185.5 0,-179.5 0,-173.5 0,-173.5 0,-161.5 0,-161.5 0,-155.5 6,-149.5 12,-149.5"/>
         
     | 
| 
      
 16 
     | 
    
         
            +
            <text text-anchor="start" x="9.5" y="-164.5" font-family="ArialMT" font-size="10.00">Category#1</text>
         
     | 
| 
      
 17 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 18 
     | 
    
         
            +
            <!-- Post#1 -->
         
     | 
| 
      
 19 
     | 
    
         
            +
            <g id="node4" class="node">
         
     | 
| 
      
 20 
     | 
    
         
            +
            <title>Post#1</title>
         
     | 
| 
      
 21 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M77.5,-73.5C77.5,-73.5 107.5,-73.5 107.5,-73.5 113.5,-73.5 119.5,-79.5 119.5,-85.5 119.5,-85.5 119.5,-97.5 119.5,-97.5 119.5,-103.5 113.5,-109.5 107.5,-109.5 107.5,-109.5 77.5,-109.5 77.5,-109.5 71.5,-109.5 65.5,-103.5 65.5,-97.5 65.5,-97.5 65.5,-85.5 65.5,-85.5 65.5,-79.5 71.5,-73.5 77.5,-73.5"/>
         
     | 
| 
      
 22 
     | 
    
         
            +
            <text text-anchor="start" x="77.5" y="-88.5" font-family="ArialMT" font-size="10.00">Post#1</text>
         
     | 
| 
      
 23 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 24 
     | 
    
         
            +
            <!-- Category#1->Post#1 -->
         
     | 
| 
      
 25 
     | 
    
         
            +
            <g id="edge1" class="edge">
         
     | 
| 
      
 26 
     | 
    
         
            +
            <title>Category#1->Post#1</title>
         
     | 
| 
      
 27 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M53.51,-142.25C59.92,-134.07 67.07,-124.94 73.48,-116.76"/>
         
     | 
| 
      
 28 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="51.02,-140.31 47.95,-149.34 55.98,-144.2 51.02,-140.31"/>
         
     | 
| 
      
 29 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="76.05,-118.6 79.12,-109.57 71.09,-114.71 76.05,-118.6"/>
         
     | 
| 
      
 30 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 31 
     | 
    
         
            +
            <!-- Post#2 -->
         
     | 
| 
      
 32 
     | 
    
         
            +
            <g id="node5" class="node">
         
     | 
| 
      
 33 
     | 
    
         
            +
            <title>Post#2</title>
         
     | 
| 
      
 34 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M160.5,-73.5C160.5,-73.5 190.5,-73.5 190.5,-73.5 196.5,-73.5 202.5,-79.5 202.5,-85.5 202.5,-85.5 202.5,-97.5 202.5,-97.5 202.5,-103.5 196.5,-109.5 190.5,-109.5 190.5,-109.5 160.5,-109.5 160.5,-109.5 154.5,-109.5 148.5,-103.5 148.5,-97.5 148.5,-97.5 148.5,-85.5 148.5,-85.5 148.5,-79.5 154.5,-73.5 160.5,-73.5"/>
         
     | 
| 
      
 35 
     | 
    
         
            +
            <text text-anchor="start" x="160.5" y="-88.5" font-family="ArialMT" font-size="10.00">Post#2</text>
         
     | 
| 
      
 36 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 37 
     | 
    
         
            +
            <!-- Category#1->Post#2 -->
         
     | 
| 
      
 38 
     | 
    
         
            +
            <g id="edge2" class="edge">
         
     | 
| 
      
 39 
     | 
    
         
            +
            <title>Category#1->Post#2</title>
         
     | 
| 
      
 40 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M75.24,-145.12C95.75,-134.36 120.36,-121.44 140.07,-111.1"/>
         
     | 
| 
      
 41 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="73.71,-142.37 67.2,-149.34 76.64,-147.94 73.71,-142.37"/>
         
     | 
| 
      
 42 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="141.68,-113.81 148.19,-106.83 138.76,-108.23 141.68,-113.81"/>
         
     | 
| 
      
 43 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 44 
     | 
    
         
            +
            <!-- Category#2 -->
         
     | 
| 
      
 45 
     | 
    
         
            +
            <g id="node2" class="node">
         
     | 
| 
      
 46 
     | 
    
         
            +
            <title>Category#2</title>
         
     | 
| 
      
 47 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M153,-0.5C153,-0.5 198,-0.5 198,-0.5 204,-0.5 210,-6.5 210,-12.5 210,-12.5 210,-24.5 210,-24.5 210,-30.5 204,-36.5 198,-36.5 198,-36.5 153,-36.5 153,-36.5 147,-36.5 141,-30.5 141,-24.5 141,-24.5 141,-12.5 141,-12.5 141,-6.5 147,-0.5 153,-0.5"/>
         
     | 
| 
      
 48 
     | 
    
         
            +
            <text text-anchor="start" x="150.5" y="-15.5" font-family="ArialMT" font-size="10.00">Category#2</text>
         
     | 
| 
      
 49 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 50 
     | 
    
         
            +
            <!-- Author#1 -->
         
     | 
| 
      
 51 
     | 
    
         
            +
            <g id="node3" class="node">
         
     | 
| 
      
 52 
     | 
    
         
            +
            <title>Author#1</title>
         
     | 
| 
      
 53 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M110,-146.5C110,-146.5 159,-146.5 159,-146.5 165,-146.5 171,-152.5 171,-158.5 171,-158.5 171,-176.5 171,-176.5 171,-182.5 165,-188.5 159,-188.5 159,-188.5 110,-188.5 110,-188.5 104,-188.5 98,-182.5 98,-176.5 98,-176.5 98,-158.5 98,-158.5 98,-152.5 104,-146.5 110,-146.5"/>
         
     | 
| 
      
 54 
     | 
    
         
            +
            <text text-anchor="start" x="107.5" y="-174.1" font-family="Monaco" font-size="8.00">let(:author)</text>
         
     | 
| 
      
 55 
     | 
    
         
            +
            <text text-anchor="start" x="115" y="-157.5" font-family="ArialMT" font-size="10.00">Author#1</text>
         
     | 
| 
      
 56 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 57 
     | 
    
         
            +
            <!-- Author#1->Post#1 -->
         
     | 
| 
      
 58 
     | 
    
         
            +
            <g id="edge4" class="edge">
         
     | 
| 
      
 59 
     | 
    
         
            +
            <title>Author#1->Post#1</title>
         
     | 
| 
      
 60 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M118.56,-138.42C113.08,-128.76 107.1,-118.22 102.25,-109.69"/>
         
     | 
| 
      
 61 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="115.84,-140.01 123.02,-146.28 121.32,-136.9 115.84,-140.01"/>
         
     | 
| 
      
 62 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 63 
     | 
    
         
            +
            <!-- Author#1->Post#2 -->
         
     | 
| 
      
 64 
     | 
    
         
            +
            <g id="edge5" class="edge">
         
     | 
| 
      
 65 
     | 
    
         
            +
            <title>Author#1->Post#2</title>
         
     | 
| 
      
 66 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M150.22,-138.13C155.53,-128.55 161.3,-118.14 165.98,-109.69"/>
         
     | 
| 
      
 67 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="147.31,-136.88 145.7,-146.28 152.82,-139.93 147.31,-136.88"/>
         
     | 
| 
      
 68 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 69 
     | 
    
         
            +
            <!-- Post#2->Category#2 -->
         
     | 
| 
      
 70 
     | 
    
         
            +
            <g id="edge8" class="edge">
         
     | 
| 
      
 71 
     | 
    
         
            +
            <title>Post#2->Category#2</title>
         
     | 
| 
      
 72 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M175.5,-64.13C175.5,-58.07 175.5,-51.64 175.5,-45.59"/>
         
     | 
| 
      
 73 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="172.35,-64.31 175.5,-73.31 178.65,-64.31 172.35,-64.31"/>
         
     | 
| 
      
 74 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="178.65,-45.53 175.5,-36.53 172.35,-45.53 178.65,-45.53"/>
         
     | 
| 
      
 75 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 76 
     | 
    
         
            +
            <!-- Blog#1 -->
         
     | 
| 
      
 77 
     | 
    
         
            +
            <g id="node6" class="node">
         
     | 
| 
      
 78 
     | 
    
         
            +
            <title>Blog#1</title>
         
     | 
| 
      
 79 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M212.5,-146.5C212.5,-146.5 252.5,-146.5 252.5,-146.5 258.5,-146.5 264.5,-152.5 264.5,-158.5 264.5,-158.5 264.5,-176.5 264.5,-176.5 264.5,-182.5 258.5,-188.5 252.5,-188.5 252.5,-188.5 212.5,-188.5 212.5,-188.5 206.5,-188.5 200.5,-182.5 200.5,-176.5 200.5,-176.5 200.5,-158.5 200.5,-158.5 200.5,-152.5 206.5,-146.5 212.5,-146.5"/>
         
     | 
| 
      
 80 
     | 
    
         
            +
            <text text-anchor="start" x="209.5" y="-174.1" font-family="Monaco" font-size="8.00">let(:blog)</text>
         
     | 
| 
      
 81 
     | 
    
         
            +
            <text text-anchor="start" x="217" y="-157.5" font-family="ArialMT" font-size="10.00">Blog#1</text>
         
     | 
| 
      
 82 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 83 
     | 
    
         
            +
            <!-- Blog#1->Post#1 -->
         
     | 
| 
      
 84 
     | 
    
         
            +
            <g id="edge9" class="edge">
         
     | 
| 
      
 85 
     | 
    
         
            +
            <title>Blog#1->Post#1</title>
         
     | 
| 
      
 86 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M192.4,-145.3C169.12,-133 140.41,-117.83 119.75,-106.91"/>
         
     | 
| 
      
 87 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="190.95,-148.1 200.38,-149.52 193.9,-142.53 190.95,-148.1"/>
         
     | 
| 
      
 88 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 89 
     | 
    
         
            +
            <!-- Blog#1->Post#2 -->
         
     | 
| 
      
 90 
     | 
    
         
            +
            <g id="edge10" class="edge">
         
     | 
| 
      
 91 
     | 
    
         
            +
            <title>Blog#1->Post#2</title>
         
     | 
| 
      
 92 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M211.32,-139C203.75,-129.18 195.45,-118.39 188.74,-109.69"/>
         
     | 
| 
      
 93 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="208.94,-141.07 216.92,-146.28 213.93,-137.23 208.94,-141.07"/>
         
     | 
| 
      
 94 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 95 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 96 
     | 
    
         
            +
            </svg>
         
     | 
    
        data/docs/img/models.svg
    ADDED
    
    | 
         @@ -0,0 +1,94 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <?xml version="1.0" encoding="UTF-8" standalone="no"?>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
         
     | 
| 
      
 3 
     | 
    
         
            +
             "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
         
     | 
| 
      
 4 
     | 
    
         
            +
            <!-- Generated by graphviz version 3.0.0 (20220226.1711)
         
     | 
| 
      
 5 
     | 
    
         
            +
             -->
         
     | 
| 
      
 6 
     | 
    
         
            +
            <!-- Title: g Pages: 1 -->
         
     | 
| 
      
 7 
     | 
    
         
            +
            <svg width="297pt" height="241pt"
         
     | 
| 
      
 8 
     | 
    
         
            +
             viewBox="0.00 0.00 297.10 240.60" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
         
     | 
| 
      
 9 
     | 
    
         
            +
            <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(28.8 211.8)">
         
     | 
| 
      
 10 
     | 
    
         
            +
            <title>g</title>
         
     | 
| 
      
 11 
     | 
    
         
            +
            <polygon fill="white" stroke="transparent" points="-28.8,28.8 -28.8,-211.8 268.3,-211.8 268.3,28.8 -28.8,28.8"/>
         
     | 
| 
      
 12 
     | 
    
         
            +
            <!-- Category#1 -->
         
     | 
| 
      
 13 
     | 
    
         
            +
            <g id="node1" class="node">
         
     | 
| 
      
 14 
     | 
    
         
            +
            <title>Category#1</title>
         
     | 
| 
      
 15 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M12,-146.5C12,-146.5 57,-146.5 57,-146.5 63,-146.5 69,-152.5 69,-158.5 69,-158.5 69,-170.5 69,-170.5 69,-176.5 63,-182.5 57,-182.5 57,-182.5 12,-182.5 12,-182.5 6,-182.5 0,-176.5 0,-170.5 0,-170.5 0,-158.5 0,-158.5 0,-152.5 6,-146.5 12,-146.5"/>
         
     | 
| 
      
 16 
     | 
    
         
            +
            <text text-anchor="start" x="9.5" y="-161.5" font-family="ArialMT" font-size="10.00">Category#1</text>
         
     | 
| 
      
 17 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 18 
     | 
    
         
            +
            <!-- Post#1 -->
         
     | 
| 
      
 19 
     | 
    
         
            +
            <g id="node4" class="node">
         
     | 
| 
      
 20 
     | 
    
         
            +
            <title>Post#1</title>
         
     | 
| 
      
 21 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M70.5,-73.5C70.5,-73.5 100.5,-73.5 100.5,-73.5 106.5,-73.5 112.5,-79.5 112.5,-85.5 112.5,-85.5 112.5,-97.5 112.5,-97.5 112.5,-103.5 106.5,-109.5 100.5,-109.5 100.5,-109.5 70.5,-109.5 70.5,-109.5 64.5,-109.5 58.5,-103.5 58.5,-97.5 58.5,-97.5 58.5,-85.5 58.5,-85.5 58.5,-79.5 64.5,-73.5 70.5,-73.5"/>
         
     | 
| 
      
 22 
     | 
    
         
            +
            <text text-anchor="start" x="70.5" y="-88.5" font-family="ArialMT" font-size="10.00">Post#1</text>
         
     | 
| 
      
 23 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 24 
     | 
    
         
            +
            <!-- Category#1->Post#1 -->
         
     | 
| 
      
 25 
     | 
    
         
            +
            <g id="edge1" class="edge">
         
     | 
| 
      
 26 
     | 
    
         
            +
            <title>Category#1->Post#1</title>
         
     | 
| 
      
 27 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M52.19,-138.87C57.3,-131.76 62.87,-124 67.97,-116.91"/>
         
     | 
| 
      
 28 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="49.54,-137.17 46.85,-146.31 54.65,-140.84 49.54,-137.17"/>
         
     | 
| 
      
 29 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="70.58,-118.68 73.27,-109.53 65.46,-115 70.58,-118.68"/>
         
     | 
| 
      
 30 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 31 
     | 
    
         
            +
            <!-- Post#2 -->
         
     | 
| 
      
 32 
     | 
    
         
            +
            <g id="node5" class="node">
         
     | 
| 
      
 33 
     | 
    
         
            +
            <title>Post#2</title>
         
     | 
| 
      
 34 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M153.5,-73.5C153.5,-73.5 183.5,-73.5 183.5,-73.5 189.5,-73.5 195.5,-79.5 195.5,-85.5 195.5,-85.5 195.5,-97.5 195.5,-97.5 195.5,-103.5 189.5,-109.5 183.5,-109.5 183.5,-109.5 153.5,-109.5 153.5,-109.5 147.5,-109.5 141.5,-103.5 141.5,-97.5 141.5,-97.5 141.5,-85.5 141.5,-85.5 141.5,-79.5 147.5,-73.5 153.5,-73.5"/>
         
     | 
| 
      
 35 
     | 
    
         
            +
            <text text-anchor="start" x="153.5" y="-88.5" font-family="ArialMT" font-size="10.00">Post#2</text>
         
     | 
| 
      
 36 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 37 
     | 
    
         
            +
            <!-- Category#1->Post#2 -->
         
     | 
| 
      
 38 
     | 
    
         
            +
            <g id="edge2" class="edge">
         
     | 
| 
      
 39 
     | 
    
         
            +
            <title>Category#1->Post#2</title>
         
     | 
| 
      
 40 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M74.6,-142.26C93.24,-132.38 115.22,-120.73 133.22,-111.19"/>
         
     | 
| 
      
 41 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="73.07,-139.5 66.6,-146.49 76.02,-145.06 73.07,-139.5"/>
         
     | 
| 
      
 42 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="134.71,-113.97 141.19,-106.97 131.76,-108.4 134.71,-113.97"/>
         
     | 
| 
      
 43 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 44 
     | 
    
         
            +
            <!-- Category#2 -->
         
     | 
| 
      
 45 
     | 
    
         
            +
            <g id="node2" class="node">
         
     | 
| 
      
 46 
     | 
    
         
            +
            <title>Category#2</title>
         
     | 
| 
      
 47 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M146,-0.5C146,-0.5 191,-0.5 191,-0.5 197,-0.5 203,-6.5 203,-12.5 203,-12.5 203,-24.5 203,-24.5 203,-30.5 197,-36.5 191,-36.5 191,-36.5 146,-36.5 146,-36.5 140,-36.5 134,-30.5 134,-24.5 134,-24.5 134,-12.5 134,-12.5 134,-6.5 140,-0.5 146,-0.5"/>
         
     | 
| 
      
 48 
     | 
    
         
            +
            <text text-anchor="start" x="143.5" y="-15.5" font-family="ArialMT" font-size="10.00">Category#2</text>
         
     | 
| 
      
 49 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 50 
     | 
    
         
            +
            <!-- Author#1 -->
         
     | 
| 
      
 51 
     | 
    
         
            +
            <g id="node3" class="node">
         
     | 
| 
      
 52 
     | 
    
         
            +
            <title>Author#1</title>
         
     | 
| 
      
 53 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M110.5,-146.5C110.5,-146.5 144.5,-146.5 144.5,-146.5 150.5,-146.5 156.5,-152.5 156.5,-158.5 156.5,-158.5 156.5,-170.5 156.5,-170.5 156.5,-176.5 150.5,-182.5 144.5,-182.5 144.5,-182.5 110.5,-182.5 110.5,-182.5 104.5,-182.5 98.5,-176.5 98.5,-170.5 98.5,-170.5 98.5,-158.5 98.5,-158.5 98.5,-152.5 104.5,-146.5 110.5,-146.5"/>
         
     | 
| 
      
 54 
     | 
    
         
            +
            <text text-anchor="start" x="107.5" y="-161.5" font-family="ArialMT" font-size="10.00">Author#1</text>
         
     | 
| 
      
 55 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 56 
     | 
    
         
            +
            <!-- Author#1->Post#1 -->
         
     | 
| 
      
 57 
     | 
    
         
            +
            <g id="edge4" class="edge">
         
     | 
| 
      
 58 
     | 
    
         
            +
            <title>Author#1->Post#1</title>
         
     | 
| 
      
 59 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M112.59,-138.29C106.95,-128.76 100.67,-118.14 95.57,-109.53"/>
         
     | 
| 
      
 60 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="110.04,-140.17 117.33,-146.31 115.46,-136.96 110.04,-140.17"/>
         
     | 
| 
      
 61 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 62 
     | 
    
         
            +
            <!-- Author#1->Post#2 -->
         
     | 
| 
      
 63 
     | 
    
         
            +
            <g id="edge5" class="edge">
         
     | 
| 
      
 64 
     | 
    
         
            +
            <title>Author#1->Post#2</title>
         
     | 
| 
      
 65 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M142.06,-138.29C147.56,-128.76 153.69,-118.14 158.67,-109.53"/>
         
     | 
| 
      
 66 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="139.2,-136.94 137.42,-146.31 144.65,-140.09 139.2,-136.94"/>
         
     | 
| 
      
 67 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 68 
     | 
    
         
            +
            <!-- Post#2->Category#2 -->
         
     | 
| 
      
 69 
     | 
    
         
            +
            <g id="edge8" class="edge">
         
     | 
| 
      
 70 
     | 
    
         
            +
            <title>Post#2->Category#2</title>
         
     | 
| 
      
 71 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M168.5,-64.13C168.5,-58.07 168.5,-51.64 168.5,-45.59"/>
         
     | 
| 
      
 72 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="165.35,-64.31 168.5,-73.31 171.65,-64.31 165.35,-64.31"/>
         
     | 
| 
      
 73 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="171.65,-45.53 168.5,-36.53 165.35,-45.53 171.65,-45.53"/>
         
     | 
| 
      
 74 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 75 
     | 
    
         
            +
            <!-- Blog#1 -->
         
     | 
| 
      
 76 
     | 
    
         
            +
            <g id="node6" class="node">
         
     | 
| 
      
 77 
     | 
    
         
            +
            <title>Blog#1</title>
         
     | 
| 
      
 78 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M197.5,-146.5C197.5,-146.5 227.5,-146.5 227.5,-146.5 233.5,-146.5 239.5,-152.5 239.5,-158.5 239.5,-158.5 239.5,-170.5 239.5,-170.5 239.5,-176.5 233.5,-182.5 227.5,-182.5 227.5,-182.5 197.5,-182.5 197.5,-182.5 191.5,-182.5 185.5,-176.5 185.5,-170.5 185.5,-170.5 185.5,-158.5 185.5,-158.5 185.5,-152.5 191.5,-146.5 197.5,-146.5"/>
         
     | 
| 
      
 79 
     | 
    
         
            +
            <text text-anchor="start" x="197.5" y="-161.5" font-family="ArialMT" font-size="10.00">Blog#1</text>
         
     | 
| 
      
 80 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 81 
     | 
    
         
            +
            <!-- Blog#1->Post#1 -->
         
     | 
| 
      
 82 
     | 
    
         
            +
            <g id="edge9" class="edge">
         
     | 
| 
      
 83 
     | 
    
         
            +
            <title>Blog#1->Post#1</title>
         
     | 
| 
      
 84 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M177.14,-143.73C156.75,-132.33 131.52,-118.23 112.7,-107.71"/>
         
     | 
| 
      
 85 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="175.86,-146.63 185.26,-148.27 178.94,-141.13 175.86,-146.63"/>
         
     | 
| 
      
 86 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 87 
     | 
    
         
            +
            <!-- Blog#1->Post#2 -->
         
     | 
| 
      
 88 
     | 
    
         
            +
            <g id="edge10" class="edge">
         
     | 
| 
      
 89 
     | 
    
         
            +
            <title>Blog#1->Post#2</title>
         
     | 
| 
      
 90 
     | 
    
         
            +
            <path fill="none" stroke="black" d="M197.06,-138.58C191.1,-128.97 184.44,-118.23 179.05,-109.53"/>
         
     | 
| 
      
 91 
     | 
    
         
            +
            <polygon fill="black" stroke="black" points="194.43,-140.32 201.85,-146.31 199.79,-137 194.43,-140.32"/>
         
     | 
| 
      
 92 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 93 
     | 
    
         
            +
            </g>
         
     | 
| 
      
 94 
     | 
    
         
            +
            </svg>
         
     | 
| 
         @@ -12,15 +12,17 @@ module Snapbot 
     | 
|
| 
       12 
12 
     | 
    
         
             
                # Get a visual handle on what small constellations of objects we're creating
         
     | 
| 
       13 
13 
     | 
    
         
             
                # in specs
         
     | 
| 
       14 
14 
     | 
    
         
             
                class DotGenerator
         
     | 
| 
       15 
     | 
    
         
            -
                  def initialize(label: "g", attrs: false, ignore_lets: %i[])
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def initialize(label: "g", attrs: false, ignore_lets: %i[], rspec: false)
         
     | 
| 
       16 
16 
     | 
    
         
             
                    @label = label
         
     | 
| 
       17 
17 
     | 
    
         
             
                    @options = { attrs: attrs }
         
     | 
| 
       18 
18 
     | 
    
         
             
                    @ignore_lets = ignore_lets
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                     
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
      
 20 
     | 
    
         
            +
                    @lets_by_value = if rspec
         
     | 
| 
      
 21 
     | 
    
         
            +
                                       example = binding.of_caller(1).eval("self")
         
     | 
| 
      
 22 
     | 
    
         
            +
                                       collect_lets(example)
         
     | 
| 
      
 23 
     | 
    
         
            +
                                     else
         
     | 
| 
      
 24 
     | 
    
         
            +
                                       {}
         
     | 
| 
      
 25 
     | 
    
         
            +
                                     end
         
     | 
| 
       24 
26 
     | 
    
         
             
                  end
         
     | 
| 
       25 
27 
     | 
    
         | 
| 
       26 
28 
     | 
    
         
             
                  def dot
         
     | 
| 
         @@ -37,7 +39,7 @@ module Snapbot 
     | 
|
| 
       37 
39 
     | 
    
         
             
                  end
         
     | 
| 
       38 
40 
     | 
    
         | 
| 
       39 
41 
     | 
    
         
             
                  def collect_lets(example)
         
     | 
| 
       40 
     | 
    
         
            -
                     
     | 
| 
      
 42 
     | 
    
         
            +
                    RSpec::Lets.new(example).collect.each_with_object({}) do |sym, lets_by_value|
         
     | 
| 
       41 
43 
     | 
    
         
             
                      value = example.send(sym) unless @ignore_lets.include?(sym)
         
     | 
| 
       42 
44 
     | 
    
         
             
                      lets_by_value[value] = sym if value.is_a?(reflector.base_activerecord_class)
         
     | 
| 
       43 
45 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -69,7 +71,7 @@ module Snapbot 
     | 
|
| 
       69 
71 
     | 
    
         
             
                          label=<
         
     | 
| 
       70 
72 
     | 
    
         
             
                            <table border="0" cellborder="0">
         
     | 
| 
       71 
73 
     | 
    
         
             
                              <%- if @lets_by_value[instance] -%>
         
     | 
| 
       72 
     | 
    
         
            -
                              <tr><td 
     | 
| 
      
 74 
     | 
    
         
            +
                              <tr><td><font face="Monaco,Courier,monospace" point-size="8">let(:<%= @lets_by_value[instance] %>)</font></td></tr>
         
     | 
| 
       73 
75 
     | 
    
         
             
                              <%- end -%>
         
     | 
| 
       74 
76 
     | 
    
         
             
                              <tr><td><%= instance_name(instance) %></td></tr>
         
     | 
| 
       75 
77 
     | 
    
         
             
                            </table>
         
     | 
| 
         @@ -78,9 +80,11 @@ module Snapbot 
     | 
|
| 
       78 
80 
     | 
    
         
             
                            <table border="0" cellborder="0">
         
     | 
| 
       79 
81 
     | 
    
         
             
                              <%- attributes(instance).each_pair do |attr, value| -%>
         
     | 
| 
       80 
82 
     | 
    
         
             
                              <tr>
         
     | 
| 
       81 
     | 
    
         
            -
                                <td align="left"  
     | 
| 
      
 83 
     | 
    
         
            +
                                <td align="left" port="<%= attr %>">
         
     | 
| 
       82 
84 
     | 
    
         
             
                                  <%= attr %>
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
      
 85 
     | 
    
         
            +
                                </td>
         
     | 
| 
      
 86 
     | 
    
         
            +
                                <td align="left">
         
     | 
| 
      
 87 
     | 
    
         
            +
                                  <font color="grey50"><%= value.inspect %></font>
         
     | 
| 
       84 
88 
     | 
    
         
             
                                </td>
         
     | 
| 
       85 
89 
     | 
    
         
             
                              </tr>
         
     | 
| 
       86 
90 
     | 
    
         
             
                              <%- end -%>
         
     | 
| 
         @@ -7,23 +7,23 @@ module Snapbot 
     | 
|
| 
       7 
7 
     | 
    
         
             
                # Render some DOT via Graphviz dot command line
         
     | 
| 
       8 
8 
     | 
    
         
             
                class Renderer
         
     | 
| 
       9 
9 
     | 
    
         
             
                  INSTALL_GRAPHVIZ_URL = "https://graphviz.org/download/#executable-packages"
         
     | 
| 
       10 
     | 
    
         
            -
                   
     | 
| 
      
 10 
     | 
    
         
            +
                  DEFAULT_OUTPUT_FILENAME = "tmp/models.svg"
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  def initialize(dot)
         
     | 
| 
       13 
13 
     | 
    
         
             
                    @dot = dot
         
     | 
| 
       14 
14 
     | 
    
         
             
                  end
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                  def save
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def save(path = DEFAULT_OUTPUT_FILENAME)
         
     | 
| 
       17 
17 
     | 
    
         
             
                    ensure_graphviz
         
     | 
| 
       18 
     | 
    
         
            -
                    FileUtils.rm( 
     | 
| 
       19 
     | 
    
         
            -
                    FileUtils.mkdir_p(File.dirname( 
     | 
| 
      
 18 
     | 
    
         
            +
                    FileUtils.rm(path, force: true)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    FileUtils.mkdir_p(File.dirname(path))
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                    IO.popen("dot -Tsvg -o #{ 
     | 
| 
      
 21 
     | 
    
         
            +
                    IO.popen("dot -Tsvg -o #{path}", "w") do |pipe|
         
     | 
| 
       22 
22 
     | 
    
         
             
                      pipe.puts(@dot)
         
     | 
| 
       23 
23 
     | 
    
         
             
                    end
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                    warn "Written to #{ 
     | 
| 
       26 
     | 
    
         
            -
                     
     | 
| 
      
 25 
     | 
    
         
            +
                    warn "Written to #{path}"
         
     | 
| 
      
 26 
     | 
    
         
            +
                    path
         
     | 
| 
       27 
27 
     | 
    
         
             
                  end
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
                  private
         
     | 
    
        data/lib/snapbot/diagram.rb
    CHANGED
    
    | 
         @@ -2,27 +2,33 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require "snapbot/diagram/dot_generator"
         
     | 
| 
       4 
4 
     | 
    
         
             
            require "snapbot/diagram/renderer"
         
     | 
| 
       5 
     | 
    
         
            -
            require "open3"
         
     | 
| 
       6 
5 
     | 
    
         | 
| 
       7 
6 
     | 
    
         
             
            module Snapbot
         
     | 
| 
       8 
7 
     | 
    
         
             
              # Print the small constellation of objects in your integration test and how they relate.
         
     | 
| 
       9 
8 
     | 
    
         
             
              # Requires Graphviz. Optimised for Mac. YMMV.
         
     | 
| 
       10 
9 
     | 
    
         
             
              module Diagram
         
     | 
| 
       11 
     | 
    
         
            -
                def save_and_open_diagram(**args)
         
     | 
| 
       12 
     | 
    
         
            -
                   
     | 
| 
       13 
     | 
    
         
            -
                  filename = Renderer.new(dot).save
         
     | 
| 
      
 10 
     | 
    
         
            +
                def save_and_open_diagram(path = Renderer::DEFAULT_OUTPUT_FILENAME, **args)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  filename = save_diagram(path, **args)
         
     | 
| 
       14 
12 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                  unless  
     | 
| 
       16 
     | 
    
         
            -
                    warn " 
     | 
| 
      
 13 
     | 
    
         
            +
                  unless launchy_present?
         
     | 
| 
      
 14 
     | 
    
         
            +
                    warn "Cannot open diagram – install `launchy`."
         
     | 
| 
       17 
15 
     | 
    
         
             
                    return
         
     | 
| 
       18 
16 
     | 
    
         
             
                  end
         
     | 
| 
       19 
17 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 18 
     | 
    
         
            +
                  Launchy.open(filename)
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def save_diagram(path = Renderer::DEFAULT_OUTPUT_FILENAME, **args)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  args.reverse_merge!(rspec: !!defined?(RSpec))
         
     | 
| 
      
 23 
     | 
    
         
            +
                  dot = DotGenerator.new(**args).dot
         
     | 
| 
      
 24 
     | 
    
         
            +
                  Renderer.new(dot).save(path)
         
     | 
| 
       22 
25 
     | 
    
         
             
                end
         
     | 
| 
       23 
26 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                def  
     | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
      
 27 
     | 
    
         
            +
                def launchy_present?
         
     | 
| 
      
 28 
     | 
    
         
            +
                  require "launchy"
         
     | 
| 
      
 29 
     | 
    
         
            +
                  true
         
     | 
| 
      
 30 
     | 
    
         
            +
                rescue LoadError
         
     | 
| 
      
 31 
     | 
    
         
            +
                  false
         
     | 
| 
       26 
32 
     | 
    
         
             
                end
         
     | 
| 
       27 
33 
     | 
    
         
             
              end
         
     | 
| 
       28 
34 
     | 
    
         
             
            end
         
     | 
    
        data/lib/snapbot/version.rb
    CHANGED
    
    
| 
         @@ -3,7 +3,7 @@ module Snapbot 
     | 
|
| 
       3 
3 
     | 
    
         
             
                # Get a visual handle on what small constellations of objects we're creating
         
     | 
| 
       4 
4 
     | 
    
         
             
                # in specs
         
     | 
| 
       5 
5 
     | 
    
         
             
                class DotGenerator
         
     | 
| 
       6 
     | 
    
         
            -
                  def initialize: (?label: ::String, ?attrs: bool, ?ignore_lets: Array[Symbol]) -> void
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize: (?label: ::String, ?attrs: bool, ?ignore_lets: Array[Symbol], ?rspec: bool) -> void
         
     | 
| 
       7 
7 
     | 
    
         
             
                  def dot: () -> ::String
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
                  @label: String
         
     | 
| 
         @@ -2,18 +2,19 @@ module Snapbot 
     | 
|
| 
       2 
2 
     | 
    
         
             
              module Diagram
         
     | 
| 
       3 
3 
     | 
    
         
             
                # Render some DOT via Graphviz dot command line
         
     | 
| 
       4 
4 
     | 
    
         
             
                class Renderer
         
     | 
| 
       5 
     | 
    
         
            -
                  INSTALL_GRAPHVIZ_URL: 
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
      
 5 
     | 
    
         
            +
                  INSTALL_GRAPHVIZ_URL:    String
         
     | 
| 
      
 6 
     | 
    
         
            +
                  DEFAULT_OUTPUT_FILENAME: String
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                  @dot: String
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
                  def initialize: (String dot) -> void
         
     | 
| 
       11 
     | 
    
         
            -
                  def save: () -> String
         
     | 
| 
      
 11 
     | 
    
         
            +
                  def save: (?String path) -> String
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                  private
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                  def graphviz_executable: () -> ::String
         
     | 
| 
       16 
16 
     | 
    
         
             
                  def ensure_graphviz: () -> void
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def launchy_present?: () -> bool
         
     | 
| 
       17 
18 
     | 
    
         
             
                end
         
     | 
| 
       18 
19 
     | 
    
         
             
              end
         
     | 
| 
       19 
20 
     | 
    
         
             
            end
         
     | 
    
        data/sig/lib/snapbot/diagram.rbs
    CHANGED
    
    | 
         @@ -2,7 +2,8 @@ module Snapbot 
     | 
|
| 
       2 
2 
     | 
    
         
             
              # Print the small constellation of objects in your integration test and how they relate.
         
     | 
| 
       3 
3 
     | 
    
         
             
              # Requires Graphviz. Optimised for Mac. YMMV.
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Diagram
         
     | 
| 
       5 
     | 
    
         
            -
                def  
     | 
| 
      
 5 
     | 
    
         
            +
                def save_diagram: (?String path, **untyped args) -> (nil | untyped)
         
     | 
| 
      
 6 
     | 
    
         
            +
                def save_and_open_diagram: (?String path, **untyped args) -> (nil | untyped)
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
       7 
8 
     | 
    
         
             
                def open_command: () -> untyped
         
     | 
| 
       8 
9 
     | 
    
         
             
              end
         
     | 
    
        data/snapbot.gemspec
    CHANGED
    
    | 
         @@ -35,6 +35,7 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       35 
35 
     | 
    
         
             
              spec.add_runtime_dependency "activerecord", version_string
         
     | 
| 
       36 
36 
     | 
    
         
             
              spec.add_runtime_dependency "activesupport", version_string
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
              spec.add_development_dependency "launchy"
         
     | 
| 
       38 
39 
     | 
    
         
             
              spec.add_development_dependency "rbs"
         
     | 
| 
       39 
40 
     | 
    
         
             
              spec.add_development_dependency "sqlite3"
         
     | 
| 
       40 
41 
     | 
    
         
             
              spec.add_development_dependency "steep"
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: snapbot
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.4.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Russell Garner
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire:
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2022-08- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-08-10 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: binding_of_caller
         
     | 
| 
         @@ -66,6 +66,20 @@ dependencies: 
     | 
|
| 
       66 
66 
     | 
    
         
             
                - - ">="
         
     | 
| 
       67 
67 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       68 
68 
     | 
    
         
             
                    version: '6.1'
         
     | 
| 
      
 69 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 70 
     | 
    
         
            +
              name: launchy
         
     | 
| 
      
 71 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 72 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 73 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 74 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 75 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 76 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 77 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 78 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 79 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 80 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 81 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 82 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       69 
83 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       70 
84 
     | 
    
         
             
              name: rbs
         
     | 
| 
       71 
85 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -124,6 +138,8 @@ files: 
     | 
|
| 
       124 
138 
     | 
    
         
             
            - README.md
         
     | 
| 
       125 
139 
     | 
    
         
             
            - Rakefile
         
     | 
| 
       126 
140 
     | 
    
         
             
            - Steepfile
         
     | 
| 
      
 141 
     | 
    
         
            +
            - docs/img/models-with-lets.svg
         
     | 
| 
      
 142 
     | 
    
         
            +
            - docs/img/models.svg
         
     | 
| 
       127 
143 
     | 
    
         
             
            - lib/snapbot.rb
         
     | 
| 
       128 
144 
     | 
    
         
             
            - lib/snapbot/diagram.rb
         
     | 
| 
       129 
145 
     | 
    
         
             
            - lib/snapbot/diagram/dot_generator.rb
         
     |