attractor-swift 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 783075f65d0b5fc72db27fbe146c75e4f08197743f0ac13d96f2c6cc11a53c44
4
+ data.tar.gz: d79761047aab01a58eff2bb89aab868b589affeb8abe9ad0dfbf01e0ca943b71
5
+ SHA512:
6
+ metadata.gz: f95d230978f00d012ff0d71545c3cfddfc1934a79ebe4bc334a56cb5a89840823ea815741b2134c029c6ace693d419a2d632776831455e7eec34a9fb0bd9af3f
7
+ data.tar.gz: 98064df8b81e17c7ffd1778cc781d83ea1257d7cabfa58945e247d3eb6416c0fed556d25ca4c7dcf187d0d86d08862951bc5e63fe5c3041a0fc62025b2b33ef0
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /coverage/
3
+ /doc/
4
+ /pkg/
5
+ /tmp/
6
+ .rspec_status
7
+ Gemfile.lock
8
+ *~
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## RELEASE 0.1.0
2
+
3
+ * Initial release: Swift calculator on `Attractor::LizardCalculator` (attractor >= 2.8), detector for `Package.swift`, Xcode projects and bare `.swift` trees.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ # Develop against an unreleased attractor: ATTRACTOR_PATH=../attractor bundle install
6
+ gem "attractor", path: ENV["ATTRACTOR_PATH"] if ENV["ATTRACTOR_PATH"]
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Julian Rubisch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # attractor-swift
2
+
3
+ Swift plugin for [attractor](https://github.com/julianrubisch/attractor), the churn vs. complexity
4
+ tool. Complexity is the per-function cyclomatic complexity reported by
5
+ [lizard](https://github.com/terryyin/lizard); churn comes from attractor's git history.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ gem install attractor attractor-swift
11
+ ```
12
+
13
+ lizard is a Python tool. attractor runs it through [uv](https://docs.astral.sh/uv/) when
14
+ `lizard` itself is not on your PATH:
15
+
16
+ ```bash
17
+ # one of
18
+ brew install uv # then `uvx lizard` fetches lizard on first use
19
+ pip install lizard
20
+ ```
21
+
22
+ attractor finds the plugin by its gem name; nothing to configure.
23
+
24
+ ## Usage
25
+
26
+ ```bash
27
+ cd my-swift-project
28
+ attractor calc -c 0 --format table # every file, table
29
+ attractor calc --format json # per-function details with line ranges
30
+ attractor diff --base main --head HEAD -c 0 # complexity delta of a branch
31
+ attractor report # the interactive chart
32
+ ```
33
+
34
+ Method-level `details` carry lizard's CCN and location:
35
+
36
+ ```json
37
+ "details": {
38
+ "total": {"score": 3, "line": 6, "end_line": 15},
39
+ "pay": {"score": 3, "line": 24, "end_line": 30}
40
+ }
41
+ ```
42
+
43
+ Two things to know about the numbers:
44
+
45
+ - CCN is McCabe cyclomatic complexity, a different scale from flog's Ruby score. attractor
46
+ reports per language, so they are never compared.
47
+ - lizard names functions without their type (`total`, not `Cart.total`). Two functions with
48
+ the same name in one file are kept apart by their start line (`card@32`, `card@50`).
49
+
50
+ Force a specific runner with `ATTRACTOR_LIZARD="python -m lizard"`.
51
+
52
+ ## Using this repository as a template for another language
53
+
54
+ Everything language-specific is two strings and a detector. To make `attractor-kotlin`:
55
+
56
+ 1. Copy this repository. Rename `swift` → `kotlin` in file names, module names, the gemspec and
57
+ the workflow.
58
+ 2. `lib/attractor/calculators/kotlin_calculator.rb`: `language: "kotlin"` (a name from
59
+ `lizard --help`, under `-l`), `file_extension: "kt"` (a regex-ish string is fine:
60
+ `"(kt|kts)"`), `@type = "Kotlin"`.
61
+ 3. `lib/attractor/detectors/kotlin_detector.rb`: what marks a project (`build.gradle.kts`,
62
+ any `*.kt`).
63
+ 4. Replace the fixture in `spec/fixtures/` and the expected numbers in the calculator spec;
64
+ run `uvx lizard -l kotlin --csv spec/fixtures/...` once to get them.
65
+ 5. Push, tag, `rake release`.
66
+
67
+ Languages lizard scores today include C/C++, C#, Go, Java, JavaScript, Kotlin, Lua, Objective-C,
68
+ PHP, Python, Rust, Scala, Swift, TypeScript and more. For languages with a better native
69
+ metric tool (Ruby has flog, JavaScript has escomplex with class and import context), write
70
+ the calculator against that tool instead, as attractor-ruby and attractor-javascript do.
71
+
72
+ ## Development
73
+
74
+ ```bash
75
+ bin/setup
76
+ bundle exec rspec # the lizard integration example skips without lizard or uv
77
+ bundle exec standardrb
78
+ ```
79
+
80
+ Against an unreleased attractor: `ATTRACTOR_PATH=../attractor bundle install`.
81
+
82
+ ## License
83
+
84
+ MIT.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "attractor/swift/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "attractor-swift"
9
+ spec.version = Attractor::Swift::VERSION
10
+ spec.authors = ["Julian Rubisch"]
11
+ spec.email = ["julian@julianrubisch.at"]
12
+
13
+ spec.summary = "Attractor plugin for Swift: churn vs. cyclomatic complexity via lizard"
14
+ spec.description = <<-DESCRIPTION
15
+ Attractor plugin for the Swift programming language. Scores files with lizard's
16
+ per-function cyclomatic complexity and attractor's git churn.
17
+ DESCRIPTION
18
+ spec.homepage = "https://github.com/julianrubisch/attractor-swift"
19
+ spec.licenses = ["MIT"]
20
+ spec.required_ruby_version = ">= 3.3"
21
+
22
+ spec.metadata["homepage_uri"] = spec.homepage
23
+ spec.metadata["source_code_uri"] = spec.homepage
24
+ spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
25
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
26
+
27
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
+ `git ls-files -z`.split("\x0").reject do |f|
29
+ f.match(%r{^(spec|\.github)/}) || %w[.rspec .rspec_status .standard.yml .tool-versions].include?(f)
30
+ end
31
+ end
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency "attractor", ">= 2.8"
35
+
36
+ spec.add_development_dependency "bundler", ">= 2.7"
37
+ spec.add_development_dependency "rake", "~> 13.0"
38
+ spec.add_development_dependency "rspec", "~> 3.0"
39
+ spec.add_development_dependency "standard"
40
+ end
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "attractor/swift"
5
+
6
+ require "irb"
7
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ bundle install
5
+ command -v uvx >/dev/null || echo "uv is not installed; lizard will not run. See https://docs.astral.sh/uv/"
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attractor
4
+ # Everything language-specific is these two strings; the scoring lives in
5
+ # Attractor::LizardCalculator (attractor >= 2.8).
6
+ class SwiftCalculator < LizardCalculator
7
+ def initialize(**options)
8
+ super(language: "swift", file_extension: "swift", **options)
9
+ @type = "Swift"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attractor
4
+ class SwiftDetector < BaseDetector
5
+ # A package manifest or an Xcode project is the cheap signal; a bare directory of .swift
6
+ # files (a script, a playground export) still counts, hence the glob fallback.
7
+ def detect
8
+ root = Dir.pwd
9
+ File.exist?(File.join(root, "Package.swift")) ||
10
+ Dir.glob(File.join(root, "*.{xcodeproj,xcworkspace}")).any? ||
11
+ Dir.glob(File.join(root, "**", "*.swift")).any?
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module Attractor
2
+ module Swift
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require "attractor/swift/version"
2
+
3
+ require "attractor"
4
+ require "attractor/registry_entry"
5
+ require "attractor/calculators/lizard_calculator"
6
+ require "attractor/calculators/swift_calculator"
7
+ require "attractor/detectors/base_detector"
8
+ require "attractor/detectors/swift_detector"
9
+
10
+ module Attractor
11
+ module Swift
12
+ class Error < StandardError; end
13
+
14
+ Attractor.register(Attractor::RegistryEntry.new(type: "swift", detector_class: SwiftDetector, calculator_class: SwiftCalculator))
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attractor-swift
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Julian Rubisch
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-09-22 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: attractor
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.8'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '2.8'
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.7'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.7'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: standard
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ description: |2
83
+ Attractor plugin for the Swift programming language. Scores files with lizard's
84
+ per-function cyclomatic complexity and attractor's git churn.
85
+ email:
86
+ - julian@julianrubisch.at
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - CHANGELOG.md
93
+ - Gemfile
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - attractor-swift.gemspec
98
+ - bin/console
99
+ - bin/setup
100
+ - lib/attractor/calculators/swift_calculator.rb
101
+ - lib/attractor/detectors/swift_detector.rb
102
+ - lib/attractor/swift.rb
103
+ - lib/attractor/swift/version.rb
104
+ homepage: https://github.com/julianrubisch/attractor-swift
105
+ licenses:
106
+ - MIT
107
+ metadata:
108
+ homepage_uri: https://github.com/julianrubisch/attractor-swift
109
+ source_code_uri: https://github.com/julianrubisch/attractor-swift
110
+ bug_tracker_uri: https://github.com/julianrubisch/attractor-swift/issues
111
+ changelog_uri: https://github.com/julianrubisch/attractor-swift/blob/main/CHANGELOG.md
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '3.3'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubygems_version: 3.6.2
127
+ specification_version: 4
128
+ summary: 'Attractor plugin for Swift: churn vs. cyclomatic complexity via lizard'
129
+ test_files: []