rubyn 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 +7 -0
- data/LICENSE +21 -0
- data/README.md +251 -0
- data/Rakefile +12 -0
- data/exe/rubyn +5 -0
- data/lib/generators/rubyn/install_generator.rb +16 -0
- data/lib/rubyn/cli.rb +85 -0
- data/lib/rubyn/client/api_client.rb +172 -0
- data/lib/rubyn/commands/agent.rb +191 -0
- data/lib/rubyn/commands/base.rb +60 -0
- data/lib/rubyn/commands/config.rb +51 -0
- data/lib/rubyn/commands/dashboard.rb +85 -0
- data/lib/rubyn/commands/index.rb +101 -0
- data/lib/rubyn/commands/init.rb +166 -0
- data/lib/rubyn/commands/refactor.rb +175 -0
- data/lib/rubyn/commands/review.rb +61 -0
- data/lib/rubyn/commands/spec.rb +72 -0
- data/lib/rubyn/commands/usage.rb +56 -0
- data/lib/rubyn/config/credentials.rb +39 -0
- data/lib/rubyn/config/project_config.rb +42 -0
- data/lib/rubyn/config/settings.rb +53 -0
- data/lib/rubyn/context/codebase_indexer.rb +195 -0
- data/lib/rubyn/context/context_builder.rb +36 -0
- data/lib/rubyn/context/file_resolver.rb +235 -0
- data/lib/rubyn/context/project_scanner.rb +132 -0
- data/lib/rubyn/engine/app/assets/images/rubyn/RubynLogo.png +0 -0
- data/lib/rubyn/engine/app/assets/javascripts/rubyn/application.js +579 -0
- data/lib/rubyn/engine/app/assets/stylesheets/rubyn/application.css +1073 -0
- data/lib/rubyn/engine/app/controllers/rubyn/agent_controller.rb +26 -0
- data/lib/rubyn/engine/app/controllers/rubyn/application_controller.rb +44 -0
- data/lib/rubyn/engine/app/controllers/rubyn/dashboard_controller.rb +19 -0
- data/lib/rubyn/engine/app/controllers/rubyn/feedback_controller.rb +17 -0
- data/lib/rubyn/engine/app/controllers/rubyn/files_controller.rb +54 -0
- data/lib/rubyn/engine/app/controllers/rubyn/refactor_controller.rb +56 -0
- data/lib/rubyn/engine/app/controllers/rubyn/reviews_controller.rb +32 -0
- data/lib/rubyn/engine/app/controllers/rubyn/settings_controller.rb +17 -0
- data/lib/rubyn/engine/app/controllers/rubyn/specs_controller.rb +33 -0
- data/lib/rubyn/engine/app/views/layouts/rubyn/application.html.erb +63 -0
- data/lib/rubyn/engine/app/views/rubyn/agent/show.html.erb +22 -0
- data/lib/rubyn/engine/app/views/rubyn/dashboard/index.html.erb +120 -0
- data/lib/rubyn/engine/app/views/rubyn/files/index.html.erb +45 -0
- data/lib/rubyn/engine/app/views/rubyn/refactor/show.html.erb +28 -0
- data/lib/rubyn/engine/app/views/rubyn/reviews/show.html.erb +28 -0
- data/lib/rubyn/engine/app/views/rubyn/settings/show.html.erb +42 -0
- data/lib/rubyn/engine/app/views/rubyn/specs/show.html.erb +28 -0
- data/lib/rubyn/engine/config/routes.rb +13 -0
- data/lib/rubyn/engine/engine.rb +18 -0
- data/lib/rubyn/output/diff_renderer.rb +106 -0
- data/lib/rubyn/output/formatter.rb +123 -0
- data/lib/rubyn/output/spinner.rb +26 -0
- data/lib/rubyn/tools/base_tool.rb +74 -0
- data/lib/rubyn/tools/bundle_add.rb +77 -0
- data/lib/rubyn/tools/create_file.rb +32 -0
- data/lib/rubyn/tools/delete_file.rb +29 -0
- data/lib/rubyn/tools/executor.rb +68 -0
- data/lib/rubyn/tools/find_files.rb +33 -0
- data/lib/rubyn/tools/find_references.rb +72 -0
- data/lib/rubyn/tools/git_commit.rb +65 -0
- data/lib/rubyn/tools/git_create_branch.rb +58 -0
- data/lib/rubyn/tools/git_diff.rb +42 -0
- data/lib/rubyn/tools/git_log.rb +43 -0
- data/lib/rubyn/tools/git_status.rb +26 -0
- data/lib/rubyn/tools/list_directory.rb +82 -0
- data/lib/rubyn/tools/move_file.rb +35 -0
- data/lib/rubyn/tools/patch_file.rb +47 -0
- data/lib/rubyn/tools/rails_generate.rb +40 -0
- data/lib/rubyn/tools/rails_migrate.rb +55 -0
- data/lib/rubyn/tools/rails_routes.rb +35 -0
- data/lib/rubyn/tools/read_file.rb +45 -0
- data/lib/rubyn/tools/registry.rb +28 -0
- data/lib/rubyn/tools/run_command.rb +48 -0
- data/lib/rubyn/tools/run_tests.rb +52 -0
- data/lib/rubyn/tools/search_files.rb +82 -0
- data/lib/rubyn/tools/write_file.rb +30 -0
- data/lib/rubyn/version.rb +5 -0
- data/lib/rubyn/version_checker.rb +74 -0
- data/lib/rubyn.rb +95 -0
- data/sig/rubyn.rbs +4 -0
- metadata +379 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubyn
|
|
4
|
+
module Context
|
|
5
|
+
class ContextBuilder
|
|
6
|
+
MAX_CONTEXT_BYTES = 100_000
|
|
7
|
+
AGENT_MAX_CONTEXT_BYTES = 500_000
|
|
8
|
+
|
|
9
|
+
attr_reader :project_root
|
|
10
|
+
|
|
11
|
+
def initialize(project_root = Dir.pwd, max_bytes: MAX_CONTEXT_BYTES)
|
|
12
|
+
@project_root = project_root
|
|
13
|
+
@max_bytes = max_bytes
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def build(target_path, related_paths = [])
|
|
17
|
+
context = {}
|
|
18
|
+
total_size = 0
|
|
19
|
+
|
|
20
|
+
all_paths = [target_path] + related_paths
|
|
21
|
+
all_paths.uniq.each do |path|
|
|
22
|
+
full_path = File.join(project_root, path)
|
|
23
|
+
next unless File.exist?(full_path)
|
|
24
|
+
|
|
25
|
+
content = File.read(full_path)
|
|
26
|
+
break if total_size + content.bytesize > @max_bytes
|
|
27
|
+
|
|
28
|
+
context[path] = content
|
|
29
|
+
total_size += content.bytesize
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubyn
|
|
4
|
+
module Context
|
|
5
|
+
class FileResolver
|
|
6
|
+
attr_reader :project_root, :project_type
|
|
7
|
+
|
|
8
|
+
def initialize(project_root = Dir.pwd, project_type: nil)
|
|
9
|
+
@project_root = project_root
|
|
10
|
+
@project_type = project_type || detect_project_type
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def resolve(file_path)
|
|
14
|
+
relative_path = file_path.start_with?(project_root) ? file_path.sub("#{project_root}/", "") : file_path
|
|
15
|
+
|
|
16
|
+
related = case @project_type
|
|
17
|
+
when "rails_app"
|
|
18
|
+
resolve_rails(relative_path)
|
|
19
|
+
when "ruby_app"
|
|
20
|
+
resolve_ruby_gem(relative_path)
|
|
21
|
+
else
|
|
22
|
+
resolve_generic(relative_path)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
related.select { |f| File.exist?(File.join(project_root, f)) }.uniq
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def detect_project_type
|
|
31
|
+
if File.exist?(File.join(project_root, "config", "routes.rb"))
|
|
32
|
+
"rails_app"
|
|
33
|
+
elsif Dir.glob(File.join(project_root, "*.gemspec")).any?
|
|
34
|
+
"ruby_app"
|
|
35
|
+
else
|
|
36
|
+
"other_app"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def resolve_rails(path)
|
|
41
|
+
related = []
|
|
42
|
+
|
|
43
|
+
case path
|
|
44
|
+
when %r{^app/controllers/(.+)_controller\.rb$}
|
|
45
|
+
name = Regexp.last_match(1)
|
|
46
|
+
singular = singularize(File.basename(name))
|
|
47
|
+
related += controller_related(name, singular)
|
|
48
|
+
when %r{^app/models/(.+)\.rb$}
|
|
49
|
+
name = Regexp.last_match(1)
|
|
50
|
+
related += model_related(name)
|
|
51
|
+
when %r{^app/services/(.+)\.rb$}
|
|
52
|
+
name = Regexp.last_match(1)
|
|
53
|
+
related += service_related(name, path)
|
|
54
|
+
when %r{^spec/(.+)_spec\.rb$}
|
|
55
|
+
source = Regexp.last_match(1)
|
|
56
|
+
related << source_from_spec(source)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Always scan for constant references to find dependencies
|
|
60
|
+
related += resolve_constants(path)
|
|
61
|
+
|
|
62
|
+
related << "config/routes.rb"
|
|
63
|
+
related << "db/schema.rb"
|
|
64
|
+
related
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def controller_related(name, singular)
|
|
68
|
+
[
|
|
69
|
+
"app/models/#{singular}.rb",
|
|
70
|
+
"config/routes.rb",
|
|
71
|
+
"spec/requests/#{name}_spec.rb",
|
|
72
|
+
"spec/controllers/#{name}_controller_spec.rb"
|
|
73
|
+
] + Dir.glob(File.join(project_root, "app/services/#{name}/**/*.rb")).map { |f| f.sub("#{project_root}/", "") }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def model_related(name)
|
|
77
|
+
plural = pluralize(name)
|
|
78
|
+
[
|
|
79
|
+
"db/schema.rb",
|
|
80
|
+
"app/controllers/#{plural}_controller.rb",
|
|
81
|
+
"spec/models/#{name}_spec.rb",
|
|
82
|
+
"spec/factories/#{plural}.rb",
|
|
83
|
+
"spec/factories/#{name}.rb"
|
|
84
|
+
]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def service_related(name, path)
|
|
88
|
+
related = ["spec/services/#{name}_spec.rb"]
|
|
89
|
+
related + resolve_constants(path)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def resolve_ruby_gem(path)
|
|
93
|
+
related = []
|
|
94
|
+
|
|
95
|
+
case path
|
|
96
|
+
when %r{^lib/(.+)\.rb$}
|
|
97
|
+
spec_path = "spec/#{Regexp.last_match(1)}_spec.rb"
|
|
98
|
+
related << spec_path
|
|
99
|
+
|
|
100
|
+
dir = File.dirname(path)
|
|
101
|
+
if dir != "lib"
|
|
102
|
+
Dir.glob(File.join(project_root, dir, "*.rb")).each do |f|
|
|
103
|
+
sibling = f.sub("#{project_root}/", "")
|
|
104
|
+
related << sibling unless sibling == path
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
when %r{^spec/(.+)_spec\.rb$}
|
|
108
|
+
related << "lib/#{Regexp.last_match(1)}.rb"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
related
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def resolve_generic(path)
|
|
115
|
+
dir = File.dirname(path)
|
|
116
|
+
related = Dir.glob(File.join(project_root, dir, "*.rb")).map { |f| f.sub("#{project_root}/", "") }
|
|
117
|
+
related.reject { |f| f == path }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def source_from_spec(spec_path)
|
|
121
|
+
case spec_path
|
|
122
|
+
when %r{^requests/(.+)$}
|
|
123
|
+
"app/controllers/#{Regexp.last_match(1)}_controller.rb"
|
|
124
|
+
when %r{^models/(.+)$}
|
|
125
|
+
"app/models/#{Regexp.last_match(1)}.rb"
|
|
126
|
+
when %r{^services/(.+)$}
|
|
127
|
+
"app/services/#{Regexp.last_match(1)}.rb"
|
|
128
|
+
else
|
|
129
|
+
"lib/#{spec_path}.rb"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Scan a file for class/module constant references and locate their source files
|
|
134
|
+
# in the project. Handles namespaced constants like Ai::ClaudeClient -> app/services/ai/claude_client.rb
|
|
135
|
+
# Also parses Rails associations (belongs_to, has_many, has_one) to find related models.
|
|
136
|
+
def resolve_constants(path)
|
|
137
|
+
full_path = File.join(project_root, path)
|
|
138
|
+
return [] unless File.exist?(full_path)
|
|
139
|
+
|
|
140
|
+
content = File.read(full_path)
|
|
141
|
+
found = []
|
|
142
|
+
|
|
143
|
+
# Match namespaced constants: Foo::Bar::Baz, Foo::Bar, or standalone Foo
|
|
144
|
+
# Excludes common Ruby/Rails constants that aren't project classes
|
|
145
|
+
constants = content.scan(/\b([A-Z][a-zA-Z0-9]*(?:::[A-Z][a-zA-Z0-9]*)*)/).flatten.uniq
|
|
146
|
+
constants -= ruby_stdlib_constants
|
|
147
|
+
|
|
148
|
+
# Parse Rails associations to find related models
|
|
149
|
+
# belongs_to :user -> User, has_many :line_items -> LineItem, has_one :profile -> Profile
|
|
150
|
+
content.scan(/(?:belongs_to|has_one|has_many|has_and_belongs_to_many)\s+:(\w+)/).flatten.each do |assoc_name|
|
|
151
|
+
# Singularize has_many names, then camelize
|
|
152
|
+
model_name = singularize(assoc_name)
|
|
153
|
+
constants << camelize(model_name)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
constants.uniq!
|
|
157
|
+
|
|
158
|
+
constants.each do |const|
|
|
159
|
+
# Convert Foo::Bar::Baz to foo/bar/baz
|
|
160
|
+
const_path = const.split("::").map { |part| underscore(part) }.join("/")
|
|
161
|
+
|
|
162
|
+
# Search common Rails locations
|
|
163
|
+
candidates = [
|
|
164
|
+
"app/models/#{const_path}.rb",
|
|
165
|
+
"app/services/#{const_path}.rb",
|
|
166
|
+
"app/controllers/#{const_path}_controller.rb",
|
|
167
|
+
"app/jobs/#{const_path}.rb",
|
|
168
|
+
"app/mailers/#{const_path}.rb",
|
|
169
|
+
"app/forms/#{const_path}.rb",
|
|
170
|
+
"app/queries/#{const_path}.rb",
|
|
171
|
+
"app/policies/#{const_path}.rb",
|
|
172
|
+
"app/serializers/#{const_path}.rb",
|
|
173
|
+
"app/validators/#{const_path}.rb",
|
|
174
|
+
"app/workers/#{const_path}.rb",
|
|
175
|
+
"lib/#{const_path}.rb"
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
candidates.each do |candidate|
|
|
179
|
+
if File.exist?(File.join(project_root, candidate)) && candidate != path
|
|
180
|
+
found << candidate
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
found.uniq
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Common Ruby/Rails constants to ignore when scanning for project dependencies
|
|
189
|
+
def ruby_stdlib_constants
|
|
190
|
+
@ruby_stdlib_constants ||= %w[
|
|
191
|
+
String Integer Float Array Hash Symbol Regexp Range IO File Dir
|
|
192
|
+
Time Date DateTime Numeric Boolean Object Class Module Kernel
|
|
193
|
+
Struct OpenStruct Comparable Enumerable Enumerator Proc Lambda
|
|
194
|
+
Set SortedSet Queue Thread Mutex Process Signal Fiber
|
|
195
|
+
StandardError RuntimeError ArgumentError TypeError NameError
|
|
196
|
+
NoMethodError NotImplementedError IOError Errno
|
|
197
|
+
ActiveRecord ActiveModel ActiveSupport ActionController ActionView
|
|
198
|
+
ActionMailer ActiveJob ActionCable ApplicationRecord ApplicationController
|
|
199
|
+
ApplicationMailer ApplicationJob ActiveStorage ActionText
|
|
200
|
+
Rails Rake Bundler Gem RSpec Minitest FactoryBot
|
|
201
|
+
Logger JSON YAML CSV ERB URI Net HTTP HTTPS
|
|
202
|
+
Base64 Digest OpenSSL SecureRandom Pathname Tempfile
|
|
203
|
+
BigDecimal Complex Rational
|
|
204
|
+
TRUE FALSE NIL ENV ARGV STDIN STDOUT STDERR
|
|
205
|
+
Grape API
|
|
206
|
+
]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def singularize(word)
|
|
210
|
+
return word[0..-4] if word.end_with?("ies")
|
|
211
|
+
return word[0..-3] if word.end_with?("ses")
|
|
212
|
+
return word[0..-2] if word.end_with?("s")
|
|
213
|
+
|
|
214
|
+
word
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def pluralize(word)
|
|
218
|
+
return "#{word[0..-2]}ies" if word.end_with?("y") && !word.end_with?("ey")
|
|
219
|
+
return "#{word}es" if word.end_with?("s", "x", "z", "ch", "sh")
|
|
220
|
+
|
|
221
|
+
"#{word}s"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def camelize(underscored)
|
|
225
|
+
underscored.split("_").map(&:capitalize).join
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def underscore(camel)
|
|
229
|
+
camel.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
230
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
231
|
+
.downcase
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubyn
|
|
4
|
+
module Context
|
|
5
|
+
class ProjectScanner
|
|
6
|
+
attr_reader :project_root
|
|
7
|
+
|
|
8
|
+
def initialize(project_root = Dir.pwd)
|
|
9
|
+
@project_root = project_root
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def scan
|
|
13
|
+
{
|
|
14
|
+
project_name: File.basename(project_root),
|
|
15
|
+
project_type: detect_project_type,
|
|
16
|
+
ruby_version: detect_ruby_version,
|
|
17
|
+
rails_version: detect_rails_version,
|
|
18
|
+
test_framework: detect_test_framework,
|
|
19
|
+
factory_library: detect_factory_library,
|
|
20
|
+
auth_library: detect_auth_library,
|
|
21
|
+
gems: detect_gems,
|
|
22
|
+
directory_structure: scan_directory_structure
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def detect_project_type
|
|
29
|
+
if File.exist?(File.join(project_root, "config", "routes.rb"))
|
|
30
|
+
"rails_app"
|
|
31
|
+
elsif File.exist?(File.join(project_root, "config.ru")) && gemfile_contains?("sinatra")
|
|
32
|
+
"sinatra_app"
|
|
33
|
+
elsif Dir.glob(File.join(project_root, "*.gemspec")).any?
|
|
34
|
+
"ruby_app"
|
|
35
|
+
else
|
|
36
|
+
"other_app"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def detect_ruby_version
|
|
41
|
+
version_file = File.join(project_root, ".ruby-version")
|
|
42
|
+
return File.read(version_file).strip if File.exist?(version_file)
|
|
43
|
+
|
|
44
|
+
tool_versions = File.join(project_root, ".tool-versions")
|
|
45
|
+
if File.exist?(tool_versions)
|
|
46
|
+
match = File.read(tool_versions).match(/ruby\s+(\S+)/)
|
|
47
|
+
return match[1] if match
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def detect_rails_version
|
|
54
|
+
lockfile = lockfile_content
|
|
55
|
+
return nil unless lockfile
|
|
56
|
+
|
|
57
|
+
match = lockfile.match(/^\s+rails\s+\((\S+)\)/m)
|
|
58
|
+
match ? match[1] : nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def detect_test_framework
|
|
62
|
+
lockfile = lockfile_content
|
|
63
|
+
return "rspec" if lockfile&.include?("rspec")
|
|
64
|
+
return "rspec" if Dir.exist?(File.join(project_root, "spec"))
|
|
65
|
+
return "minitest" if Dir.exist?(File.join(project_root, "test"))
|
|
66
|
+
|
|
67
|
+
"rspec"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def detect_factory_library
|
|
71
|
+
lockfile = lockfile_content
|
|
72
|
+
return "factory_bot" if lockfile&.include?("factory_bot")
|
|
73
|
+
return "fabrication" if lockfile&.include?("fabrication")
|
|
74
|
+
|
|
75
|
+
"no_factory"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def detect_auth_library
|
|
79
|
+
lockfile = lockfile_content
|
|
80
|
+
return "devise" if lockfile&.include?("devise")
|
|
81
|
+
return "clearance" if lockfile&.include?("clearance")
|
|
82
|
+
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def detect_gems
|
|
87
|
+
lockfile = lockfile_content
|
|
88
|
+
return [] unless lockfile
|
|
89
|
+
|
|
90
|
+
gems = []
|
|
91
|
+
in_specs = false
|
|
92
|
+
lockfile.each_line do |line|
|
|
93
|
+
if line.strip == "specs:"
|
|
94
|
+
in_specs = true
|
|
95
|
+
next
|
|
96
|
+
end
|
|
97
|
+
break if in_specs && !line.start_with?(" ")
|
|
98
|
+
|
|
99
|
+
if in_specs
|
|
100
|
+
match = line.match(/^\s{4}(\S+)\s+\((\S+)\)/)
|
|
101
|
+
gems << { name: match[1], version: match[2] } if match
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
gems
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def scan_directory_structure
|
|
108
|
+
dirs = %w[app lib spec test config db]
|
|
109
|
+
structure = {}
|
|
110
|
+
dirs.each do |dir|
|
|
111
|
+
full_path = File.join(project_root, dir)
|
|
112
|
+
structure[dir] = Dir.exist?(full_path)
|
|
113
|
+
end
|
|
114
|
+
structure
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def gemfile_contains?(gem_name)
|
|
118
|
+
gemfile = File.join(project_root, "Gemfile")
|
|
119
|
+
return false unless File.exist?(gemfile)
|
|
120
|
+
|
|
121
|
+
File.read(gemfile).include?(gem_name)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def lockfile_content
|
|
125
|
+
@lockfile_content ||= begin
|
|
126
|
+
path = File.join(project_root, "Gemfile.lock")
|
|
127
|
+
File.exist?(path) ? File.read(path) : nil
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
Binary file
|