rails-ai-context 5.21.1 → 5.21.2
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/CHANGELOG.md +22 -0
- data/docs/INTROSPECTORS.md +1 -0
- data/lib/rails_ai_context/introspectors/initializer_introspector.rb +19 -2
- data/lib/rails_ai_context/introspectors/listeners/mixins_listener.rb +60 -0
- data/lib/rails_ai_context/introspectors/model_introspector.rb +12 -0
- data/lib/rails_ai_context/introspectors/security_introspector.rb +8 -2
- data/lib/rails_ai_context/introspectors/source_introspector.rb +2 -1
- data/lib/rails_ai_context/version.rb +1 -1
- data/server.json +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 986161d7fdb3003eca77d6f88b2820b5b76a35d72b5282d8f910b0e316b29298
|
|
4
|
+
data.tar.gz: 2fc004a71ff022abd1c1d216466936b27249c5da6dede308dece659b3cc311a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c335f590a135a3d8591bd52daef9673a59b4b9af150aebbe80cf91a39905098c7e215f5e77dea0dd07abf1aa9d94c96ac3586a2d487f0955ab3eb6220f51bc5
|
|
7
|
+
data.tar.gz: 230525933f4ccd784307c70ec97785b3b7df2cafedcb4720b9d7ae5d6b95239693ecd9e11d0651215ce177a4cf8e1144ef85dee82a061f62b21f0252e1fe5523
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.21.2] - 2026-08-12
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **The static tier no longer drops a model's concerns.** Nothing in the
|
|
13
|
+
listener stack collected `include`/`prepend`, so `rails_get_model_details`
|
|
14
|
+
answered `--no-boot` with no Concerns section at all - not even an
|
|
15
|
+
`[UNAVAILABLE]` marker - for a model that includes one. A new
|
|
16
|
+
`MixinsListener` reads them from the source, filtered to the mixins
|
|
17
|
+
reflection would report so both tiers answer the same question. (#137)
|
|
18
|
+
- **Cookie settings that Rails computes per request are named, not
|
|
19
|
+
inspected.** Rails 8 defaults `same_site` to a lambda, which reached
|
|
20
|
+
`.ai-context.json` as `#<Proc:0x...>` - an address that answers nothing and
|
|
21
|
+
moves on every boot, so the file was rewritten on every run even when the app
|
|
22
|
+
had not changed. Callables now render `[UNAVAILABLE: computed at request
|
|
23
|
+
time]`, nested ones included. (#138)
|
|
24
|
+
- **Initializer sources are gem-relative instead of absolute machine paths.**
|
|
25
|
+
Every one of them was written into `.ai-context.json` as
|
|
26
|
+
`/Users/<name>/.rvm/gems/ruby-3.4.9/gems/railties-8.0.5.1/...`, which is
|
|
27
|
+
wrong on every other machine, wrong again after a Ruby upgrade, and carries
|
|
28
|
+
the generating developer's home directory into the app's repository. (#139)
|
|
29
|
+
|
|
8
30
|
## [5.21.1] - 2026-08-12
|
|
9
31
|
|
|
10
32
|
### Fixed
|
data/docs/INTROSPECTORS.md
CHANGED
|
@@ -185,6 +185,7 @@ It runs a single-pass Dispatcher that walks the AST once and feeds events to all
|
|
|
185
185
|
| CallbacksListener | All AR callback types, `after_commit` with `on:` resolution |
|
|
186
186
|
| MacrosListener | `encrypts`, `normalizes`, `delegate`, `has_secure_password`, `serialize`, `store`, `has_one_attached`, `has_many_attached`, `has_rich_text`, `generates_token_for`, `attribute` |
|
|
187
187
|
| MethodsListener | `def`/`def self.`, visibility tracking, parameter extraction, `class << self` |
|
|
188
|
+
| MixinsListener | `include`, `prepend`, `extend`, flagging the ones that reach the ancestor chain |
|
|
188
189
|
|
|
189
190
|
### The targeted-walk listeners
|
|
190
191
|
|
|
@@ -110,12 +110,29 @@ module RailsAiContext
|
|
|
110
110
|
return nil unless loc
|
|
111
111
|
|
|
112
112
|
path, line = loc
|
|
113
|
-
|
|
114
|
-
"#{path}:#{line}"
|
|
113
|
+
"#{relativize_source(path)}:#{line}"
|
|
115
114
|
rescue => e
|
|
116
115
|
$stderr.puts "[rails-ai-context] block_source_location failed: #{e.message}" if ENV["DEBUG"]
|
|
117
116
|
nil
|
|
118
117
|
end
|
|
118
|
+
|
|
119
|
+
# These sources are written into .ai-context.json, which the app commits.
|
|
120
|
+
# An absolute path is wrong on every other machine and carries the
|
|
121
|
+
# generating developer's home directory, so app paths go app-relative and
|
|
122
|
+
# gem paths keep the gem and version instead of the install prefix.
|
|
123
|
+
def relativize_source(path)
|
|
124
|
+
return path.sub("#{root}/", "") if path.start_with?(root)
|
|
125
|
+
|
|
126
|
+
gem_roots.each do |gem_root|
|
|
127
|
+
return path.delete_prefix(gem_root) if path.start_with?(gem_root)
|
|
128
|
+
end
|
|
129
|
+
path
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def gem_roots
|
|
133
|
+
@gem_roots ||= (Gem.path + [ Gem.default_dir ]).compact.uniq
|
|
134
|
+
.map { |dir| File.join(dir, "gems") + File::SEPARATOR }
|
|
135
|
+
end
|
|
119
136
|
end
|
|
120
137
|
end
|
|
121
138
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
module RailsAiContext
|
|
6
|
+
module Introspectors
|
|
7
|
+
module Listeners
|
|
8
|
+
# Detects `include`, `prepend` and `extend` with a constant argument.
|
|
9
|
+
#
|
|
10
|
+
# `ancestor` marks the ones that reach the ancestor chain, which is what
|
|
11
|
+
# reflection reports - so a caller can answer the same question the
|
|
12
|
+
# booted tier answers.
|
|
13
|
+
class MixinsListener < BaseListener
|
|
14
|
+
MIXIN_MACROS = %i[include prepend extend].to_set.freeze
|
|
15
|
+
ANCESTOR_MACROS = %i[include prepend].to_set.freeze
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
super
|
|
19
|
+
@singleton_depth = 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# `include` inside `class << self` lands on the singleton class, so it
|
|
23
|
+
# never reaches `ancestors` - extend semantics wearing an include's name.
|
|
24
|
+
def on_singleton_class_node_enter(_node)
|
|
25
|
+
@singleton_depth += 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def on_singleton_class_node_leave(_node)
|
|
29
|
+
@singleton_depth -= 1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def on_call_node_enter(node)
|
|
33
|
+
return unless node.receiver.nil?
|
|
34
|
+
return unless MIXIN_MACROS.include?(node.name)
|
|
35
|
+
|
|
36
|
+
(node.arguments&.arguments || []).each do |arg|
|
|
37
|
+
name = constant_name(arg)
|
|
38
|
+
next unless name
|
|
39
|
+
|
|
40
|
+
@results << {
|
|
41
|
+
macro: node.name,
|
|
42
|
+
name: name,
|
|
43
|
+
ancestor: @singleton_depth.zero? && ANCESTOR_MACROS.include?(node.name),
|
|
44
|
+
location: node.location.start_line,
|
|
45
|
+
confidence: confidence_for(node)
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def constant_name(node)
|
|
53
|
+
return unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
|
54
|
+
|
|
55
|
+
extract_value(node)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -591,11 +591,23 @@ module RailsAiContext
|
|
|
591
591
|
# with callbacks found" and then raised a TypeError on a Hash lookup
|
|
592
592
|
# against an Array.
|
|
593
593
|
callbacks: group_callbacks_by_type(data[:callbacks]),
|
|
594
|
+
concerns: static_concerns(data[:mixins]),
|
|
594
595
|
macros: data[:macros],
|
|
595
596
|
methods: data[:methods]
|
|
596
597
|
}
|
|
597
598
|
end
|
|
598
599
|
|
|
600
|
+
# Only the mixins reflection would report, so both tiers answer the same
|
|
601
|
+
# question. This sees the model file alone, where the booted tier also
|
|
602
|
+
# walks what its superclass and its concerns pulled in.
|
|
603
|
+
def static_concerns(mixins)
|
|
604
|
+
(mixins || [])
|
|
605
|
+
.select { |mixin| mixin[:ancestor] }
|
|
606
|
+
.map { |mixin| mixin[:name] }
|
|
607
|
+
.reject { |name| framework_concern?(name) }
|
|
608
|
+
.uniq
|
|
609
|
+
end
|
|
610
|
+
|
|
599
611
|
# Mongoid documents are invisible to ActiveRecord reflection, so both
|
|
600
612
|
# tiers parse them from source. Fields and embedded relations come from
|
|
601
613
|
# the Mongoid listener; shared-name macros (belongs_to, has_many,
|
|
@@ -246,11 +246,17 @@ module RailsAiContext
|
|
|
246
246
|
{}
|
|
247
247
|
end
|
|
248
248
|
|
|
249
|
+
# Rails 8 defaults same_site to a lambda. Left alone it reaches
|
|
250
|
+
# .ai-context.json as an address that answers nothing and moves every
|
|
251
|
+
# boot, so the file is rewritten on every run. Nested one level down it
|
|
252
|
+
# is worse: the encoder renders it `{}`, which reads as a setting the app
|
|
253
|
+
# left empty.
|
|
249
254
|
def serializable(value)
|
|
250
255
|
case value
|
|
251
256
|
when Symbol then value.to_s
|
|
252
|
-
when Hash then value.
|
|
253
|
-
when Array then value.map(
|
|
257
|
+
when Hash then value.each_with_object({}) { |(key, nested), result| result[key.to_s] = serializable(nested) }
|
|
258
|
+
when Array then value.map { |nested| serializable(nested) }
|
|
259
|
+
when Proc then Confidence.unavailable("computed at request time")
|
|
254
260
|
else value
|
|
255
261
|
end
|
|
256
262
|
end
|
|
@@ -20,7 +20,8 @@ module RailsAiContext
|
|
|
20
20
|
enums: Listeners::EnumsListener,
|
|
21
21
|
callbacks: Listeners::CallbacksListener,
|
|
22
22
|
macros: Listeners::MacrosListener,
|
|
23
|
-
methods: Listeners::MethodsListener
|
|
23
|
+
methods: Listeners::MethodsListener,
|
|
24
|
+
mixins: Listeners::MixinsListener
|
|
24
25
|
}.freeze
|
|
25
26
|
|
|
26
27
|
# Introspect a file on disk (cached parse) with default listeners.
|
data/server.json
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-ai-context
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.21.
|
|
4
|
+
version: 5.21.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- crisnahine
|
|
@@ -345,6 +345,7 @@ files:
|
|
|
345
345
|
- lib/rails_ai_context/introspectors/listeners/methods_listener.rb
|
|
346
346
|
- lib/rails_ai_context/introspectors/listeners/middleware_config_listener.rb
|
|
347
347
|
- lib/rails_ai_context/introspectors/listeners/migration_dsl_listener.rb
|
|
348
|
+
- lib/rails_ai_context/introspectors/listeners/mixins_listener.rb
|
|
348
349
|
- lib/rails_ai_context/introspectors/listeners/model_reference_listener.rb
|
|
349
350
|
- lib/rails_ai_context/introspectors/listeners/mongoid_fields_listener.rb
|
|
350
351
|
- lib/rails_ai_context/introspectors/listeners/mount_listener.rb
|