snapbot 0.1.2 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e57537e104190501dc2c47d19b8f76a95e91dd2b7f189f2686b4159c171ad101
4
- data.tar.gz: d8dfe6118e506a0a10ad5d8dc86c8a8ba041e3fa99253d64c2a29062026f853f
3
+ metadata.gz: 7f8b1c94c13ad202a7a33eb020d8aa4c6fc66b3123a08f0f210c13a2b91598c3
4
+ data.tar.gz: 54ade2525a1d3f0497c4148610488b350bacc2c2860ed7e6969d5fb237cce0f9
5
5
  SHA512:
6
- metadata.gz: 3c3d84a4445afa197bafa704cb1d2ac1535b8bdf12c8a19155734af415ca03aa9bc1fa2c28ba44903ddbcfd15fea0e1bcee244bdd0d99e4aaada3c6fbbcbb0c0
7
- data.tar.gz: 3f19483aa69878acbdf035ef05d65be3db10f0d5f0bb4a79658020b86e6ed5580f2f366b4bb204fa312d99a7f197c021b1717852be828591f5fb74bb525a4a17
6
+ metadata.gz: 7b4b99a9dd2bf1dea4174ff924eed781818f29707a89b94d3952830ecd59368fbb2632d90c3633414aa175ac03741dcfaa322698160f8e2484d1b1884cdbad5d
7
+ data.tar.gz: cd76dec5c1fd06383679a1d35b06cd8cd59d17e8c9a676395efbe30065c79b1baa99cad653f6589557243a1ace625050b470b7133843d41663fdc210dfd02fc0
data/.rubocop.yml CHANGED
@@ -1,12 +1,23 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.6
3
3
  SuggestExtensions: false
4
+ Exclude:
5
+ - Steepfile
6
+
7
+
8
+ Metrics/AbcSize:
9
+ Exclude:
10
+ - spec/support/fixture_database.rb
4
11
 
5
12
  Metrics/BlockLength:
6
13
  Exclude:
7
14
  - spec/**/*_spec.rb
8
15
  - snapbot.gemspec
9
16
 
17
+ Metrics/MethodLength:
18
+ Exclude:
19
+ - spec/support/fixture_database.rb
20
+
10
21
  Style/StderrPuts:
11
22
  Exclude:
12
23
  - lib/snapbot/diagram/renderer.rb
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Snapbot
2
2
 
3
+ ![example workflow](https://github.com/rgarner/snapbot/actions/workflows/main.yml/badge.svg)
4
+
3
5
  Snapbot generates little diagrams via `save_and_open_diagram` for you to visualise the small constellations of
4
6
  ActiveRecord objects that you find in feature and integration tests. These are most often made by
5
7
  [FactoryBot](https://github.com/thoughtbot/factory_bot) or some other fixture-handling method, but this gem has no
data/Rakefile CHANGED
@@ -3,10 +3,12 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
5
 
6
+ Dir["lib/tasks/*.rake"].each { |file| import file }
7
+
6
8
  RSpec::Core::RakeTask.new(:spec)
7
9
 
8
10
  require "rubocop/rake_task"
9
11
 
10
12
  RuboCop::RakeTask.new
11
13
 
12
- task default: %i[spec rubocop]
14
+ task default: %i[spec rubocop steep:check]
data/Steepfile ADDED
@@ -0,0 +1,43 @@
1
+ D = Steep::Diagnostic
2
+
3
+ target :lib do
4
+ signature "sig"
5
+
6
+ check "lib" # Directory name
7
+ # check "Gemfile" # File name
8
+ # check "app/models/**/*.rb" # Glob
9
+ # ignore "lib/templates/*.rb"
10
+
11
+ library "set"
12
+ # library "rspec"
13
+ # library "strong_json" # Gems
14
+
15
+ # configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
16
+ configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
17
+ configure_code_diagnostics do |hash| # You can setup everything yourself
18
+ # lib/snapbot/reflector.rb:73:35: [error] Unsupported block params pattern, probably masgn?
19
+ # │ Diagnostic ID: Ruby::UnsupportedSyntax
20
+ # │
21
+ # └ hash.each_with_object([]) do |(key, value), array|
22
+ # ~~~~~~~~~~~~~~~~~~~~~
23
+ # This disables that ^^
24
+ hash[D::Ruby::UnsupportedSyntax] = :information
25
+
26
+ # lib/snapbot/diagram/renderer.rb:21:58: [error] The method cannot be called with a block
27
+ # │ Diagnostic ID: Ruby::UnexpectedBlockGiven
28
+ # │
29
+ # └ IO.popen("dot -Tsvg -o #{OUTPUT_FILENAME}", "w+") do |pipe|
30
+ # ~~~~~~~~~
31
+ #
32
+ # This disables that ^^ but probably a bit too much. Can we restrict to renderer.rb?
33
+ hash[D::Ruby::UnexpectedBlockGiven] = :information
34
+ end
35
+ end
36
+
37
+ # target :test do
38
+ # signature "sig", "sig-private"
39
+ #
40
+ # check "test"
41
+ #
42
+ # # library "pathname", "set" # Standard libraries
43
+ # end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "snapbot/reflector"
4
- require "snapbot/diagram/dot_generator/relationship"
5
4
 
6
5
  if defined?(::RSpec)
7
6
  require "snapbot/rspec/lets"
@@ -16,6 +16,8 @@ module Snapbot
16
16
  def save
17
17
  ensure_graphviz
18
18
  FileUtils.rm(OUTPUT_FILENAME, force: true)
19
+ FileUtils.mkdir_p(File.dirname(OUTPUT_FILENAME))
20
+
19
21
  IO.popen("dot -Tsvg -o #{OUTPUT_FILENAME}", "w") do |pipe|
20
22
  pipe.puts(@dot)
21
23
  end
@@ -11,7 +11,7 @@ module Snapbot
11
11
  self.destination = destination
12
12
  end
13
13
 
14
- def equals(other)
14
+ def ==(other)
15
15
  source == other.source && destination == other.destination
16
16
  end
17
17
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_record"
4
+ require "snapbot/reflector/relationship"
4
5
 
5
6
  module Snapbot
6
7
  # Reflect models and instances in a way that's useful for generating a diagram
@@ -13,11 +14,21 @@ module Snapbot
13
14
  @models ||= begin
14
15
  Rails.application.eager_load! if defined?(Rails)
15
16
  base_activerecord_class.descendants.reject do |c|
16
- c.to_s == "Schema" || (only_with_records && c.count.zero?)
17
+ activerecord_ignore?(c) || (only_with_records && c.count.zero?)
17
18
  end
18
19
  end
19
20
  end
20
21
 
22
+ ACTIVERECORD_IGNORE = [
23
+ /^Schema$/,
24
+ /^HABTM_/,
25
+ /^ActiveRecord::InternalMetadata$/,
26
+ /^ActiveRecord::SchemaMigration$/
27
+ ].freeze
28
+ def activerecord_ignore?(klass)
29
+ ACTIVERECORD_IGNORE.any? { |r| klass.name =~ r } || klass.abstract_class
30
+ end
31
+
21
32
  def instances
22
33
  @instances ||= models.each_with_object([]) do |klass, array|
23
34
  array << klass.all
@@ -43,7 +54,8 @@ module Snapbot
43
54
  def reflect_associations(instance)
44
55
  (
45
56
  instance.class.reflect_on_all_associations(:has_many).reject { |a| a.name == :schemas } +
46
- instance.class.reflect_on_all_associations(:has_one)
57
+ instance.class.reflect_on_all_associations(:has_one) +
58
+ instance.class.reflect_on_all_associations(:has_and_belongs_to_many)
47
59
  ).flatten
48
60
  end
49
61
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Snapbot
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.1"
5
5
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :steep do
4
+ desc "Run `steep check`"
5
+ task :check do
6
+ system("steep check", exception: true)
7
+ end
8
+ end
@@ -0,0 +1,25 @@
1
+ module Snapbot
2
+ module Diagram
3
+ # Get a visual handle on what small constellations of objects we're creating
4
+ # in specs
5
+ class DotGenerator
6
+ def initialize: (?label: ::String, ?attrs: bool, ?ignore_lets: Array[Symbol]) -> void
7
+ def dot: () -> ::String
8
+
9
+ @label: String
10
+ @attrs: bool
11
+ @ignore_lets: Array[Symbol]
12
+
13
+ @options: Hash[Object, Object]
14
+ @lets_by_value: Hash[Object, Symbol]
15
+ @reflector: Reflector
16
+
17
+ private
18
+
19
+ def reflector: () -> Reflector
20
+ def collect_lets: (::RSpec::Core::ExampleGroup example) -> untyped
21
+ def instance_name: (ActiveRecord::Base `instance`) -> ::String
22
+ def template: () -> ::String
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Snapbot
2
+ module Diagram
3
+ # Render some DOT via Graphviz dot command line
4
+ class Renderer
5
+ INSTALL_GRAPHVIZ_URL: "https://graphviz.org/download/#executable-packages"
6
+ OUTPUT_FILENAME: "tmp/models.svg"
7
+
8
+ @dot: String
9
+
10
+ def initialize: (String dot) -> void
11
+ def save: () -> String
12
+
13
+ private
14
+
15
+ def graphviz_executable: () -> ::String
16
+ def ensure_graphviz: () -> void
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module Snapbot
2
+ # Print the small constellation of objects in your integration test and how they relate.
3
+ # Requires Graphviz. Optimised for Mac. YMMV.
4
+ module Diagram
5
+ def save_and_open_diagram: (**untyped args) -> (nil | untyped)
6
+
7
+ def open_command: () -> untyped
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ module Snapbot
2
+ class Reflector
3
+ # A source/destination-based relationship
4
+ class Relationship
5
+ attr_accessor source: ::String
6
+
7
+ attr_accessor destination: ::String
8
+
9
+ def initialize: (::String source, ::String destination) -> void
10
+
11
+ def ==: (Relationship other) -> bool
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveRecord
2
+ class Base
3
+ end
4
+
5
+ module Reflection
6
+ class AssociationReflection
7
+ def name: () -> Symbol
8
+ end
9
+ end
10
+ end
11
+
12
+ module Snapbot
13
+ # Reflect models and instances in a way that's useful for generating a diagram
14
+ class Reflector
15
+ ACTIVERECORD_IGNORE: ::Array[::Regexp]
16
+
17
+ @models: Array[Class]
18
+ @instances: Array[ActiveRecord::Base]
19
+ @relationships: Set[Relationship]
20
+
21
+ def base_activerecord_class: () -> Class
22
+ def models: (?only_with_records: bool) -> Array[Class]
23
+ def activerecord_ignore?: (Class klass) -> bool
24
+ def instances: () -> Array[ActiveRecord::Base]
25
+
26
+ # A Set of relationships to other identified entities
27
+ def relationships: () -> Set[Relationship]
28
+ def add_relationships: (ActiveRecord::Base `instance`, Set[Relationship] set) -> untyped
29
+ def reflect_associations: (ActiveRecord::Base `instance`) -> Array[ActiveRecord::Reflection::AssociationReflection]
30
+ def attributes: (ActiveRecord::Base `instance`) -> Hash[Symbol, Object]
31
+
32
+ private
33
+
34
+ def instance_name: (ActiveRecord::Base `instance`) -> ::String
35
+ # Remains commented out, as is private, and https://github.com/ruby/rbs/issues/734#issuecomment-1192607498
36
+ # def escape_hash: (Hash[Symbol, Object] hash) -> untyped
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ module RSpec
2
+ module Core
3
+ class ExampleGroup
4
+ end
5
+ end
6
+ end
7
+
8
+ module Snapbot
9
+ module RSpec
10
+ # Collect RSpec `let`s for a given example and all her parents
11
+ class Lets
12
+ @lets: Array[Symbol]
13
+ @lets_by_value: Hash[Object, Symbol]
14
+ @example: ::RSpec::Core::ExampleGroup
15
+
16
+ def initialize: (::RSpec::Core::ExampleGroup example) -> void
17
+
18
+ def collect: () -> Array[Symbol]
19
+
20
+ private
21
+
22
+ def _collect: (::RSpec::Core::ExampleGroup klass, Array[Symbol] lets) -> Array[Symbol]
23
+ end
24
+ end
25
+ end
data/snapbot.gemspec CHANGED
@@ -35,7 +35,9 @@ 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 "rbs"
38
39
  spec.add_development_dependency "sqlite3"
40
+ spec.add_development_dependency "steep"
39
41
 
40
42
  # For more information and examples about making a new gem, check out our
41
43
  # guide at: https://bundler.io/guides/creating_gem.html
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.1.2
4
+ version: 0.2.1
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-08 00:00:00.000000000 Z
11
+ date: 2022-08-09 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: rbs
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: sqlite3
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,20 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: steep
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  description:
84
112
  email:
85
113
  - rgarner@zephyros-systems.co.uk
@@ -95,14 +123,22 @@ files:
95
123
  - LICENSE.txt
96
124
  - README.md
97
125
  - Rakefile
126
+ - Steepfile
98
127
  - lib/snapbot.rb
99
128
  - lib/snapbot/diagram.rb
100
129
  - lib/snapbot/diagram/dot_generator.rb
101
- - lib/snapbot/diagram/dot_generator/relationship.rb
102
130
  - lib/snapbot/diagram/renderer.rb
103
131
  - lib/snapbot/reflector.rb
132
+ - lib/snapbot/reflector/relationship.rb
104
133
  - lib/snapbot/rspec/lets.rb
105
134
  - lib/snapbot/version.rb
135
+ - lib/tasks/steep.rake
136
+ - sig/lib/snapbot/diagram.rbs
137
+ - sig/lib/snapbot/diagram/dot_generator.rbs
138
+ - sig/lib/snapbot/diagram/renderer.rbs
139
+ - sig/lib/snapbot/reflector.rbs
140
+ - sig/lib/snapbot/reflector/relationship.rbs
141
+ - sig/lib/snapbot/rspec/lets.rbs
106
142
  - sig/snapbot.rbs
107
143
  - snapbot.gemspec
108
144
  homepage: https://github.com/rgarner/snapbot