cocoapods-graph 1.0.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: 6d00b5c1eced22d283334372175b45d6a166c71261dcada2731d9581a86facc9
4
+ data.tar.gz: 6c11214289c0328e13214292758d3757bcafa6f70367b9f84effa7b17e7b038b
5
+ SHA512:
6
+ metadata.gz: 1477ae702985ad56ff75ad3c134b41fdc85a216d0616399cdd4660b64613f84b588f30c0e56f0e2d1496bbee637f39687da166045d352ed927e02f46d2b205b9
7
+ data.tar.gz: edc4c0d543bd6ee201d84496041e0476c95b89a50db097a5746eb4efa2d5964f98fc734ddb2ad7467aa60f82cf32d9deec338b8fe47bf04cf12e54c8c872ab84
data/CHANGELOG.md ADDED
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2024-12-15
9
+
10
+ ### Added
11
+ - 🎉 Initial release of CocoaPods Graph
12
+ - ✨ Interactive dependency wheel visualization using D3.js
13
+ - 🔍 Smart search functionality with auto-complete dropdown
14
+ - 📊 Real-time statistics dashboard (total packages, direct dependencies, total dependencies)
15
+ - 📱 Responsive design that works on desktop and mobile
16
+ - 🎨 Modern UI/UX with gradient backgrounds and smooth animations
17
+ - 🌐 Self-contained HTML output that works offline
18
+ - 📄 JSON export for programmatic access
19
+ - 🖥️ Console output for quick dependency inspection
20
+ - 💎 Ruby gem distribution for easy installation
21
+ - 📖 Comprehensive documentation and examples
22
+
23
+ ### Technical Features
24
+ - **Command-line interface** with multiple output options
25
+ - **Modular Ruby architecture** with clean separation of concerns
26
+ - **Professional HTML template** with embedded CSS and JavaScript
27
+ - **D3.js integration** for interactive visualizations
28
+ - **Search and highlight** functionality for exploring large dependency graphs
29
+ - **Color-coded visualization** for easy package identification
30
+ - **Hover effects and animations** for better user experience
31
+
32
+ ### Supported Formats
33
+ - HTML: Interactive dependency wheel with search functionality
34
+ - JSON: Structured data for programmatic use
35
+ - Console: Quick text-based dependency listing
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cocoapods-graph.gemspec
4
+ gemspec
5
+
6
+ gem 'bundler', '~> 2.0'
7
+ gem 'rake', '~> 13.0'
8
+ gem 'rspec', '~> 3.0'
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2025 Erick Jung
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # 📦 CocoaPods Graph
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/cocoapods-graph.svg)](https://badge.fury.io/rb/cocoapods-graph)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Generate beautiful, interactive dependency graphs for your CocoaPods projects. Transform your `Podfile.lock` into stunning visualizations that help you understand complex pod relationships.
7
+
8
+ ![CocoaPods Graph Demo](https://raw.githubusercontent.com/erickjung/cocoapods-graph/main/assets/demo.gif)
9
+
10
+ ## ✨ Features
11
+
12
+ - **🎨 Interactive Dependency Wheel**: Beautiful D3.js visualization with hover effects and smooth animations
13
+ - **🔍 Smart Search**: Find and highlight specific packages instantly with auto-complete dropdown
14
+ - **📊 Detailed Statistics**: Real-time stats showing total packages, direct dependencies, and relationships
15
+ - **📱 Responsive Design**: Works perfectly on desktop and mobile devices
16
+ - **🎯 Modern UI/UX**: Clean, professional interface with gradient backgrounds and smooth transitions
17
+ - **⚡ Fast Performance**: Optimized for projects with hundreds of dependencies
18
+ - **🌐 Self-contained**: Generated HTML files work offline with no external dependencies
19
+
20
+ ## 🚀 Installation
21
+
22
+ ### Install as a Gem (Recommended)
23
+
24
+ ```bash
25
+ gem install cocoapods-graph
26
+ ```
27
+
28
+ ### Install from Source
29
+
30
+ ```bash
31
+ git clone https://github.com/erickjung/cocoapods-graph.git
32
+ cd cocoapods-graph
33
+ bundle install
34
+ gem build cocoapods-graph.gemspec
35
+ gem install cocoapods-graph-*.gem
36
+ ```
37
+
38
+ ## 📖 Usage
39
+
40
+ ### Basic Usage
41
+
42
+ ```bash
43
+ # Generate interactive HTML report
44
+ cocoapods-graph -f Podfile.lock --html
45
+
46
+ # Generate JSON data
47
+ cocoapods-graph -f Podfile.lock --json
48
+
49
+ # Print dependencies to console
50
+ cocoapods-graph -f Podfile.lock --show
51
+
52
+ # Generate both HTML and JSON
53
+ cocoapods-graph -f Podfile.lock --html --json
54
+ ```
55
+
56
+ ### Command Line Options
57
+
58
+ | Option | Description |
59
+ |--------|-------------|
60
+ | `-f, --file FILE` | Specify path to Podfile.lock file |
61
+ | `--html` | Generate interactive HTML dependency wheel |
62
+ | `--json` | Generate JSON data file |
63
+ | `--show` | Print dependencies to console |
64
+ | `-h, --help` | Show help message |
65
+ | `-v, --version` | Show version |
66
+
67
+ ### Examples
68
+
69
+ ```bash
70
+ # In your iOS project directory
71
+ cocoapods-graph -f Podfile.lock --html
72
+ # Creates: Podfile.lock.html
73
+
74
+ # Specify custom file path
75
+ cocoapods-graph -f MyProject/Podfile.lock --html --json
76
+ # Creates: MyProject/Podfile.lock.html and MyProject/Podfile.lock.json
77
+
78
+ # Quick dependency check
79
+ cocoapods-graph -f Podfile.lock --show
80
+ ```
81
+
82
+ ## 🎨 Features Showcase
83
+
84
+ ### Interactive Dependency Wheel
85
+
86
+ ![WordPress iOS App Dependencies](docs/Wordpress-Report.png)
87
+ *Example dependency visualization from the WordPress iOS app, showing the interactive dependency wheel with search functionality and statistics dashboard.*
88
+
89
+ 📊 **[Try the live interactive demo!](docs/Wordpress-Report.html)** - Open the full WordPress iOS app dependency visualization
90
+
91
+ - **Hover effects**: Move your mouse over any package to see its relationships
92
+ - **Visual highlighting**: Related dependencies are highlighted while others fade
93
+ - **Color coding**: Each package gets a unique color for easy identification
94
+ - **Smooth animations**: Elegant transitions make exploration enjoyable
95
+
96
+ ### Smart Search Functionality
97
+ - **Live search**: Results appear as you type
98
+ - **Auto-complete**: Dropdown shows matching packages with dependency counts
99
+ - **One-click selection**: Click any result to highlight it on the wheel
100
+ - **Keyboard navigation**: Use Enter/Escape for quick actions
101
+
102
+ ### Professional Statistics Dashboard
103
+ - **Total Packages**: Count of all pods in your project
104
+ - **Direct Dependencies**: Number of pods your app directly depends on
105
+ - **Total Dependencies**: Sum of all dependency relationships
106
+ - **Real-time updates**: Stats update as you explore the graph
107
+
108
+ ## 📁 Output Files
109
+
110
+ ### HTML Report (`Podfile.lock.html`)
111
+ - Self-contained interactive visualization
112
+ - Modern, responsive design
113
+ - Search and highlight functionality
114
+ - Professional statistics dashboard
115
+ - Offline-ready (no internet required)
116
+
117
+ ### JSON Data (`Podfile.lock.json`)
118
+ - Structured dependency data
119
+ - Perfect for CI/CD integration
120
+ - Easy to parse programmatically
121
+ - Compatible with other tools
122
+
123
+ ## 🛠️ Development
124
+
125
+ ### Setup Development Environment
126
+
127
+ ```bash
128
+ git clone https://github.com/erickjung/cocoapods-graph.git
129
+ cd cocoapods-graph
130
+ bundle install
131
+ ```
132
+
133
+ ### Run Tests
134
+
135
+ ```bash
136
+ bundle exec rake
137
+ ```
138
+
139
+ ### Build Gem Locally
140
+
141
+ ```bash
142
+ gem build cocoapods-graph.gemspec
143
+ gem install cocoapods-graph-*.gem
144
+ ```
145
+
146
+ ## 🏗️ Project Structure
147
+
148
+ ```
149
+ cocoapods-graph/
150
+ ├── lib/
151
+ │ ├── cocoapods_graph.rb # Main module
152
+ │ └── cocoapods_graph/
153
+ │ ├── version.rb # Version constant
154
+ │ ├── pod_class.rb # Pod data structure
155
+ │ ├── generator.rb # Core parsing logic
156
+ │ └── template.html # HTML template
157
+ ├── exe/
158
+ │ └── cocoapods-graph # Executable binary
159
+ ├── cocoapods-graph.gemspec # Gem specification
160
+ └── README.md # This file
161
+ ```
162
+
163
+ ## 🤝 Contributing
164
+
165
+ We welcome contributions! Here's how you can help:
166
+
167
+ 1. **Fork the repository**
168
+ 2. **Create a feature branch**: `git checkout -b my-new-feature`
169
+ 3. **Make your changes** and add tests if applicable
170
+ 4. **Commit your changes**: `git commit -am 'Add some feature'`
171
+ 5. **Push to the branch**: `git push origin my-new-feature`
172
+ 6. **Submit a pull request**
173
+
174
+ ### Ideas for Contributions
175
+ - Add more visualization types (tree view, network graph)
176
+ - Implement filtering and grouping options
177
+ - Add export to other formats (SVG, PNG, PDF)
178
+ - Create VS Code / Xcode extensions
179
+ - Add support for other dependency managers
180
+
181
+ ## 📝 Changelog
182
+
183
+ ### v1.0.0 (2024-XX-XX)
184
+ - 🎉 Initial release
185
+ - ✨ Interactive dependency wheel visualization
186
+ - 🔍 Smart search functionality
187
+ - 📊 Statistics dashboard
188
+ - 📱 Responsive design
189
+ - 🌐 Self-contained HTML output
190
+
191
+ ## 📄 License
192
+
193
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
194
+
195
+ ## 👨‍💻 Author
196
+
197
+ **Erick Jung** - [E7G](https://www.erickjung.com)
198
+ - Email: talk@erickjung.com
199
+ - GitHub: [@erickjung](https://github.com/erickjung)
200
+
201
+ ## 🙏 Acknowledgments
202
+
203
+ - Built with [D3.js](https://d3js.org/) for beautiful visualizations
204
+ - Inspired by the need for better dependency analysis tools
205
+ - Thanks to the CocoaPods team for creating an amazing dependency manager
206
+
207
+ ## ⭐ Support
208
+
209
+ If you find this tool helpful, please give it a star on GitHub! ⭐
210
+
211
+ Found a bug or have a feature request? [Open an issue](https://github.com/erickjung/cocoapods-graph/issues)
212
+
213
+ ---
214
+
215
+ <div align="center">
216
+ <sub>Built with ❤️ by <a href="https://www.erickjung.com">E7G</a></sub>
217
+ </div>
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ desc 'Run tests'
7
+ task default: :spec
8
+
9
+ desc 'Install gem locally'
10
+ task :install_local do
11
+ sh 'gem build cocoapods-graph.gemspec'
12
+ sh 'gem install cocoapods-graph-*.gem'
13
+ end
14
+
15
+ desc 'Uninstall gem'
16
+ task :uninstall do
17
+ sh 'gem uninstall cocoapods-graph'
18
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "lib/cocoapods_graph/version"
2
+ require_relative "lib/cocoapods_graph/constants"
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = CocoaPodsGraph::PROJECT_NAME
6
+ spec.version = CocoaPodsGraph::VERSION
7
+ spec.authors = [CocoaPodsGraph::PROJECT_AUTHOR]
8
+ spec.email = [CocoaPodsGraph::PROJECT_EMAIL]
9
+
10
+ spec.summary = CocoaPodsGraph::PROJECT_SUMMARY
11
+ spec.description = "A Ruby gem that parses Podfile.lock files and generates beautiful, interactive dependency wheel visualizations using D3.js. Perfect for understanding complex pod relationships in your iOS projects."
12
+ spec.homepage = CocoaPodsGraph::PROJECT_HOMEPAGE
13
+ spec.license = CocoaPodsGraph::PROJECT_LICENSE
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = CocoaPodsGraph::PROJECT_SOURCE_CODE_URI
19
+ spec.metadata["changelog_uri"] = CocoaPodsGraph::PROJECT_CHANGELOG_URI
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ Dir.glob("lib/**/*") + Dir.glob("exe/*") + %w[
24
+ cocoapods-graph.gemspec
25
+ README.md
26
+ LICENSE
27
+ CHANGELOG.md
28
+ Gemfile
29
+ Rakefile
30
+ ]
31
+ end
32
+
33
+ spec.bindir = "exe"
34
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ["lib"]
36
+
37
+ # Runtime dependencies
38
+ spec.add_dependency "json", "~> 2.0"
39
+
40
+ # Development dependencies
41
+ spec.add_development_dependency "bundler", "~> 2.0"
42
+ spec.add_development_dependency "rake", "~> 13.0"
43
+ spec.add_development_dependency "rspec", "~> 3.0"
44
+ end
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
5
+ require_relative '../lib/cocoapods_graph'
6
+
7
+ module CocoaPodsGraph
8
+ class CLI
9
+ VERSION = CocoaPodsGraph::VERSION
10
+
11
+ def self.run
12
+ options = {}
13
+
14
+ parser = OptionParser.new do |parser|
15
+ parser.banner = 'Usage: cocoapods-graph [options]'
16
+ parser.on('-f', '--file FILE', 'Specify Podfile.lock file path') { |f| options[:file] = f }
17
+ parser.on('--show', 'Print dependencies on console') { options[:show] = true }
18
+ parser.on('--json', 'Save dependencies to JSON file') { options[:json] = true }
19
+ parser.on('--html', 'Save dependencies to interactive HTML wheel graph') { options[:html] = true }
20
+ parser.on('-v', '--version', 'Show version') do
21
+ puts VERSION
22
+ exit
23
+ end
24
+ parser.on('-h', '--help', 'Show this help message') do
25
+ puts parser
26
+ puts "\nExamples:"
27
+ puts " cocoapods-graph -f Podfile.lock --html # Generate interactive HTML report"
28
+ puts " cocoapods-graph -f Podfile.lock --json # Generate JSON output"
29
+ puts " cocoapods-graph -f Podfile.lock --show # Print to console"
30
+ puts " cocoapods-graph -f Podfile.lock --html --json # Generate both HTML and JSON"
31
+ exit
32
+ end
33
+ end
34
+
35
+ begin
36
+ parser.parse!
37
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
38
+ puts "Error: #{e.message}"
39
+ puts parser
40
+ exit(1)
41
+ end
42
+
43
+ if ARGV.empty? && options.empty?
44
+ puts "#{PROJECT_NAME} - #{VERSION} (#{PROJECT_DESCRIPTION})"
45
+ puts "by #{PROJECT_AUTHOR_WITH_EMAIL}"
46
+ puts 'Type: cocoapods-graph -h to see more information'
47
+ exit(1)
48
+ end
49
+
50
+ if options[:file] && !options[:file].empty?
51
+ unless options[:show] || options[:json] || options[:html]
52
+ puts 'You must select an output option (--show | --json | --html)'
53
+ exit(1)
54
+ end
55
+
56
+ # Add .lock extension if not present
57
+ options[:file] += '.lock' unless options[:file].include?('.lock')
58
+
59
+ unless File.exist?(options[:file])
60
+ puts "Error: File '#{options[:file]}' not found"
61
+ exit(1)
62
+ end
63
+
64
+ result = CocoaPodsGraph::Generator.parse_lock_file(options[:file])
65
+
66
+ if options[:show]
67
+ puts 'Printing dependencies...'
68
+ result.each(&:print_object)
69
+ end
70
+
71
+ if options[:json]
72
+ puts 'Saving JSON file...'
73
+ CocoaPodsGraph::Generator.save_json_file(result, options[:file])
74
+ end
75
+
76
+ if options[:html]
77
+ puts 'Saving HTML file...'
78
+ CocoaPodsGraph::Generator.save_html_wheel_file(result, options[:file])
79
+ end
80
+
81
+ puts 'Done!'
82
+ else
83
+ puts 'Error: Please specify a Podfile.lock file with -f option'
84
+ exit(1)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ CocoaPodsGraph::CLI.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CocoaPodsGraph
4
+ # Project metadata constants - single source of truth
5
+ PROJECT_NAME = 'cocoapods-graph'
6
+ PROJECT_DESCRIPTION = 'Generate interactive dependency graphs for CocoaPods projects'
7
+ PROJECT_AUTHOR = 'Erick Jung'
8
+ PROJECT_EMAIL = 'talk@erickjung.com'
9
+ PROJECT_HOMEPAGE = 'https://github.com/erickjung/cocoapods-graph'
10
+ PROJECT_SOURCE_CODE_URI = 'https://github.com/erickjung/cocoapods-graph'
11
+ PROJECT_CHANGELOG_URI = 'https://github.com/erickjung/cocoapods-graph/blob/main/CHANGELOG.md'
12
+ PROJECT_LICENSE = 'MIT'
13
+
14
+ # Derived constants for convenience
15
+ PROJECT_AUTHOR_WITH_EMAIL = "#{PROJECT_AUTHOR} (#{PROJECT_EMAIL})"
16
+ PROJECT_SUMMARY = 'Interactive dependency wheel visualizations for CocoaPods projects'
17
+ end
@@ -0,0 +1,96 @@
1
+ require 'json'
2
+ require 'time'
3
+
4
+ module CocoaPodsGraph
5
+ class Generator
6
+
7
+ def self.parse_lock_file(file_name)
8
+ def self.parse_lock_pods_line(line)
9
+ begin
10
+ name = line[line.index('-') + 1...line.index('(')].strip
11
+ rescue
12
+ name = line[line.index('-') + 1..-1].strip
13
+ end
14
+
15
+ begin
16
+ version = line[line.index('(') + 1...line.index(')')].strip
17
+ rescue
18
+ version = ''
19
+ end
20
+
21
+ PodClass.new(name, version, [])
22
+ end
23
+
24
+ content_file = File.readlines(file_name).map { |x| x.chomp.delete('"') }
25
+
26
+ result_list = []
27
+ pod = PodClass.new('', '', [])
28
+
29
+ content_file.each do |line|
30
+ if line.start_with?(' -')
31
+ result_list << pod if pod.name.length > 0
32
+ pod = parse_lock_pods_line(line)
33
+ elsif line.start_with?(' -')
34
+ pod.dependencies << parse_lock_pods_line(line)
35
+ elsif line.start_with?('DEPENDENCIES')
36
+ result_list << pod if pod.name.length > 0
37
+ break
38
+ end
39
+ end
40
+
41
+ result_list
42
+ end
43
+
44
+ def self.generate_json(result_list)
45
+ def self.generate_json_deps(result_list)
46
+ data = '{'
47
+ result_list.each do |result|
48
+ data += '"%s":"1",' % [result.name]
49
+ end
50
+ data = data[0...-1]
51
+ data + '}'
52
+ end
53
+
54
+ json = '{"packages": ['
55
+ deps = generate_json_deps(result_list)
56
+ json += '{"name":"app","require":%s},' % deps
57
+
58
+ result_list.each do |pod|
59
+ if pod.dependencies.length > 0
60
+ pod_deps = generate_json_deps(pod.dependencies)
61
+ json += '{"name":"%s","require":%s},' % [pod.name, pod_deps]
62
+ else
63
+ json += '{"name":"%s","require":{}},' % pod.name
64
+ end
65
+ end
66
+
67
+ json[0...-1] + ']}'
68
+ end
69
+
70
+ def self.save_json_file(data, file_name)
71
+ File.open(file_name + '.json', 'w') do |outfile|
72
+ outfile.write(generate_json(data))
73
+ end
74
+ end
75
+
76
+ def self.save_html_wheel_file(data, file_name)
77
+ template_path = File.join(__dir__, 'template.html')
78
+ p_template = File.read(template_path)
79
+ p_data_json = "'%s'" % generate_json(data)
80
+ p_data_width = '960'
81
+ p_data_margin = '200'
82
+ p_data_padding = '.02'
83
+ p_generation_date = Time.now.strftime('%B %d, %Y at %I:%M %p')
84
+
85
+ html_out = p_template.gsub('P_DATA_JSON', p_data_json)
86
+ .gsub('P_DATA_WIDTH', p_data_width)
87
+ .gsub('P_DATA_MARGIN', p_data_margin)
88
+ .gsub('P_DATA_PADDING', p_data_padding)
89
+ .gsub('P_GENERATION_DATE', p_generation_date)
90
+
91
+ File.open(file_name + '.html', 'w') do |outfile|
92
+ outfile.write(html_out)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,18 @@
1
+ module CocoaPodsGraph
2
+ class PodClass
3
+ attr_accessor :name, :version, :dependencies
4
+
5
+ def initialize(name, version, dependencies)
6
+ @name = name
7
+ @version = version
8
+ @dependencies = dependencies
9
+ end
10
+
11
+ def print_object
12
+ puts "#{@name} #{@version}"
13
+ @dependencies.each do |dep|
14
+ puts " #{dep.name} #{dep.version}"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,537 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>CocoaPods Dependencies Graph</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
8
+ <style>
9
+ * { margin: 0; padding: 0; box-sizing: border-box; }
10
+
11
+ body {
12
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
13
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
14
+ min-height: 100vh;
15
+ color: #333;
16
+ line-height: 1.6;
17
+ }
18
+
19
+ .header {
20
+ text-align: center;
21
+ padding: 2rem 1rem 1rem;
22
+ color: white;
23
+ }
24
+
25
+ .header h1 {
26
+ font-size: 2.5rem;
27
+ font-weight: 700;
28
+ margin-bottom: 0.5rem;
29
+ text-shadow: 0 2px 4px rgba(0,0,0,0.3);
30
+ }
31
+
32
+ .header p {
33
+ font-size: 1.1rem;
34
+ opacity: 0.9;
35
+ font-weight: 300;
36
+ }
37
+
38
+ .main-container {
39
+ max-width: 1200px;
40
+ margin: 0 auto;
41
+ padding: 0 1rem;
42
+ }
43
+
44
+ .chart-container {
45
+ background: white;
46
+ border-radius: 16px;
47
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1);
48
+ padding: 2rem;
49
+ margin-bottom: 2rem;
50
+ position: relative;
51
+ overflow: hidden;
52
+ }
53
+
54
+ .chart-container::before {
55
+ content: "";
56
+ position: absolute;
57
+ top: 0;
58
+ left: 0;
59
+ right: 0;
60
+ height: 4px;
61
+ background: linear-gradient(90deg, #667eea, #764ba2);
62
+ }
63
+
64
+ .chart-header {
65
+ text-align: center;
66
+ margin-bottom: 2rem;
67
+ }
68
+
69
+ .chart-title {
70
+ font-size: 1.5rem;
71
+ font-weight: 600;
72
+ color: #2d3748;
73
+ margin-bottom: 0.5rem;
74
+ }
75
+
76
+ .chart-subtitle {
77
+ color: #718096;
78
+ font-size: 0.95rem;
79
+ }
80
+
81
+ #chart_placeholder {
82
+ display: flex;
83
+ justify-content: center;
84
+ align-items: center;
85
+ min-height: 500px;
86
+ position: relative;
87
+ }
88
+
89
+ .dependencyWheel {
90
+ font: 11px "Inter", sans-serif;
91
+ filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));
92
+ }
93
+
94
+ .group text {
95
+ font-weight: 500;
96
+ font-size: 11px;
97
+ }
98
+
99
+ .chord {
100
+ transition: opacity 0.3s ease;
101
+ }
102
+
103
+ .group path {
104
+ transition: all 0.3s ease;
105
+ }
106
+
107
+ .group:hover path {
108
+ transform: scale(1.02);
109
+ filter: brightness(1.1);
110
+ }
111
+
112
+ .instructions {
113
+ background: #f7fafc;
114
+ border-radius: 12px;
115
+ padding: 1.5rem;
116
+ margin-bottom: 2rem;
117
+ border-left: 4px solid #667eea;
118
+ }
119
+
120
+ .instructions h3 {
121
+ color: #2d3748;
122
+ font-size: 1.1rem;
123
+ font-weight: 600;
124
+ margin-bottom: 0.5rem;
125
+ display: flex;
126
+ align-items: center;
127
+ gap: 0.5rem;
128
+ }
129
+
130
+ .instructions ul {
131
+ color: #4a5568;
132
+ font-size: 0.9rem;
133
+ list-style: none;
134
+ padding-left: 0;
135
+ }
136
+
137
+ .instructions li {
138
+ margin-bottom: 0.3rem;
139
+ padding-left: 1.2rem;
140
+ position: relative;
141
+ }
142
+
143
+ .instructions li::before {
144
+ content: "•";
145
+ color: #667eea;
146
+ font-weight: bold;
147
+ position: absolute;
148
+ left: 0;
149
+ }
150
+
151
+ .search-container {
152
+ background: #f8fafc;
153
+ border-radius: 8px;
154
+ padding: 1rem;
155
+ margin-bottom: 1.5rem;
156
+ border: 1px solid #e2e8f0;
157
+ }
158
+
159
+ .search-header {
160
+ display: flex;
161
+ align-items: center;
162
+ gap: 0.5rem;
163
+ margin-bottom: 0.75rem;
164
+ color: #2d3748;
165
+ font-weight: 600;
166
+ font-size: 1rem;
167
+ }
168
+
169
+ .search-input-container {
170
+ position: relative;
171
+ margin-bottom: 0.75rem;
172
+ }
173
+
174
+ .search-input {
175
+ width: 100%;
176
+ padding: 0.75rem 1rem 0.75rem 2.5rem;
177
+ border: 2px solid #e2e8f0;
178
+ border-radius: 8px;
179
+ font-size: 1rem;
180
+ font-family: inherit;
181
+ transition: all 0.3s ease;
182
+ }
183
+
184
+ .search-input:focus {
185
+ outline: none;
186
+ border-color: #667eea;
187
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
188
+ }
189
+
190
+ .search-icon {
191
+ position: absolute;
192
+ left: 0.75rem;
193
+ top: 50%;
194
+ transform: translateY(-50%);
195
+ color: #718096;
196
+ font-size: 1.1rem;
197
+ }
198
+
199
+ .search-results {
200
+ max-height: 200px;
201
+ overflow-y: auto;
202
+ border: 1px solid #e2e8f0;
203
+ border-radius: 6px;
204
+ background: white;
205
+ position: absolute;
206
+ width: 100%;
207
+ z-index: 10;
208
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
209
+ display: none;
210
+ }
211
+
212
+ .search-result-item {
213
+ padding: 0.75rem 1rem;
214
+ cursor: pointer;
215
+ border-bottom: 1px solid #f7fafc;
216
+ transition: background-color 0.2s ease;
217
+ display: flex;
218
+ justify-content: space-between;
219
+ align-items: center;
220
+ }
221
+
222
+ .search-result-item:hover {
223
+ background-color: #f7fafc;
224
+ }
225
+
226
+ .search-result-item:last-child {
227
+ border-bottom: none;
228
+ }
229
+
230
+ .search-result-name {
231
+ font-weight: 500;
232
+ color: #2d3748;
233
+ }
234
+
235
+ .search-result-version {
236
+ font-size: 0.85rem;
237
+ color: #718096;
238
+ font-family: monospace;
239
+ }
240
+
241
+ .stats {
242
+ display: grid;
243
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
244
+ gap: 1rem;
245
+ margin-bottom: 2rem;
246
+ }
247
+
248
+ .stat-card {
249
+ background: white;
250
+ border-radius: 12px;
251
+ padding: 1.5rem;
252
+ text-align: center;
253
+ box-shadow: 0 4px 12px rgba(0,0,0,0.05);
254
+ border: 1px solid #e2e8f0;
255
+ }
256
+
257
+ .stat-number {
258
+ font-size: 2rem;
259
+ font-weight: 700;
260
+ color: #667eea;
261
+ margin-bottom: 0.25rem;
262
+ }
263
+
264
+ .stat-label {
265
+ color: #718096;
266
+ font-size: 0.9rem;
267
+ font-weight: 500;
268
+ }
269
+
270
+ .footer {
271
+ text-align: center;
272
+ padding: 2rem 1rem;
273
+ color: rgba(255,255,255,0.8);
274
+ font-size: 0.85rem;
275
+ }
276
+
277
+ .loading {
278
+ display: flex;
279
+ align-items: center;
280
+ justify-content: center;
281
+ min-height: 300px;
282
+ color: #718096;
283
+ font-size: 1.1rem;
284
+ }
285
+
286
+ .spinner {
287
+ width: 40px;
288
+ height: 40px;
289
+ border: 3px solid #e2e8f0;
290
+ border-top: 3px solid #667eea;
291
+ border-radius: 50%;
292
+ animation: spin 1s linear infinite;
293
+ margin-right: 1rem;
294
+ }
295
+
296
+ @keyframes spin {
297
+ 0% { transform: rotate(0deg); }
298
+ 100% { transform: rotate(360deg); }
299
+ }
300
+
301
+ @media (max-width: 768px) {
302
+ .header h1 { font-size: 2rem; }
303
+ .chart-container { padding: 1rem; margin: 0 0.5rem 1rem; }
304
+ .stats { grid-template-columns: 1fr 1fr; gap: 0.75rem; }
305
+ }
306
+ </style>
307
+ </head>
308
+ <body>
309
+ <div class="header">
310
+ <h1>📦 CocoaPods Dependencies</h1>
311
+ <p>Interactive dependency visualization for your iOS project</p>
312
+ </div>
313
+
314
+ <div class="main-container">
315
+ <div class="stats" id="stats"></div>
316
+
317
+ <div class="chart-container">
318
+ <div class="chart-header">
319
+ <h2 class="chart-title">Dependencies Wheel</h2>
320
+ <p class="chart-subtitle">Hover over packages to explore relationships</p>
321
+ </div>
322
+
323
+ <div class="search-container">
324
+ <div class="search-header">
325
+ 🔍 Search Packages
326
+ </div>
327
+ <div class="search-input-container">
328
+ <span class="search-icon">🔍</span>
329
+ <input
330
+ type="text"
331
+ id="packageSearch"
332
+ class="search-input"
333
+ placeholder="Type package name to search and highlight..."
334
+ autocomplete="off"
335
+ />
336
+ <div id="searchResults" class="search-results"></div>
337
+ </div>
338
+ </div>
339
+
340
+ <div id="chart_placeholder">
341
+ <div class="loading">
342
+ <div class="spinner"></div>
343
+ Loading dependency graph...
344
+ </div>
345
+ </div>
346
+ </div>
347
+
348
+ <div class="instructions">
349
+ <h3>🎯 How to use this visualization</h3>
350
+ <ul>
351
+ <li>Hover over any package name or arc to highlight its dependencies</li>
352
+ <li>The curves (chords) show dependency relationships between packages</li>
353
+ <li>Thicker arcs represent packages with more dependencies</li>
354
+ <li>Colors help distinguish different packages and their relationships</li>
355
+ </ul>
356
+ </div>
357
+ </div>
358
+
359
+ <div class="footer">
360
+ Generated by CocoaPods Graph • Made with ❤️ by <a href="https://www.erickjung.com" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: none;">E7G</a><br>
361
+ <small>Generated on P_GENERATION_DATE</small>
362
+ </div>
363
+
364
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
365
+ <script>
366
+ // Parse data and show stats
367
+ const data = JSON.parse(P_DATA_JSON);
368
+ const totalPackages = data.packages.length;
369
+ const appDependencies = Object.keys(data.packages[0].require).length;
370
+ const totalDependencies = data.packages.reduce((sum, pkg) => sum + Object.keys(pkg.require).length, 0);
371
+
372
+ document.getElementById("stats").innerHTML = `
373
+ <div class="stat-card">
374
+ <div class="stat-number">${totalPackages}</div>
375
+ <div class="stat-label">Total Packages</div>
376
+ </div>
377
+ <div class="stat-card">
378
+ <div class="stat-number">${appDependencies}</div>
379
+ <div class="stat-label">Direct Dependencies</div>
380
+ </div>
381
+ <div class="stat-card">
382
+ <div class="stat-number">${totalDependencies}</div>
383
+ <div class="stat-label">Total Dependencies</div>
384
+ </div>
385
+ `;
386
+
387
+ // D3 dependency wheel code
388
+ d3.chart=d3.chart||{},d3.chart.dependencyWheel=function(e){var t=window,r=document,n=r.documentElement,a=r.getElementsByTagName("body")[0],c=(t.innerWidth||n.clientWidth||a.clientWidth,t.innerHeight||n.clientHeight||a.clientHeight,200),i=.03,o=960;function u(e){e.each(function(e){var t=e.matrix,r=e.packageNames,n=o/2-c,a=d3.chord().padAngle(i).sortSubgroups(d3.descending),u=d3.select(this).selectAll("svg").data([e]).enter().append("svg:svg").attr("width",o).attr("height",o).attr("class","dependencyWheel").append("g").attr("transform","translate("+o/2+","+o/2+")"),d=d3.arc().innerRadius(n).outerRadius(n+20),l=function(e){return 0===e.index?"#ccc":"hsl("+parseInt((r[e.index][0].charCodeAt()-97)/26*360,10)+",90%,70%)"},s=function(e){return function(t,r){u.selectAll(".chord").filter(function(e){return e.source.index!=r&&e.target.index!=r}).transition().style("opacity",e);var n=[];u.selectAll(".chord").filter(function(e){e.source.index==r&&n.push(e.target.index),e.target.index==r&&n.push(e.source.index)}),n.push(r);var a=n.length;u.selectAll(".group").filter(function(e){for(var t=0;t<a;t++)if(n[t]==e.index)return!1;return!0}).transition().style("opacity",e)}},g=a(t),p=g.groups[0],h=-(p.endAngle-p.startAngle)/2*(180/Math.PI),f=u.selectAll("g.group").data(g.groups).enter().append("svg:g").attr("class","group").attr("transform",function(e){return"rotate("+h+")"});f.append("svg:path").style("fill",l).style("stroke",l).attr("d",d).style("cursor","pointer").on("mouseover",s(.1)).on("mouseout",s(1)),f.append("svg:text").each(function(e){e.angle=(e.startAngle+e.endAngle)/2}).attr("dy",".35em").attr("text-anchor",function(e){return e.angle>Math.PI?"end":null}).attr("transform",function(e){return"rotate("+(180*e.angle/Math.PI-90)+")translate("+(n+26)+")"+(e.angle>Math.PI?"rotate(180)":"")}).style("cursor","pointer").text(function(e){return r[e.index]}).on("mouseover",s(.1)).on("mouseout",s(1)),u.selectAll("path.chord").data(g).enter().append("svg:path").attr("class","chord").style("stroke",function(e){return d3.rgb(l(e.source)).darker()}).style("fill",function(e){return l(e.source)}).attr("d",d3.ribbon().radius(n)).attr("transform",function(e){return"rotate("+h+")"}).style("opacity",1)})}return u.width=function(e){return arguments.length?(o=e,u):o},u.margin=function(e){return arguments.length?(c=e,u):c},u.padding=function(e){return arguments.length?(i=e,u):i},u};
389
+ var buildMatrixFromDependencies=function(e){var t=e.packages,r={},n={},a=[],c=0,i={};return t.forEach(function(e){if(e.replace)for(replaced in e.replace)i[replaced]=e.name}),t.forEach(function(e){for(packageName in e.require)packageName in i&&(e.require[i[packageName]]=e.require[packageName],delete e.require[packageName])}),t.forEach(function(e){packageName=e.name,packageName in r||(n[c]=packageName,r[packageName]=c++)}),t.forEach(function(e){var t=r[e.name],n=a[t];if(!n){n=a[t]=[];for(var i=-1;++i<c;)n[i]=0}for(packageName in e.require)n[r[packageName]]++}),a.forEach(function(e,t){for(var r=.001,n=-1;++n<c;){var a=(n+t)%c;1==e[a]&&(e[a]+=r,r+=.001)}}),{matrix:a,packageNames:n}};
390
+
391
+ // Clear loading and render chart
392
+ d3.select("#chart_placeholder").selectAll("*").remove();
393
+ d3.select("#chart_placeholder").datum(buildMatrixFromDependencies(data)).call(d3.chart.dependencyWheel().width(P_DATA_WIDTH).margin(P_DATA_MARGIN).padding(P_DATA_PADDING));
394
+
395
+ // Search functionality
396
+ let currentHighlightedIndex = -1;
397
+ const packageNames = data.packages.map(pkg => pkg.name);
398
+
399
+ const searchInput = document.getElementById("packageSearch");
400
+ const searchResults = document.getElementById("searchResults");
401
+
402
+ function highlightPackage(packageName, temporary = false) {
403
+ const packageIndex = packageNames.indexOf(packageName);
404
+ if (packageIndex === -1) return;
405
+
406
+ // Find the corresponding group element and trigger mouseover
407
+ const svg = d3.select("#chart_placeholder svg");
408
+ const groups = svg.selectAll(".group");
409
+
410
+ groups.each(function(d, i) {
411
+ if (i === packageIndex) {
412
+ // Reset all to normal first
413
+ resetHighlight();
414
+
415
+ // Highlight this package
416
+ currentHighlightedIndex = packageIndex;
417
+ d3.select(this).select("path").style("stroke-width", "3px").style("filter", "brightness(1.2)");
418
+ d3.select(this).select("text").style("font-weight", "bold").style("font-size", "13px");
419
+
420
+ // Fade non-related elements
421
+ svg.selectAll(".chord")
422
+ .filter(function(chord) {
423
+ return chord.source.index !== packageIndex && chord.target.index !== packageIndex;
424
+ })
425
+ .transition().style("opacity", 0.1);
426
+
427
+ svg.selectAll(".group")
428
+ .filter(function(group, idx) {
429
+ // Find related packages
430
+ let related = [packageIndex];
431
+ svg.selectAll(".chord").each(function(chord) {
432
+ if (chord.source.index === packageIndex) related.push(chord.target.index);
433
+ if (chord.target.index === packageIndex) related.push(chord.source.index);
434
+ });
435
+ return related.indexOf(idx) === -1;
436
+ })
437
+ .transition().style("opacity", 0.3);
438
+ }
439
+ });
440
+ }
441
+
442
+ function resetHighlight() {
443
+ const svg = d3.select("#chart_placeholder svg");
444
+ svg.selectAll(".chord").transition().style("opacity", 1);
445
+ svg.selectAll(".group").transition().style("opacity", 1);
446
+ svg.selectAll(".group path").style("stroke-width", "1px").style("filter", "none");
447
+ svg.selectAll(".group text").style("font-weight", "500").style("font-size", "11px");
448
+ currentHighlightedIndex = -1;
449
+ }
450
+
451
+ function performSearch(query) {
452
+ if (!query.trim()) {
453
+ searchResults.style.display = "none";
454
+ resetHighlight();
455
+ return;
456
+ }
457
+
458
+ const filtered = data.packages.filter(pkg =>
459
+ pkg.name.toLowerCase().includes(query.toLowerCase())
460
+ ).slice(0, 10); // Limit to 10 results
461
+
462
+ if (filtered.length === 0) {
463
+ searchResults.style.display = "none";
464
+ resetHighlight();
465
+ return;
466
+ }
467
+
468
+ searchResults.innerHTML = filtered.map(pkg => `
469
+ <div class="search-result-item" data-package="${pkg.name}">
470
+ <span class="search-result-name">${pkg.name}</span>
471
+ <span class="search-result-version">${Object.keys(pkg.require).length} deps</span>
472
+ </div>
473
+ `).join("");
474
+
475
+ searchResults.style.display = "block";
476
+
477
+ // Add click handlers to results
478
+ searchResults.querySelectorAll(".search-result-item").forEach(item => {
479
+ item.addEventListener("click", function() {
480
+ const packageName = this.dataset.package;
481
+ searchInput.value = packageName;
482
+ searchResults.style.display = "none";
483
+ highlightPackage(packageName);
484
+ });
485
+
486
+ item.addEventListener("mouseenter", function() {
487
+ const packageName = this.dataset.package;
488
+ highlightPackage(packageName, true);
489
+ });
490
+
491
+ item.addEventListener("mouseleave", function() {
492
+ if (currentHighlightedIndex === -1) {
493
+ resetHighlight();
494
+ }
495
+ });
496
+ });
497
+
498
+ // Auto-highlight first result if exact match
499
+ if (filtered.length === 1 || filtered[0].name.toLowerCase() === query.toLowerCase()) {
500
+ highlightPackage(filtered[0].name, true);
501
+ }
502
+ }
503
+
504
+ // Search input events
505
+ searchInput.addEventListener("input", function() {
506
+ performSearch(this.value);
507
+ });
508
+
509
+ searchInput.addEventListener("keydown", function(e) {
510
+ if (e.key === "Enter") {
511
+ const results = searchResults.querySelectorAll(".search-result-item");
512
+ if (results.length > 0) {
513
+ results[0].click();
514
+ }
515
+ } else if (e.key === "Escape") {
516
+ this.value = "";
517
+ searchResults.style.display = "none";
518
+ resetHighlight();
519
+ }
520
+ });
521
+
522
+ // Click outside to close search results
523
+ document.addEventListener("click", function(e) {
524
+ if (!e.target.closest(".search-input-container")) {
525
+ searchResults.style.display = "none";
526
+ }
527
+ });
528
+
529
+ // Double-click on chart to reset
530
+ document.getElementById("chart_placeholder").addEventListener("dblclick", function() {
531
+ searchInput.value = "";
532
+ searchResults.style.display = "none";
533
+ resetHighlight();
534
+ });
535
+ </script>
536
+ </body>
537
+ </html>
@@ -0,0 +1,3 @@
1
+ module CocoaPodsGraph
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'cocoapods_graph/version'
2
+ require_relative 'cocoapods_graph/constants'
3
+ require_relative 'cocoapods_graph/pod_class'
4
+ require_relative 'cocoapods_graph/generator'
5
+
6
+ module CocoaPodsGraph
7
+ class Error < StandardError; end
8
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cocoapods-graph
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Erick Jung
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-09-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: A Ruby gem that parses Podfile.lock files and generates beautiful, interactive
70
+ dependency wheel visualizations using D3.js. Perfect for understanding complex pod
71
+ relationships in your iOS projects.
72
+ email:
73
+ - talk@erickjung.com
74
+ executables:
75
+ - cocoapods-graph
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - CHANGELOG.md
80
+ - Gemfile
81
+ - LICENSE
82
+ - README.md
83
+ - Rakefile
84
+ - cocoapods-graph.gemspec
85
+ - exe/cocoapods-graph
86
+ - lib/cocoapods_graph.rb
87
+ - lib/cocoapods_graph/constants.rb
88
+ - lib/cocoapods_graph/generator.rb
89
+ - lib/cocoapods_graph/pod_class.rb
90
+ - lib/cocoapods_graph/template.html
91
+ - lib/cocoapods_graph/version.rb
92
+ homepage: https://github.com/erickjung/cocoapods-graph
93
+ licenses:
94
+ - MIT
95
+ metadata:
96
+ allowed_push_host: https://rubygems.org
97
+ homepage_uri: https://github.com/erickjung/cocoapods-graph
98
+ source_code_uri: https://github.com/erickjung/cocoapods-graph
99
+ changelog_uri: https://github.com/erickjung/cocoapods-graph/blob/main/CHANGELOG.md
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 2.6.0
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 3.4.19
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Interactive dependency wheel visualizations for CocoaPods projects
119
+ test_files: []